package org.jvnet.jaxb2_commons.plugin.copyable; import java.util.Arrays; import java.util.Collection; import javax.xml.namespace.QName; import org.jvnet.jaxb2_commons.lang.CopyStrategy; import org.jvnet.jaxb2_commons.lang.CopyTo; import org.jvnet.jaxb2_commons.lang.JAXBCopyStrategy; 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.JClass; import com.sun.codemodel.JCodeModel; import com.sun.codemodel.JConditional; 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.JOp; 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 CopyablePlugin extends AbstractParameterizablePlugin { @Override public String getOptionName() { return "Xcopyable"; } @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 copyStrategyClass = JAXBCopyStrategy.class.getName(); public void setCopyStrategyClass(final String copyStrategy) { this.copyStrategyClass = copyStrategy; } public String getCopyStrategyClass() { return copyStrategyClass; } public JExpression createCopyStrategy(JCodeModel codeModel) { return StrategyClassUtils.createStrategyInstanceExpression(codeModel, CopyStrategy.class, getCopyStrategyClass()); } private Ignoring ignoring = new CustomizedIgnoring( org.jvnet.jaxb2_commons.plugin.copyable.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.copyable.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(Cloneable.class)); @SuppressWarnings("unused") final JMethod object$clone = generateObject$clone(classOutline, theClass); ClassUtils._implements(theClass, theClass.owner().ref(CopyTo.class)); @SuppressWarnings("unused") final JMethod copyTo$copyTo = generateCopyTo$copyTo(classOutline, theClass); @SuppressWarnings("unused") final JMethod copyTo$copyTo1 = generateCopyTo$copyTo1(classOutline, theClass); // @SuppressWarnings("unused") // final JMethod copyable$copyTo = generateCopyable$CopyTo(classOutline, // theClass); // @SuppressWarnings("unused") // final JMethod copyable$copyTo1 = // generateCopyable$CopyTo1(classOutline, // theClass); // @SuppressWarnings("unused") // final JMethod copyFrom$copyFrom = generateCopyFrom$CopyFrom( // classOutline, theClass); // @SuppressWarnings("unused") // final JMethod copyable$copyFrom = generateCopyable$CopyFrom( // classOutline, theClass); if (!classOutline.target.isAbstract()) { @SuppressWarnings("unused") final JMethod createCopy = generateCopyTo$createNewInstance( classOutline, theClass); } } protected JMethod generateCopyTo$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; } } protected JMethod generateObject$clone(final ClassOutline classOutline, final JDefinedClass theClass) { final JMethod clone = theClass.method(JMod.PUBLIC, theClass.owner() .ref(Object.class), "clone"); { final JBlock body = clone.body(); body._return(JExpr.invoke("copyTo").arg( JExpr.invoke("createNewInstance"))); } return clone; } protected JMethod generateCopyTo$copyTo(final ClassOutline classOutline, final JDefinedClass theClass) { final JCodeModel codeModel = theClass.owner(); final JMethod copyTo$copyTo = theClass.method(JMod.PUBLIC, codeModel.ref(Object.class), "copyTo"); { final JVar target = copyTo$copyTo.param(Object.class, "target"); final JBlock body = copyTo$copyTo.body(); final JVar copyStrategy = body.decl(JMod.FINAL, codeModel.ref(CopyStrategy.class), "strategy", createCopyStrategy(codeModel)); body._return(JExpr.invoke("copyTo").arg(JExpr._null()).arg(target) .arg(copyStrategy)); } return copyTo$copyTo; } protected JMethod generateCopyTo$copyTo1(ClassOutline classOutline, final JDefinedClass theClass) { ClassUtils._implements(theClass, theClass.owner().ref(CopyTo.class)); final JMethod copyTo = theClass.method(JMod.PUBLIC, theClass.owner() .ref(Object.class), "copyTo"); { final JVar locator = copyTo.param(ObjectLocator.class, "locator"); final JVar target = copyTo.param(Object.class, "target"); final JVar copyStrategy = copyTo.param(CopyStrategy.class, "strategy"); final JBlock body = copyTo.body(); final JVar draftCopy; if (!classOutline.target.isAbstract()) { draftCopy = body.decl( JMod.FINAL, theClass.owner().ref(Object.class), "draftCopy", JOp.cond(JOp.eq(target, JExpr._null()), JExpr.invoke("createNewInstance"), target)); } else { body._if(JExpr._null().eq(target)) ._then() ._throw(JExpr ._new(theClass.owner().ref( IllegalArgumentException.class)) .arg("Target argument must not be null for abstract copyable classes.")); draftCopy = target; } Boolean superClassImplementsCopyTo = StrategyClassUtils .superClassImplements(classOutline, getIgnoring(), CopyTo.class); if (superClassImplementsCopyTo == null) { } else if (superClassImplementsCopyTo.booleanValue()) { body.invoke(JExpr._super(), "copyTo").arg(locator) .arg(draftCopy).arg(copyStrategy); } else { } final FieldOutline[] declaredFields = FieldOutlineUtils.filter( classOutline.getDeclaredFields(), getIgnoring()); if (declaredFields.length > 0) { final JBlock bl = body._if(draftCopy._instanceof(theClass)) ._then(); final JVar copy = bl.decl(JMod.FINAL, theClass, "copy", JExpr.cast(theClass, draftCopy)); for (final FieldOutline fieldOutline : declaredFields) { final FieldAccessorEx sourceFieldAccessor = getFieldAccessorFactory() .createFieldAccessor(fieldOutline, JExpr._this()); final FieldAccessorEx copyFieldAccessor = getFieldAccessorFactory() .createFieldAccessor(fieldOutline, copy); if (sourceFieldAccessor.isConstant()) { continue; } final JBlock block = bl.block(); final JBlock setValueBlock; final JBlock unsetValueBlock; final JExpression valueIsSet = sourceFieldAccessor .hasSetValue(); if (!sourceFieldAccessor.isAlwaysSet() && valueIsSet != null) { final JConditional ifValueIsSet = block._if(valueIsSet); setValueBlock = ifValueIsSet._then(); unsetValueBlock = ifValueIsSet._else(); } else { setValueBlock = block; unsetValueBlock = null; } if (setValueBlock != null) { final JType copyFieldType = sourceFieldAccessor .getType(); final JVar sourceField = setValueBlock.decl( copyFieldType, "source" + fieldOutline.getPropertyInfo() .getName(true)); sourceFieldAccessor.toRawValue(setValueBlock, sourceField); final JExpression builtCopy = JExpr .invoke(copyStrategy, "copy") .arg(theClass .owner() .ref(LocatorUtils.class) .staticInvoke("property") .arg(locator) .arg(fieldOutline.getPropertyInfo() .getName(false)) .arg(sourceField)).arg(sourceField); final JVar copyField = setValueBlock.decl( copyFieldType, "copy" + fieldOutline.getPropertyInfo() .getName(true), copyFieldType.isPrimitive() ? builtCopy : JExpr.cast(copyFieldType, builtCopy)); if (copyFieldType instanceof JClass && ((JClass) copyFieldType).isParameterized()) { copyField.annotate(SuppressWarnings.class).param( "value", "unchecked"); } copyFieldAccessor.fromRawValue(setValueBlock, "unique" + fieldOutline.getPropertyInfo().getName(true), copyField); } if (unsetValueBlock != null) { copyFieldAccessor.unsetValues(unsetValueBlock); } } } body._return(draftCopy); } return copyTo; } }