cdc.io.tools.XmlCheckClassRefs Maven / Gradle / Ivy
package cdc.io.tools;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import cdc.io.data.Attribute;
import cdc.io.data.Element;
import cdc.io.data.paths.SPath;
import cdc.io.data.xml.XmlDataReader;
import cdc.util.cli.AbstractMainSupport;
import cdc.util.lang.CollectionUtils;
import cdc.util.lang.FailureReaction;
import cdc.util.refs.ClassRef;
import cdc.util.refs.LazyClassRef;
import cdc.util.refs.ResolutionStatus;
/**
* Utility used to check existence of classes in an XML file.
*
* @author Damien Carbonne
*
*/
public final class XmlCheckClassRefs {
protected static final Logger LOGGER = LogManager.getLogger(XmlCheckClassRefs.class);
private final MainArgs margs;
private final Set classes = new HashSet<>();
private final Map done = new HashMap<>();
public static class MainArgs {
public final List input = new ArrayList<>();
/** Set of attributes and elements to analyze. */
public final Set paths = new HashSet<>();
public final List jarFiles = new ArrayList<>();
}
private XmlCheckClassRefs(MainArgs margs) {
this.margs = margs;
}
private void execute() throws IOException {
for (final File jar : margs.jarFiles) {
LOGGER.info("Analyze jar '{}'", jar);
listJarClasseNames(jar);
}
for (final File file : margs.input) {
LOGGER.info("Analyze xml '{}'", file);
final Element root = XmlDataReader.loadRoot(file);
analyze(file, root);
}
LOGGER.info("Valid classes");
for (final String className : CollectionUtils.toSortedList(done.keySet())) {
final boolean success = done.get(className);
if (success) {
LOGGER.info(" {}", className);
}
}
LOGGER.info("Invalid classes");
for (final String className : CollectionUtils.toSortedList(done.keySet())) {
final boolean success = done.get(className);
if (!success) {
LOGGER.info(" {}", className);
}
}
}
private void listJarClasseNames(File jar) {
LOGGER.debug("listJarClasseNames({})", jar);
try (final ZipFile archive = new ZipFile(jar)) {
final Enumeration extends ZipEntry> iter = archive.entries();
while (iter.hasMoreElements()) {
final ZipEntry entry = iter.nextElement();
final String name = entry.getName();
if (name.endsWith(".class")) {
final String className = name.substring(0, name.length() - 6).replace("/", ".");
LOGGER.debug(" {}", className);
classes.add(className);
}
}
} catch (final IOException e) {
LOGGER.catching(e);
}
}
private void analyze(File file,
Element element) {
for (final SPath path : margs.paths) {
if (path.matchesElement(element)) {
final String s = element.getText(null);
checkClassName(s, file, element.getQName());
}
for (final Attribute att : element.getAttributes()) {
if (path.matchesAttribute(element, att.getName())) {
final String s = att.getValue();
checkClassName(s, file, element.getQName() + "/@" + att.getName());
}
}
}
for (final Element child : element.getElements()) {
analyze(file, child);
}
}
private void checkClassName(String className,
File file,
String location) {
LOGGER.debug("checkClassName({}, {})", className, location);
Boolean success = done.get(className);
if (success == null) {
success = resolve(className);
done.put(className, success);
}
if (success != null && success) {
LOGGER.info("{}: {}: {}: valid class name\"", file, location, className);
} else {
LOGGER.info("{}: {}: {}: invalid class name\"", file, location, className);
}
}
/**
* Returns {@code true} when a class name can be resolved.
*
* Class name is first searched in referenced jars.
* If not found, it is searched in class path.
*
* @param className The class name.
* @return {@code true} when {@code className} can be resolved.
*/
private boolean resolve(String className) {
if (classes.contains(className)) {
return true;
} else {
final ClassRef ref = new LazyClassRef(className);
ref.get(FailureReaction.DEFAULT);
return ref.getResolutionStatus() == ResolutionStatus.SUCCESS;
}
}
public static void execute(MainArgs margs) throws IOException {
final XmlCheckClassRefs instance = new XmlCheckClassRefs(margs);
instance.execute();
}
public static void main(String[] args) {
final MainSupport support = new MainSupport();
support.main(args);
}
private static class MainSupport extends AbstractMainSupport {
private static final String JAR_FILE = "jar";
public MainSupport() {
super(XmlCheckClassRefs.class, LOGGER);
}
@Override
protected String getVersion() {
return Config.VERSION;
}
@Override
protected void addSpecificOptions(Options options) {
options.addOption(Option.builder()
.longOpt(INPUT)
.desc("XML input URL(s).")
.hasArgs()
.required()
.build());
options.addOption(Option.builder()
.longOpt(JAR_FILE)
.desc("Jar file(s) where classes must be searched, in addition to classes that can be accessed with class path.")
.hasArgs()
.build());
options.addOption(Option.builder()
.longOpt(PATH)
.desc("Path(s) of attributes and elements to analyze in input files.\n"
+ "They must match elt-name(/elt-name)* or (elt-name(/elt-name)*)@att-name.\n"
+ "Examples: foo, foo1/foo2, @bar, foo@bar, foo1/foo2@bar")
.hasArgs()
.required()
.build());
}
@Override
protected MainArgs analyze(CommandLine cl) throws ParseException {
final MainArgs margs = new MainArgs();
if (cl.getOptionValues(JAR_FILE) != null) {
for (final String s : cl.getOptionValues(JAR_FILE)) {
margs.jarFiles.add(new File(s));
}
}
if (cl.getOptionValues(INPUT) != null) {
for (final String s : cl.getOptionValues(INPUT)) {
margs.input.add(new File(s));
}
}
if (cl.hasOption(PATH)) {
for (final String s : cl.getOptionValues(PATH)) {
margs.paths.add(new SPath(s));
}
}
return margs;
}
@Override
protected Void execute(MainArgs margs) throws Exception {
XmlCheckClassRefs.execute(margs);
return null;
}
}
}