com.massfords.jaxb.VisitorPlugin Maven / Gradle / Ivy
package com.massfords.jaxb;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JPackage;
import com.sun.tools.xjc.BadCommandLineException;
import com.sun.tools.xjc.Options;
import com.sun.tools.xjc.Plugin;
import com.sun.tools.xjc.model.Aspect;
import com.sun.tools.xjc.model.CClassInfoParent;
import com.sun.tools.xjc.outline.ClassOutline;
import com.sun.tools.xjc.outline.Outline;
import com.sun.tools.xjc.outline.PackageOutline;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
/**
* Plugin generates the following code:
*
*
* - Visitor: interface with visit methods for each of the beans
* - Traverser: interface with traverse methods for each of the beans. The traverser traverses each of the bean's children and visits them.
*
- BaseVisitor: no-op impl of the Visitor interface
* - DepthFirstTraverserImpl: depth first implementation of the traverser interface
* - TraversingVisitor: class that pairs the visitor and traverser to visit the whole graph
* - accept(Visitor): added to each of the generated JAXB classes
*
*
* @author markford
*/
public class VisitorPlugin extends Plugin {
/**
* name of the package for our generated visitor classes. If not set, we'll pick the first package from the outline.
*/
private String packageName;
private boolean includeType = false;
@Override
public String getOptionName() {
return "Xvisitor";
}
@Override
public String getUsage() {
return null;
}
@Override
public int parseArgument(Options opt, String[] args, int index) throws BadCommandLineException, IOException {
// look for the visitor-package argument since we'll use this for package name for our generated code.
String arg = args[index];
if (arg.startsWith("-Xvisitor-package:")) {
packageName = arg.split(":")[1];
return 1;
}
if (arg.startsWith("-Xvisitor-includeType:")) {
includeType = "true".equalsIgnoreCase(arg.split(":")[1]);
return 1;
}
if (arg.equals("-Xvisitor-includeType")) {
includeType = true;
return 1;
}
return 0;
}
@Override
public boolean run(Outline outline, Options options, ErrorHandler errorHandler)
throws SAXException {
try {
/*
// create a set to hold all of the beans that need a qname
// add a qname field to each of these beans
// add a getter/setter for the qname via an interface
// add unmarshaller hook to each of these beans to pull the qname from their JAXBElement parent
// update the traverser code for JAXBElement to see if the bean is an instance of this interface and invoke the setter
// done and done
*/
JPackage vizPackage = getOrCreatePackageForVisitors(outline);
Set sorted = sortClasses(outline);
Set directClasses = ClassDiscoverer.discoverDirectClasses(outline, sorted);
// create JAXBElement name support for holding JAXBElement names
CreateJAXBElementNameCallback cni =
new CreateJAXBElementNameCallback(outline, vizPackage);
cni.run(sorted, directClasses);
/*
* These functions are used to produce the name of a Visitor or
* Traverser method. Previously, these names were hardcoded and
* we relied on Java's overloading to handle the dispatch but Issue #8
* was filed regarding performance issues.
*
* The names produced from these functions will include the
* type name if that feature is enabled. It's still possible that
* may have some overloaded methods due to inner types but this should
* cut down on overloading significantly.
*/
Function visitMethodNamer;
Function traverseMethodNamer;
if (includeType) {
visitMethodNamer = s -> "visit" + s;
traverseMethodNamer = s -> "traverse" + s;
} else {
visitMethodNamer = s -> "visit";
traverseMethodNamer = s -> "traverse";
}
// create visitor interface
CreateVisitorInterface createVisitorInterface =
new CreateVisitorInterface(outline, vizPackage, visitMethodNamer);
createVisitorInterface.run(sorted, directClasses);
JDefinedClass visitor = createVisitorInterface.getOutput();
// create visitable interface and have all the beans implement it
CreateVisitableInterface createVisitableInterface =
new CreateVisitableInterface(visitor, outline, vizPackage);
createVisitableInterface.run(sorted, directClasses);
JDefinedClass visitable = createVisitableInterface.getOutput();
// add accept method to beans
AddAcceptMethod addAcceptMethod = new AddAcceptMethod(visitMethodNamer);
addAcceptMethod.run(sorted, visitor);
// create traverser interface
CreateTraverserInterface createTraverserInterface =
new CreateTraverserInterface(visitor, outline, vizPackage, traverseMethodNamer);
createTraverserInterface.run(sorted, directClasses);
JDefinedClass traverser = createTraverserInterface.getOutput();
// create base visitor class
CreateBaseVisitorClass createBaseVisitorClass =
new CreateBaseVisitorClass(visitor, outline, vizPackage, visitMethodNamer);
createBaseVisitorClass.run(sorted, directClasses);
createBaseVisitorClass.getOutput();
// create default generic depth first traverser class
CreateDepthFirstTraverserClass createDepthFirstTraverserClass =
new CreateDepthFirstTraverserClass(visitor, traverser,
visitable, outline, vizPackage, traverseMethodNamer);
createDepthFirstTraverserClass.run(sorted, directClasses);
// create progress monitor for traversing visitor
CreateTraversingVisitorProgressMonitorInterface progMon =
new CreateTraversingVisitorProgressMonitorInterface(visitable,
outline, vizPackage);
progMon.run(sorted, directClasses);
JDefinedClass progressMonitor = progMon.getOutput();
// create traversing visitor class
CreateTraversingVisitorClass createTraversingVisitorClass =
new CreateTraversingVisitorClass(visitor, progressMonitor,
traverser, outline, vizPackage, visitMethodNamer, traverseMethodNamer);
createTraversingVisitorClass.run(sorted, directClasses);
} catch (Throwable t) {
t.printStackTrace();
return false;
}
return true;
}
/**
* The classes are sorted for test purposes only. This gives us a predictable order for our
* assertions on the generated code.
*
* @param outline
*/
private Set sortClasses(Outline outline) {
Set sorted = new TreeSet<>((aOne, aTwo) -> {
String one = aOne.implClass.fullName();
String two = aTwo.implClass.fullName();
return one.compareTo(two);
});
sorted.addAll(outline.getClasses());
return sorted;
}
private JPackage getOrCreatePackageForVisitors(Outline outline) {
JPackage vizPackage = null;
if (getPackageName() != null) {
JPackage root = outline.getCodeModel().rootPackage();
String[] packages = getPackageName().split("\\.");
JPackage current = root;
for(String p : packages) {
current = current.subPackage(p);
}
vizPackage = current;
}
if (vizPackage == null) {
PackageOutline packageOutline = outline.getAllPackageContexts().iterator().next();
CClassInfoParent.Package pkage = new CClassInfoParent.Package(packageOutline._package());
vizPackage = (JPackage) outline.getContainer(pkage, Aspect.IMPLEMENTATION);
}
return vizPackage;
}
@SuppressWarnings("WeakerAccess")
public String getPackageName() {
return packageName;
}
@SuppressWarnings("UnusedDeclaration")
public void setPackageName(String packageName) {
this.packageName = packageName;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy