diff --git a/src/main/java/com/sun/tools/xjc/addon/xew/Candidate.java b/src/main/java/com/sun/tools/xjc/addon/xew/Candidate.java index a34c95d..7a9961f 100644 --- a/src/main/java/com/sun/tools/xjc/addon/xew/Candidate.java +++ b/src/main/java/com/sun/tools/xjc/addon/xew/Candidate.java @@ -25,44 +25,41 @@ * of objects. */ public final class Candidate { - private final JDefinedClass candidateClass; + private final JDefinedClass candidateClass; - private final JFieldVar field; + private final JFieldVar field; - private final CPropertyInfo fieldPropertyInfo; + private final CPropertyInfo fieldPropertyInfo; - private final String fieldTargetNamespace; + private final String fieldTargetNamespace; - private final JDefinedClass fieldParametrisationClass; - - private final JDefinedClass fieldParametrisationImpl; + private final Collection parametrisationInfos; // Order matters (value Object Factory is first): - private final Map objectFactoryClasses = new LinkedHashMap<>(); + private final Map objectFactoryClasses = new LinkedHashMap<>(); - private final boolean valueObjectDisabled; + private final boolean valueObjectDisabled; // Order matters as it affects the order of generated methods in Object Factory: - private final Map scopedFactoryMethods = new LinkedHashMap<>(); + private final Map scopedFactoryMethods = new LinkedHashMap<>(); /** * By default the candidate is marked for removal unless something prevents it from being removed. */ - private boolean markedForRemoval = true; + private boolean markedForRemoval = true; /** * Number of times this candidate has been substituted in the model. */ - private int substitutionsCount; + private int substitutionsCount; Candidate(JDefinedClass candidateClass, CClassInfo candidateClassInfo, JFieldVar field, - JDefinedClass fieldParametrizationClass, JDefinedClass fieldParametrisationImpl, - JClass xmlElementDeclModelClass, JClass xmlSchemaModelClass) { + Collection parametrisationInfos, JClass xmlElementDeclModelClass, + JClass xmlSchemaModelClass) { this.candidateClass = candidateClass; this.field = field; this.fieldPropertyInfo = candidateClassInfo.getProperty(field.name()); - this.fieldParametrisationClass = fieldParametrizationClass; - this.fieldParametrisationImpl = fieldParametrisationImpl; + this.parametrisationInfos = parametrisationInfos; this.valueObjectDisabled = addObjectFactoryForClass(candidateClass); this.fieldTargetNamespace = getTargetNamespace(candidateClassInfo, xmlSchemaModelClass); collectScopedFactoryMethods(xmlElementDeclModelClass); @@ -138,7 +135,7 @@ public String getFieldName() { } /** - * The class of the only field in container class (collection interface or concrete implementation). + * The class of the only field in container class (collection interface or particular implementation). */ public JClass getFieldClass() { return (JClass) field.type(); @@ -159,20 +156,11 @@ public String getFieldTargetNamespace() { } /** - * The only parametrisation class of the field (collection type). In case of basic parametrisation like - * {@code List} this property is {@code null}. - */ - public JDefinedClass getFieldParametrisationClass() { - return fieldParametrisationClass; - } - - /** - * If {@link #getFieldParametrisationClass()} is an interface, then this holds the same value. Otherwise it holds - * the implementation (value object) of {@link #getFieldParametrisationClass()}. In case of basic parametrisation - * like {@code List} this property is {@code null}. + * The list of parametrisation classes of the field (collection types). In case of basic parametrisation like + * {@code List} this collection is empty. */ - public JDefinedClass getFieldParametrisationImpl() { - return fieldParametrisationImpl; + public Collection getParametrisationInfos() { + return parametrisationInfos; } /** diff --git a/src/main/java/com/sun/tools/xjc/addon/xew/ParametrisationInfo.java b/src/main/java/com/sun/tools/xjc/addon/xew/ParametrisationInfo.java new file mode 100644 index 0000000..4a5ba29 --- /dev/null +++ b/src/main/java/com/sun/tools/xjc/addon/xew/ParametrisationInfo.java @@ -0,0 +1,26 @@ +package com.sun.tools.xjc.addon.xew; + +import com.sun.codemodel.JDefinedClass; + +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * Container for parametrisation class and corresponding implementation. If value objects are enabled, then class and + * implementation refer the same class for simplicity. + */ +public class ParametrisationInfo { + + public final JDefinedClass parametrisationClass; + + public JDefinedClass parametrisationImpl; + + public ParametrisationInfo(JDefinedClass parametrisationClass) { + this.parametrisationClass = parametrisationClass; + } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } +} diff --git a/src/main/java/com/sun/tools/xjc/addon/xew/XmlElementWrapperPlugin.java b/src/main/java/com/sun/tools/xjc/addon/xew/XmlElementWrapperPlugin.java index 57ef24c..f8e9fcc 100644 --- a/src/main/java/com/sun/tools/xjc/addon/xew/XmlElementWrapperPlugin.java +++ b/src/main/java/com/sun/tools/xjc/addon/xew/XmlElementWrapperPlugin.java @@ -34,6 +34,7 @@ import static com.sun.tools.xjc.addon.xew.CommonUtils.isHiddenClass; import static com.sun.tools.xjc.addon.xew.CommonUtils.isListedAsParametrisation; import static com.sun.tools.xjc.addon.xew.CommonUtils.setPrivateField; +import static java.util.Collections.singletonList; import java.io.IOException; import java.util.ArrayList; @@ -153,7 +154,7 @@ protected void runInternal(Outline outline) throws ClassNotFoundException, IOExc // Write information on candidate classes to summary file. writeSummary("Candidates:"); - for (Candidate candidate : findCandidateClasses(outline, xmlElementDeclModelClass)) { + for (Candidate candidate : findCandidateClasses(outline, xmlElementsModelClass, xmlElementDeclModelClass)) { if (globalConfiguration.isClassIncluded(candidate.getClassName())) { if (globalConfiguration.isClassUnmarkedForRemoval(candidate.getClassName())) { candidate.unmarkForRemoval(); @@ -496,34 +497,37 @@ else if (candidateFieldPropertyInfo instanceof CReferencePropertyInfo) { */ private boolean moveInnerClassToParent(Outline outline, Candidate candidate) { // Skip basic parametrisations like "List": - if (candidate.getFieldParametrisationClass() == null) { + if (candidate.getParametrisationInfos().isEmpty()) { return false; } - JDefinedClass fieldParametrisationImpl = candidate.getFieldParametrisationImpl(); + boolean wasAnyClassMoved = false; - if (candidate.getClazz() != fieldParametrisationImpl.parentContainer()) { - // Field parametrisation class is not inner class of the candidate: - return false; - } + for (ParametrisationInfo parametrisationInfo : candidate.getParametrisationInfos()) { + if (candidate.getClazz() != parametrisationInfo.parametrisationImpl.parentContainer()) { + // Field parametrisation class is not inner class of the candidate, hence skipped: + continue; + } - JDefinedClass fieldParametrisationClass = candidate.getFieldParametrisationClass(); + wasAnyClassMoved = true; - String oldMethodName = fieldParametrisationClass.outer().name() + fieldParametrisationClass.name(); + String oldMethodName = parametrisationInfo.parametrisationClass.outer().name() + + parametrisationInfo.parametrisationClass.name(); - moveClassLevelUp(outline, fieldParametrisationImpl); + moveClassLevelUp(outline, parametrisationInfo.parametrisationImpl); - renameFactoryMethod(fieldParametrisationImpl._package()._getClass(FACTORY_CLASS_NAME), oldMethodName, - fieldParametrisationClass.name()); + renameFactoryMethod(parametrisationInfo.parametrisationImpl._package()._getClass(FACTORY_CLASS_NAME), + oldMethodName, parametrisationInfo.parametrisationClass.name()); - if (candidate.isValueObjectDisabled()) { - moveClassLevelUp(outline, fieldParametrisationClass); + if (candidate.isValueObjectDisabled()) { + moveClassLevelUp(outline, parametrisationInfo.parametrisationClass); - renameFactoryMethod(fieldParametrisationClass._package()._getClass(FACTORY_CLASS_NAME), oldMethodName, - fieldParametrisationClass.name()); + renameFactoryMethod(parametrisationInfo.parametrisationClass._package()._getClass(FACTORY_CLASS_NAME), + oldMethodName, parametrisationInfo.parametrisationClass.name()); + } } - return true; + return wasAnyClassMoved; } /** @@ -609,14 +613,18 @@ private int createScopedFactoryMethods(JCodeModel codeModel, JDefinedClass facto /** * Locate the candidates classes for substitution/removal. * - * @return a map className -> Candidate + * @return list of potential candidates */ - private Collection findCandidateClasses(Outline outline, JClass xmlElementDeclModelClass) { + private Collection findCandidateClasses(Outline outline, JClass xmlElementsModelClass, + JClass xmlElementDeclModelClass) { + Map classes = new HashMap<>(); Map interfaceImplementations = new HashMap<>(); // Visit all classes to create a map "interfaceName -> ClassOutline". // This map is later used to resolve implementations from interfaces. for (ClassOutline classOutline : outline.getClasses()) { + classes.put(classOutline.implClass.fullName() + ".class", classOutline.implClass); + for (Iterator iter = classOutline.implClass._implements(); iter.hasNext();) { JClass interfaceClass = iter.next(); @@ -676,30 +684,55 @@ private Collection findCandidateClasses(Outline outline, JClass xmlEl // FIXME: All known collections have exactly one parametrisation type. assert fieldParametrisations.size() == 1; - JDefinedClass fieldParametrisationClass = null; - JDefinedClass fieldParametrisationImpl = null; + List parametrisationInfos = new ArrayList<>(); - // Parametrisations like "List" or "List" are not considered. - // They are substituted as is and do not require moving of classes. if (fieldParametrisations.get(0) instanceof JDefinedClass) { - fieldParametrisationClass = (JDefinedClass) fieldParametrisations.get(0); + parametrisationInfos = singletonList( + new ParametrisationInfo((JDefinedClass) fieldParametrisations.get(0))); + } + else { + parametrisationInfos = new ArrayList<>(); + + // Parametrisations like "List" or "List" require types in @XmlElements to be considered. + JAnnotationUse annotation = getAnnotation(field, xmlElementsModelClass); + + if (annotation != null) { + JAnnotationArrayMember annotationArrayMember = (JAnnotationArrayMember) getAnnotationMember( + annotation, "value"); + + if (annotationArrayMember != null) { + for (JAnnotationUse subAnnotation : annotationArrayMember.annotations()) { + String typeClassName = getAnnotationMemberValue(subAnnotation, "type"); + if (typeClassName != null) { + JDefinedClass typeClass = classes.get(typeClassName); + + if (typeClass != null) { + parametrisationInfos.add(new ParametrisationInfo(typeClass)); + } + } + } + } + } + } + for (ParametrisationInfo parametrisationInfo : parametrisationInfos) { ClassOutline fieldParametrisationClassOutline = interfaceImplementations - .get(fieldParametrisationClass.fullName()); + .get(parametrisationInfo.parametrisationClass.fullName()); if (fieldParametrisationClassOutline != null) { - assert fieldParametrisationClassOutline.ref == fieldParametrisationClass; + assert fieldParametrisationClassOutline.ref == parametrisationInfo.parametrisationClass; - fieldParametrisationImpl = fieldParametrisationClassOutline.implClass; + parametrisationInfo.parametrisationImpl = fieldParametrisationClassOutline.implClass; } else { - fieldParametrisationImpl = fieldParametrisationClass; + // Set to the same class for simplicity: + parametrisationInfo.parametrisationImpl = parametrisationInfo.parametrisationClass; } } // We have a candidate class: - Candidate candidate = new Candidate(candidateClass, classOutline.target, field, fieldParametrisationClass, - fieldParametrisationImpl, xmlElementDeclModelClass, xmlSchemaModelClass); + Candidate candidate = new Candidate(candidateClass, classOutline.target, field, parametrisationInfos, + xmlElementDeclModelClass, xmlSchemaModelClass); candidates.add(candidate); logger.debug("Found " + candidate); @@ -758,7 +791,8 @@ private int deleteCandidates(Outline outline, Collection candidates) /** * Rename methods in factory class: {@code createABC() -> createAC()}. */ - private void renameFactoryMethod(JDefinedClass factoryClass, String oldMethodNameSuffix, String newMethodNameSuffix) { + private void renameFactoryMethod(JDefinedClass factoryClass, String oldMethodNameSuffix, + String newMethodNameSuffix) { for (JMethod method : factoryClass.methods()) { String methodName = method.name(); @@ -834,6 +868,7 @@ private void moveClassLevelUp(Outline outline, JDefinedClass clazz) { // Modify the container so it now refers the class. Container can be a class or package. JDefinedClass parent = (JDefinedClass) clazz.parentContainer(); JClassContainer grandParent = parent.parentContainer(); + // Allows to track class name collisions: Map classes; // FIXME: Pending https://java.net/jira/browse/JAXB-957 diff --git a/src/test/generated_resources/element_mixed/AnyText.java b/src/test/generated_resources/element_mixed/AnyText.java index f86fe2f..675a4f2 100644 --- a/src/test/generated_resources/element_mixed/AnyText.java +++ b/src/test/generated_resources/element_mixed/AnyText.java @@ -51,7 +51,7 @@ public class AnyText { @XmlElements({ @XmlElement(name = "i", type = I.class, namespace = "http://foo.org/"), @XmlElement(name = "b", type = B.class, namespace = "http://foo.org/"), - @XmlElement(name = "br", type = Br.class, namespace = "http://foo.org/") + @XmlElement(name = "br", type = FormattedTextBr.class, namespace = "http://foo.org/") }) protected List formattedText = new ArrayList(); @XmlElementWrapper(name = "fixed-text", required = true) diff --git a/src/test/generated_resources/element_mixed/Br.java b/src/test/generated_resources/element_mixed/Br.java index f03b03f..91d4c8f 100644 --- a/src/test/generated_resources/element_mixed/Br.java +++ b/src/test/generated_resources/element_mixed/Br.java @@ -3,6 +3,7 @@ import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAttribute; import jakarta.xml.bind.annotation.XmlType; @@ -15,6 +16,7 @@ * <complexType name="br"> * <complexContent> * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> + * <attribute name="mode" type="{http://www.w3.org/2001/XMLSchema}string" /> * </restriction> * </complexContent> * </complexType> @@ -26,5 +28,31 @@ @XmlType(name = "br") public class Br { + @XmlAttribute(name = "mode") + protected String mode; + + /** + * Gets the value of the mode property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getMode() { + return mode; + } + + /** + * Sets the value of the mode property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setMode(String value) { + this.mode = value; + } } diff --git a/src/test/generated_resources/element_mixed/FormattedTextBr.java b/src/test/generated_resources/element_mixed/FormattedTextBr.java new file mode 100644 index 0000000..cfd9033 --- /dev/null +++ b/src/test/generated_resources/element_mixed/FormattedTextBr.java @@ -0,0 +1,30 @@ + +package element_mixed; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlType; + + +/** + *

Java class for anonymous complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType>
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +public class FormattedTextBr { + + +} diff --git a/src/test/generated_resources/element_mixed/ObjectFactory.java b/src/test/generated_resources/element_mixed/ObjectFactory.java index f3820fb..f55ff5d 100644 --- a/src/test/generated_resources/element_mixed/ObjectFactory.java +++ b/src/test/generated_resources/element_mixed/ObjectFactory.java @@ -70,6 +70,14 @@ public Br createBr() { return new Br(); } + /** + * Create an instance of {@link FormattedTextBr } + * + */ + public FormattedTextBr createFormattedTextBr() { + return new FormattedTextBr(); + } + /** * Create an instance of {@link JAXBElement }{@code <}{@link B }{@code >} * diff --git a/src/test/java/com/sun/tools/xjc/addon/xew/XmlElementWrapperPluginTest.java b/src/test/java/com/sun/tools/xjc/addon/xew/XmlElementWrapperPluginTest.java index 6ead2e3..4c42593 100644 --- a/src/test/java/com/sun/tools/xjc/addon/xew/XmlElementWrapperPluginTest.java +++ b/src/test/java/com/sun/tools/xjc/addon/xew/XmlElementWrapperPluginTest.java @@ -212,7 +212,7 @@ public void testElementAnyType() throws Exception { public void testElementMixed() throws Exception { // Most classes cannot be tested for content List extraXewOptions = singletonList("-debug"); - List classesToCheck = asList("B", "Br", "I", "AnyText", "package-info"); + List classesToCheck = asList("B", "Br", "FormattedTextBr", "I", "AnyText", "package-info"); runTest("element-mixed", extraXewOptions, false, classesToCheck); } diff --git a/src/test/resources/com/sun/tools/xjc/addon/xew/element-mixed.xsd b/src/test/resources/com/sun/tools/xjc/addon/xew/element-mixed.xsd index 5a46972..df2ddb8 100644 --- a/src/test/resources/com/sun/tools/xjc/addon/xew/element-mixed.xsd +++ b/src/test/resources/com/sun/tools/xjc/addon/xew/element-mixed.xsd @@ -25,11 +25,9 @@ - - - + + + @@ -40,13 +38,18 @@ - + + + + diff --git a/src/test/resources/com/sun/tools/xjc/addon/xew/element-name-collision.xml b/src/test/resources/com/sun/tools/xjc/addon/xew/element-name-collision.xml index 4e9c1d1..0c673a7 100644 --- a/src/test/resources/com/sun/tools/xjc/addon/xew/element-name-collision.xml +++ b/src/test/resources/com/sun/tools/xjc/addon/xew/element-name-collision.xml @@ -8,11 +8,11 @@ - Mutant - SpiderMan + rebellion + Han Solo - Jedi + jedi Luke Skywalker