/* * gvNIX is an open source tool for rapid application development (RAD). * Copyright (C) 2010 Generalitat Valenciana * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ package org.gvnix.service.roo.addon.addon.ws.export; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.Validate; import org.gvnix.service.roo.addon.addon.JavaParserService; import org.gvnix.service.roo.addon.annotations.GvNIXXmlElement; import org.gvnix.service.roo.addon.annotations.GvNIXXmlElementField; import org.springframework.roo.classpath.PhysicalTypeCategory; import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils; import org.springframework.roo.classpath.PhysicalTypeMetadata; import org.springframework.roo.classpath.details.DeclaredFieldAnnotationDetails; import org.springframework.roo.classpath.details.FieldMetadata; import org.springframework.roo.classpath.details.FieldMetadataBuilder; import org.springframework.roo.classpath.details.MemberFindingUtils; import org.springframework.roo.classpath.details.MethodMetadataBuilder; import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType; import org.springframework.roo.classpath.details.annotations.AnnotationAttributeValue; import org.springframework.roo.classpath.details.annotations.AnnotationMetadata; import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder; import org.springframework.roo.classpath.details.annotations.ArrayAttributeValue; import org.springframework.roo.classpath.details.annotations.EnumAttributeValue; import org.springframework.roo.classpath.details.annotations.StringAttributeValue; import org.springframework.roo.classpath.itd.AbstractItdTypeDetailsProvidingMetadataItem; import org.springframework.roo.classpath.itd.InvocableMemberBodyBuilder; import org.springframework.roo.metadata.MetadataIdentificationUtils; import org.springframework.roo.model.EnumDetails; import org.springframework.roo.model.JavaSymbolName; import org.springframework.roo.model.JavaType; import org.springframework.roo.project.LogicalPath; /** * <p> * gvNIX Xml Element Marsharlling generation. * </p> * * @author <a href="http://www.disid.com">DISID Corporation S.L.</a> made for <a * href="http://www.dgti.gva.es">General Directorate for Information * Technologies (DGTI)</a> */ public class WSExportXmlElementMetadata extends AbstractItdTypeDetailsProvidingMetadataItem { private static final String XML_ELEMENT_STRING = WSExportXmlElementMetadata.class .getName(); private static final String XML_ELEMENT_TYPE = MetadataIdentificationUtils .create(XML_ELEMENT_STRING); public WSExportXmlElementMetadata(String id, JavaType aspectName, PhysicalTypeMetadata physicalType, List<FieldMetadata> fields, JavaParserService javaParserService) { super(id, aspectName, physicalType); // Validate metadata identifier is of type gvNIX xml element metadata Validate.isTrue(isValid(id), "Metadata identification string '" + id + "' does not appear to be valid"); if (!isValid()) { return; } // Get the gvNIX xml element annotation AnnotationMetadata annotation = governorTypeDetails .getTypeAnnotation(new JavaType(GvNIXXmlElement.class.getName())); if (annotation != null) { // Add to class XmlRoot, XmlType and XmlEnum or XmlAccessorType List<AnnotationMetadata> annotationTypeList = getAnnotations( annotation, fields); for (AnnotationMetadata annotationMetadata : annotationTypeList) { builder.addAnnotation(annotationMetadata); } // If is not a enumeration type if (!governorTypeDetails.getPhysicalTypeCategory().equals( PhysicalTypeCategory.ENUMERATION)) { // Add XmlElement annotation for each field List<DeclaredFieldAnnotationDetails> declaredFields = getXmlElementAnnotations( fields, javaParserService); for (DeclaredFieldAnnotationDetails declaredField : declaredFields) { builder.addFieldAnnotation(declaredField); } // Avoid if abstract class or interface (can't add method) if (!Modifier.isAbstract(governorTypeDetails.getModifier()) && !Modifier.isInterface(governorTypeDetails .getModifier())) { // Add annotation and method to avoid cycles convert to XML addCycleDetection(id); } } } // Build the aspect Java defined into builder itdTypeDetails = builder.build(); } /** * Add annotation and method to avoid cycles converting to XML. * * @param id Declared by metadata id */ protected void addCycleDetection(String id) { // Implements class and create method to avoid XML cycles builder.addImplementsType(new JavaType( "com.sun.xml.bind.CycleRecoverable")); // Create method executed when cycle detected JavaSymbolName methodName = new JavaSymbolName("onCycleDetected"); JavaType returnType = new JavaType(Object.class.getName()); List<AnnotatedJavaType> paramTypes = new ArrayList<AnnotatedJavaType>(1); paramTypes.add(new AnnotatedJavaType(new JavaType( "com.sun.xml.bind.CycleRecoverable.Context"), new ArrayList<AnnotationMetadata>())); List<JavaSymbolName> paramNames = new ArrayList<JavaSymbolName>(1); paramNames.add(new JavaSymbolName("context")); InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); bodyBuilder.appendFormalLine("return new " + governorTypeDetails.getName() + " ();"); builder.addMethod(new MethodMetadataBuilder(id, Modifier.PUBLIC, methodName, returnType, paramTypes, paramNames, bodyBuilder)); } /** * Create XmlElement annotation for each field. * <p> * Field XmlElement annotation has same attributes as GvNIXXmlElementField * or only name attribute with field name if GvNIXXmlElementField not * exists. * </p> * * @param fields Fields to be exported * @return All the annotated @XmlElement fields (may be empty) */ public List<DeclaredFieldAnnotationDetails> getXmlElementAnnotations( List<FieldMetadata> fields, JavaParserService javaParserService) { // Result list of annotations for fields List<DeclaredFieldAnnotationDetails> result = new ArrayList<DeclaredFieldAnnotationDetails>(); for (FieldMetadata field : fields) { // In some cases, a field can be null // If annotation already exists in Java, don't add it to AJ if (field != null && !hasAnnotation(field, "javax.xml.bind.annotation.XmlElement")) { // Get field annotation of GvNIXXmlElementField type AnnotationMetadata annotation = MemberFindingUtils .getAnnotationOfType( field.getAnnotations(), new JavaType(GvNIXXmlElementField.class .getName())); List<AnnotationAttributeValue<?>> attrs = new ArrayList<AnnotationAttributeValue<?>>(); if (annotation != null) { // Annotation exists, duplicate all annotation attributes for (JavaSymbolName javaSymbolName : annotation .getAttributeNames()) { attrs.add(annotation.getAttribute(javaSymbolName)); } } else { // Annotation not exists, create name attr with field name attrs.add(new StringAttributeValue(new JavaSymbolName( "name"), field.getFieldName().getSymbolName())); } // Add field when defined on same class (avoid parent fields) if (javaParserService.isMetadataId( governorPhysicalTypeMetadata.getId(), field)) { // Create XmlElement annotation for field with attributes result.add(new DeclaredFieldAnnotationDetails( new FieldMetadataBuilder( governorPhysicalTypeMetadata.getId(), field) .build(), new AnnotationMetadataBuilder(new JavaType( "javax.xml.bind.annotation.XmlElement"), attrs).build())); } } } return result; } /** * Class annotations: XmlRoot, XmlType and XmlEnum or XmlAccessorType. * <ul> * <li>XmlRoot: with name and namespace if annotation has name</li> * <li>XmlType: with name, propOrder and namespace</li> * <li>XmlEnum: without attributes</li> * <li>XmlAccessorType: with always field access type.</li> * </ul> * * @param annotation Original gvNIX xml element annotation * @param fields Declared fields to be exported * @return {@link List} of {@link AnnotationMetadata} to build the ITD */ protected List<AnnotationMetadata> getAnnotations( AnnotationMetadata annotation, List<FieldMetadata> fields) { // Generated result annotations list List<AnnotationMetadata> result = new ArrayList<AnnotationMetadata>(); AnnotationMetadata xmlRootAnnotation = getXmlRootAnnotation(annotation); if (xmlRootAnnotation != null) { result.add(getXmlRootAnnotation(annotation)); } // Add XmlType with name, propOrder and namespace for each field AnnotationMetadata xmlTypeAnnotation = getXmlTypeAnnotation(annotation, fields); if (xmlTypeAnnotation != null) { result.add(xmlTypeAnnotation); } if (governorTypeDetails.getPhysicalTypeCategory().equals( PhysicalTypeCategory.ENUMERATION)) { // Is an enumeration: add XmlEnum annotation without attributes. AnnotationMetadata xmlEnumAnnotation = getXmlEnumAnnotation(); if (xmlEnumAnnotation != null) { result.add(xmlEnumAnnotation); } } else { // Is not an enumeration: add XmlAccessorType with field access type AnnotationMetadata xmlAccesorType = getXmlAccesorTypeAnnotation(); if (xmlAccesorType != null) { result.add(xmlAccesorType); } } return result; } /** * Indicates whether the annotation will be introduced via this ITD. * * @param annotation to be check if exists * @return true if it will be introduced, false otherwise */ public boolean hasAnnotation(String annotation) { JavaType javaType = new JavaType(annotation); AnnotationMetadata result = governorTypeDetails.getAnnotation(javaType); return result != null; } /** * Indicates whether the annotation introduced via this field. * * @para field to be check annotations * @param annotation to be check if exists * @return true if annotation exists into field, false otherwise */ public boolean hasAnnotation(FieldMetadata field, String annotation) { // Get field annotation of GvNIXXmlElementField type AnnotationMetadata annot = MemberFindingUtils.getAnnotationOfType( field.getAnnotations(), new JavaType(annotation)); if (annot == null) { return false; } return true; } /** * Get XmlRootElement with name and namespace if annotation has name. * * @param annotation Annotation to get name and namespace * @return XmlRootElement annotation */ protected AnnotationMetadata getXmlRootAnnotation( AnnotationMetadata annotation) { // If annotation already exists in Java, not add it in AJ if (hasAnnotation("javax.xml.bind.annotation.XmlRootElement")) { return null; } // Get gvNIX xml element annotation name AnnotationAttributeValue<?> nameAttr = annotation .getAttribute(new JavaSymbolName("name")); // If name exists, create XmlRootElement annotation if (nameAttr != null) { // @XmlRootElement with name and namespace from gvNIX annotation List<AnnotationAttributeValue<?>> xmlRootElementAttrs = new ArrayList<AnnotationAttributeValue<?>>(); xmlRootElementAttrs.add(nameAttr); xmlRootElementAttrs.add(annotation.getAttribute(new JavaSymbolName( "namespace"))); return new AnnotationMetadataBuilder(new JavaType( "javax.xml.bind.annotation.XmlRootElement"), xmlRootElementAttrs).build(); } return null; } /** * Get XmlType with name, propOrder and namespace. * <ul> * <li>name from annotation if annotation has xmlTypeName, else blank</li> * <li>propOrder from fields names</li> * <li>namespace from annotation</li> * </ul> * * @param annotation * @param fields * @return */ protected AnnotationMetadata getXmlTypeAnnotation( AnnotationMetadata annotation, List<FieldMetadata> fields) { // If annotation already exists in Java, not add it in AJ if (hasAnnotation("javax.xml.bind.annotation.XmlType")) { return null; } List<AnnotationAttributeValue<?>> xmlTypeAttrs = new ArrayList<AnnotationAttributeValue<?>>(); if (annotation.getAttribute(new JavaSymbolName("xmlTypeName")) != null) { // @XmlType name from gvNIX annotation if xml type name exists xmlTypeAttrs.add(new StringAttributeValue( new JavaSymbolName("name"), ((StringAttributeValue) annotation .getAttribute(new JavaSymbolName("xmlTypeName"))) .getValue())); } else { // @XmlType without name if xml type name not exists xmlTypeAttrs.add(new StringAttributeValue( new JavaSymbolName("name"), "")); } // @XmlType prop order from declared fields List<StringAttributeValue> propOrderList = new ArrayList<StringAttributeValue>(); for (FieldMetadata field : fields) { // FIXME In unknow cases, a field can be null if (field != null) { propOrderList.add(new StringAttributeValue(new JavaSymbolName( "ignored"), field.getFieldName().getSymbolName())); } } xmlTypeAttrs.add(new ArrayAttributeValue<StringAttributeValue>( new JavaSymbolName("propOrder"), propOrderList)); // @XmlType prop order from gvNIX annotation xmlTypeAttrs.add(annotation .getAttribute(new JavaSymbolName("namespace"))); // @XmlType annotation return new AnnotationMetadataBuilder(new JavaType( "javax.xml.bind.annotation.XmlType"), xmlTypeAttrs).build(); } /** * Get XmlEnum annotation without attributes. * * @return XmlEnum annotation */ protected AnnotationMetadata getXmlEnumAnnotation() { // If annotation already exists in Java, not add it in AJ if (hasAnnotation("javax.xml.bind.annotation.XmlEnum")) { return null; } // @XmlEnum return new AnnotationMetadataBuilder(new JavaType( "javax.xml.bind.annotation.XmlEnum"), new ArrayList<AnnotationAttributeValue<?>>()).build(); } /** * Get XmlAccessorType with always field access type. * * @return XmlAccessorType annotation */ protected AnnotationMetadata getXmlAccesorTypeAnnotation() { // If annotation already exists in Java, not add it in AJ if (hasAnnotation("javax.xml.bind.annotation.XmlAccessorType")) { return null; } // @XmlAccessorType always is field access type List<AnnotationAttributeValue<?>> xmlAccessorTypeAttrs = new ArrayList<AnnotationAttributeValue<?>>(); xmlAccessorTypeAttrs.add(new EnumAttributeValue(new JavaSymbolName( "value"), new EnumDetails(new JavaType( "javax.xml.bind.annotation.XmlAccessType"), new JavaSymbolName( "FIELD")))); // @XmlAccessorType return new AnnotationMetadataBuilder(new JavaType( "javax.xml.bind.annotation.XmlAccessorType"), xmlAccessorTypeAttrs).build(); } public static String getMetadataIdentiferType() { // Get metadata identifier for this annotation return XML_ELEMENT_TYPE; } public static boolean isValid(String metadataIdentificationString) { // Is a valid gvNIX xml element physical type idenfifier ? return PhysicalTypeIdentifierNamingUtils.isValid(XML_ELEMENT_STRING, metadataIdentificationString); } public static final JavaType getJavaType(String metadataIdentificationString) { // Get java type related to gvNIX xml element physical type idenfifier return PhysicalTypeIdentifierNamingUtils.getJavaType( XML_ELEMENT_STRING, metadataIdentificationString); } public static final LogicalPath getPath(String metadataIdentificationString) { // Get path related to gvNIX xml element physical type idenfifier return PhysicalTypeIdentifierNamingUtils.getPath(XML_ELEMENT_STRING, metadataIdentificationString); } public static final String createIdentifier(JavaType javaType, LogicalPath path) { // Get gvNIX xml element physical type idenfifier for java type in path return PhysicalTypeIdentifierNamingUtils.createIdentifier( XML_ELEMENT_STRING, javaType, path); } }