package org.jvnet.jaxb2_commons.plugin.mergeable;
import java.util.Arrays;
import java.util.Collection;
import javax.xml.namespace.QName;
import org.jvnet.jaxb2_commons.lang.JAXBMergeStrategy;
import org.jvnet.jaxb2_commons.lang.MergeFrom;
import org.jvnet.jaxb2_commons.lang.MergeStrategy;
import org.jvnet.jaxb2_commons.locator.ObjectLocator;
import org.jvnet.jaxb2_commons.locator.util.LocatorUtils;
import org.jvnet.jaxb2_commons.plugin.AbstractParameterizablePlugin;
import org.jvnet.jaxb2_commons.plugin.Customizations;
import org.jvnet.jaxb2_commons.plugin.CustomizedIgnoring;
import org.jvnet.jaxb2_commons.plugin.Ignoring;
import org.jvnet.jaxb2_commons.plugin.util.FieldOutlineUtils;
import org.jvnet.jaxb2_commons.plugin.util.StrategyClassUtils;
import org.jvnet.jaxb2_commons.util.ClassUtils;
import org.jvnet.jaxb2_commons.util.FieldAccessorFactory;
import org.jvnet.jaxb2_commons.util.PropertyFieldAccessorFactory;
import org.jvnet.jaxb2_commons.xjc.outline.FieldAccessorEx;
import org.xml.sax.ErrorHandler;
import com.sun.codemodel.JBlock;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JExpression;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;
import com.sun.tools.xjc.Options;
import com.sun.tools.xjc.outline.ClassOutline;
import com.sun.tools.xjc.outline.FieldOutline;
import com.sun.tools.xjc.outline.Outline;
public class MergeablePlugin extends AbstractParameterizablePlugin {
@Override
public String getOptionName() {
return "Xmergeable";
}
@Override
public String getUsage() {
return "TBD";
}
private FieldAccessorFactory fieldAccessorFactory = PropertyFieldAccessorFactory.INSTANCE;
public FieldAccessorFactory getFieldAccessorFactory() {
return fieldAccessorFactory;
}
public void setFieldAccessorFactory(
FieldAccessorFactory fieldAccessorFactory) {
this.fieldAccessorFactory = fieldAccessorFactory;
}
private String mergeStrategyClass = JAXBMergeStrategy.class.getName();
public void setMergeStrategyClass(
final String mergeStrategyClass) {
this.mergeStrategyClass = mergeStrategyClass;
}
public String getMergeStrategyClass() {
return mergeStrategyClass;
}
public JExpression createMergeStrategy(JCodeModel codeModel) {
return StrategyClassUtils.createStrategyInstanceExpression(codeModel,
MergeStrategy.class, getMergeStrategyClass());
}
private Ignoring ignoring = new CustomizedIgnoring(
org.jvnet.jaxb2_commons.plugin.mergeable.Customizations.IGNORED_ELEMENT_NAME,
Customizations.IGNORED_ELEMENT_NAME,
Customizations.GENERATED_ELEMENT_NAME);
public Ignoring getIgnoring() {
return ignoring;
}
public void setIgnoring(Ignoring ignoring) {
this.ignoring = ignoring;
}
@Override
public Collection<QName> getCustomizationElementNames() {
return Arrays
.asList(org.jvnet.jaxb2_commons.plugin.mergeable.Customizations.IGNORED_ELEMENT_NAME,
Customizations.IGNORED_ELEMENT_NAME,
Customizations.GENERATED_ELEMENT_NAME);
}
@Override
public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) {
for (final ClassOutline classOutline : outline.getClasses())
if (!getIgnoring().isIgnored(classOutline)) {
processClassOutline(classOutline);
}
return true;
}
protected void processClassOutline(ClassOutline classOutline) {
final JDefinedClass theClass = classOutline.implClass;
ClassUtils._implements(theClass, theClass.owner().ref(MergeFrom.class));
@SuppressWarnings("unused")
final JMethod mergeFrom$mergeFrom0 = generateMergeFrom$mergeFrom0(
classOutline, theClass);
@SuppressWarnings("unused")
final JMethod mergeFrom$mergeFrom = generateMergeFrom$mergeFrom(
classOutline, theClass);
if (!classOutline.target.isAbstract()) {
@SuppressWarnings("unused")
final JMethod createCopy = generateMergeFrom$createNewInstance(
classOutline, theClass);
}
}
protected JMethod generateMergeFrom$mergeFrom0(
final ClassOutline classOutline, final JDefinedClass theClass) {
JCodeModel codeModel = theClass.owner();
final JMethod mergeFrom$mergeFrom = theClass.method(JMod.PUBLIC,
codeModel.VOID, "mergeFrom");
{
final JVar left = mergeFrom$mergeFrom.param(Object.class, "left");
final JVar right = mergeFrom$mergeFrom.param(Object.class, "right");
final JBlock body = mergeFrom$mergeFrom.body();
final JVar mergeStrategy = body.decl(JMod.FINAL,
codeModel.ref(MergeStrategy.class), "strategy",
createMergeStrategy(codeModel));
body.invoke("mergeFrom").arg(JExpr._null()).arg(JExpr._null())
.arg(left).arg(right).arg(mergeStrategy);
}
return mergeFrom$mergeFrom;
}
protected JMethod generateMergeFrom$mergeFrom(ClassOutline classOutline,
final JDefinedClass theClass) {
final JCodeModel codeModel = theClass.owner();
final JMethod mergeFrom = theClass.method(JMod.PUBLIC, codeModel.VOID,
"mergeFrom");
{
final JVar leftLocator = mergeFrom.param(ObjectLocator.class,
"leftLocator");
final JVar rightLocator = mergeFrom.param(ObjectLocator.class,
"rightLocator");
final JVar left = mergeFrom.param(Object.class, "left");
final JVar right = mergeFrom.param(Object.class, "right");
final JVar mergeStrategy = mergeFrom.param(MergeStrategy.class,
"strategy");
final JBlock methodBody = mergeFrom.body();
Boolean superClassImplementsMergeFrom = StrategyClassUtils
.superClassImplements(classOutline, getIgnoring(),
MergeFrom.class);
if (superClassImplementsMergeFrom == null) {
} else if (superClassImplementsMergeFrom.booleanValue()) {
methodBody.invoke(JExpr._super(), "mergeFrom").arg(leftLocator)
.arg(rightLocator).arg(left).arg(right)
.arg(mergeStrategy);
} else {
}
final FieldOutline[] declaredFields = FieldOutlineUtils.filter(
classOutline.getDeclaredFields(), getIgnoring());
if (declaredFields.length > 0) {
final JBlock body = methodBody._if(right._instanceof(theClass))
._then();
JVar target = body.decl(JMod.FINAL, theClass, "target",
JExpr._this());
JVar leftObject = body.decl(JMod.FINAL, theClass, "leftObject",
JExpr.cast(theClass, left));
JVar rightObject = body.decl(JMod.FINAL, theClass,
"rightObject", JExpr.cast(theClass, right));
for (final FieldOutline fieldOutline : declaredFields) {
final FieldAccessorEx leftFieldAccessor = getFieldAccessorFactory()
.createFieldAccessor(fieldOutline, leftObject);
final FieldAccessorEx rightFieldAccessor = getFieldAccessorFactory()
.createFieldAccessor(fieldOutline, rightObject);
if (leftFieldAccessor.isConstant()
|| rightFieldAccessor.isConstant()) {
continue;
}
final JBlock block = body.block();
final JVar leftField = block.decl(
leftFieldAccessor.getType(),
"lhs"
+ fieldOutline.getPropertyInfo().getName(
true));
leftFieldAccessor.toRawValue(block, leftField);
final JVar rightField = block.decl(
rightFieldAccessor.getType(),
"rhs"
+ fieldOutline.getPropertyInfo().getName(
true));
rightFieldAccessor.toRawValue(block, rightField);
final JExpression leftFieldLocator = codeModel
.ref(LocatorUtils.class).staticInvoke("property")
.arg(leftLocator)
.arg(fieldOutline.getPropertyInfo().getName(false))
.arg(leftField);
final JExpression rightFieldLocator = codeModel
.ref(LocatorUtils.class).staticInvoke("property")
.arg(rightLocator)
.arg(fieldOutline.getPropertyInfo().getName(false))
.arg(rightField);
final FieldAccessorEx targetFieldAccessor = getFieldAccessorFactory()
.createFieldAccessor(fieldOutline, target);
final JExpression mergedValue = JExpr.cast(
targetFieldAccessor.getType(),
mergeStrategy.invoke("merge").arg(leftFieldLocator)
.arg(rightFieldLocator).arg(leftField)
.arg(rightField));
final JVar merged = block.decl(
rightFieldAccessor.getType(),
"merged"
+ fieldOutline.getPropertyInfo().getName(
true), mergedValue);
targetFieldAccessor.fromRawValue(block, "unique"
+ fieldOutline.getPropertyInfo().getName(true),
merged);
}
}
}
return mergeFrom;
}
protected JMethod generateMergeFrom$createNewInstance(
final ClassOutline classOutline, final JDefinedClass theClass) {
final JMethod existingMethod = theClass.getMethod("createNewInstance",
new JType[0]);
if (existingMethod == null) {
final JMethod newMethod = theClass.method(JMod.PUBLIC, theClass
.owner().ref(Object.class), "createNewInstance");
{
final JBlock body = newMethod.body();
body._return(JExpr._new(theClass));
}
return newMethod;
} else {
return existingMethod;
}
}
}