/* * 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.occ.roo.addon.addon; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Logger; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; import org.gvnix.occ.roo.addon.annotations.GvNIXEntityOCCChecksum; import org.springframework.roo.addon.jpa.addon.activerecord.JpaActiveRecordMetadata; import org.springframework.roo.classpath.PhysicalTypeDetails; import org.springframework.roo.classpath.PhysicalTypeIdentifier; import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils; import org.springframework.roo.classpath.PhysicalTypeMetadata; import org.springframework.roo.classpath.TypeManagementService; import org.springframework.roo.classpath.details.BeanInfoUtils; import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails; import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetailsBuilder; import org.springframework.roo.classpath.details.FieldMetadata; import org.springframework.roo.classpath.details.FieldMetadataBuilder; import org.springframework.roo.classpath.details.ItdTypeDetails; import org.springframework.roo.classpath.details.MemberFindingUtils; import org.springframework.roo.classpath.details.MemberHoldingTypeDetails; import org.springframework.roo.classpath.details.MethodMetadata; 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.populator.AutoPopulate; import org.springframework.roo.classpath.details.annotations.populator.AutoPopulationUtils; import org.springframework.roo.classpath.itd.InvocableMemberBodyBuilder; import org.springframework.roo.classpath.itd.ItdTypeDetailsProvidingMetadataItem; import org.springframework.roo.classpath.persistence.PersistenceMemberLocator; import org.springframework.roo.classpath.scanner.MemberDetails; import org.springframework.roo.classpath.scanner.MemberDetailsScanner; import org.springframework.roo.metadata.AbstractMetadataItem; import org.springframework.roo.metadata.MetadataIdentificationUtils; import org.springframework.roo.model.JavaSymbolName; import org.springframework.roo.model.JavaType; import org.springframework.roo.project.LogicalPath; import org.springframework.roo.support.logging.HandlerUtils; /** * gvNIX OCCChecksum Metadata * * @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 OCCChecksumMetadata extends AbstractMetadataItem implements ItdTypeDetailsProvidingMetadataItem { private static final Logger LOGGER = HandlerUtils .getLogger(OCCChecksumMetadata.class); // From AbstractItdTypeDetailsProvidingMetadataItem private ClassOrInterfaceTypeDetails governorTypeDetails; private final JavaType destination; private final JavaType aspectName; private final PhysicalTypeMetadata governorPhysicalTypeMetadata; private static final String PROVIDES_TYPE_STRING = OCCChecksumMetadata.class .getName(); private static final String PROVIDES_TYPE = MetadataIdentificationUtils .create(PROVIDES_TYPE_STRING); // Template portions to create *_Roo_GvNIXRelatedPattern.aj when replacing // vars with values and concatenated in order private static final String ITD_TEMPLATE_CLASS_START = "Entity_gvnix_persistence_occ.aj_template1"; private static final String METHOD_GET_MESSAGE_DIGEST = "Entity_gvnix_persistence_occ.aj_template2"; private static final String ITD_TEMPLATE_METHOD_REMOVE = "Entity_gvnix_persistence_occ.aj_template3"; private static final String ITD_TEMPLATE_METHOD_MERGE = "Entity_gvnix_persistence_occ.aj_template4"; private static final String METHOD_CHECK_CONCURRENCY = "Entity_gvnix_persistence_occ.aj_template5"; private static final String METHOD_LOAD_CHECKSUM = "Entity_gvnix_persistence_occ.aj_template6"; private static final String METHOD_CHECKSUM_DIGEST = "Entity_gvnix_persistence_occ.aj_template7"; private static final String ITD_TEMPLATE_CLASS_END = "Entity_gvnix_persistence_occ.aj_template8"; private static final String PROPERTY_KEY = "${property}"; private static final String CODE_FORMAT_PRIMITIVE = "\tsb.append((String.valueOf(" + PROPERTY_KEY + ").equals(\"null\") ? nullstr : String.valueOf(" + PROPERTY_KEY + ")) + separator);\n"; private static final String CODE_FORMAT = "\tsb.append((" + PROPERTY_KEY + " == null ? nullstr : String.valueOf(" + PROPERTY_KEY + ")) + separator);\n"; private static final String GET_ID_KEY = "${getId}"; private static final String MANY_TO_ONE_CODE_FORMAT = "\tsb.append((" + PROPERTY_KEY + " == null ? nullstr : String.valueOf(" + PROPERTY_KEY + "." + GET_ID_KEY + "())) + separator);\n"; private JpaActiveRecordMetadata entityMetadata; // DISID: Used to get the type members private final MemberDetailsScanner memberDetailsScanner; // Used to get related persistence object private final PersistenceMemberLocator persistenceMemberLocator; private String itdFileContents = null; // From annotation @AutoPopulate private final String fieldName = "occChekcsum"; @AutoPopulate private final String digestMethod = "md5"; private FieldMetadata idField; private FieldMetadata versionField; public OCCChecksumMetadata(String identifier, JavaType aspectName, PhysicalTypeMetadata governorPhysicalTypeMetadata, JpaActiveRecordMetadata entityMetadata, MemberDetailsScanner memberDetailsScanner, TypeManagementService typeManagementService, PersistenceMemberLocator persistenceMemberLocator, FieldMetadata idField, FieldMetadata versionField) { // From AbstractItdTypeDetailsProvidingMetadataItem super(identifier); Validate.notNull(aspectName, "Aspect name required"); Validate.notNull(governorPhysicalTypeMetadata, "Governor physical type metadata required"); this.aspectName = aspectName; this.governorPhysicalTypeMetadata = governorPhysicalTypeMetadata; // DISID: Initialize memberDetailsScanner this.memberDetailsScanner = memberDetailsScanner; this.persistenceMemberLocator = persistenceMemberLocator; this.idField = idField; this.versionField = versionField; PhysicalTypeDetails physicalTypeDetails = governorPhysicalTypeMetadata .getMemberHoldingTypeDetails(); if (!(physicalTypeDetails instanceof ClassOrInterfaceTypeDetails)) { // There is a problem valid = false; } else { // We have reliable physical type details governorTypeDetails = (ClassOrInterfaceTypeDetails) physicalTypeDetails; } this.destination = governorTypeDetails.getName(); Validate.isTrue(isValid(identifier), "Metadata identification string '" + identifier + "' does not appear to be a valid"); if (entityMetadata != null) { this.entityMetadata = entityMetadata; if (!isValid()) { return; } // Process values from the annotation, if present AnnotationMetadata annotation = MemberFindingUtils .getDeclaredTypeAnnotation( governorTypeDetails, new JavaType(GvNIXEntityOCCChecksum.class.getName())); if (annotation != null) { AutoPopulationUtils.populate(this, annotation); } // Adds field to entity java file add persist it FieldMetadata field = getChecksumField(); MethodMetadata getter = getChecksumAccessor(); MethodMetadata setter = getChecksumMutator(); addChecksumFieldToEntity(field, getter, setter, typeManagementService); // Generates ITD itdFileContents = generateITDContents(field, persistenceMemberLocator); } } /** * Replace some vars with values in all AspectJ template portions and concat * results in order into one string. * <p> * Vars has next format: ${entity_package}. Template portions will be placed * at same package in src/main/resources. If some method placed in a portion * already defined (push-in), don't add it to result string. * </p> * * @param checksumField Checksum field to get field name (one var value). * @param persistenceMemberLocator To get identifier field (one var value). * @return All template portions replacing vars with values and concatenated * in order. */ private String generateITDContents(FieldMetadata checksumField, PersistenceMemberLocator persistenceMemberLocator) { StringBuilder contents = new StringBuilder(); Map<String, String> params = prepareParamsMap(checksumField); contents.append(generateITDContent(ITD_TEMPLATE_CLASS_START, checksumField, persistenceMemberLocator, params)); if (MemberFindingUtils.getDeclaredMethod(governorTypeDetails, new JavaSymbolName("getMessageDigest"), null) == null) { contents.append(generateITDContent(METHOD_GET_MESSAGE_DIGEST, checksumField, persistenceMemberLocator, params)); } if (MemberFindingUtils.getDeclaredMethod(governorTypeDetails, new JavaSymbolName("remove"), null) == null) { contents.append(generateITDContent(ITD_TEMPLATE_METHOD_REMOVE, checksumField, persistenceMemberLocator, params)); } if (MemberFindingUtils.getDeclaredMethod(governorTypeDetails, new JavaSymbolName("merge"), null) == null) { contents.append(generateITDContent(ITD_TEMPLATE_METHOD_MERGE, checksumField, persistenceMemberLocator, params)); } List<JavaType> parameters = new ArrayList<JavaType>(); parameters.add(new JavaType(governorTypeDetails.getName() .getSimpleTypeName())); if (MemberFindingUtils.getDeclaredMethod(governorTypeDetails, new JavaSymbolName("checkConcurrency")) == null) { contents.append(generateITDContent(METHOD_CHECK_CONCURRENCY, checksumField, persistenceMemberLocator, params)); } if (MemberFindingUtils.getDeclaredMethod(governorTypeDetails, new JavaSymbolName("loadChecksum"), null) == null) { contents.append(generateITDContent(METHOD_LOAD_CHECKSUM, checksumField, persistenceMemberLocator, params)); } if (MemberFindingUtils.getDeclaredMethod(governorTypeDetails, new JavaSymbolName("checksumDigest"), null) == null) { contents.append(generateITDContent(METHOD_CHECKSUM_DIGEST, checksumField, persistenceMemberLocator, params)); } contents.append(generateITDContent(ITD_TEMPLATE_CLASS_END, checksumField, persistenceMemberLocator, params)); return contents.toString(); } /** * Replace some vars with values in a template file name. * <p> * Vars has next format: ${entity_package}. Template will be placed at same * package in src/main/resources. * </p> * * @param templateName File name with a template. * @param checksumField Checksum field to get field name (one var value). * @param persistenceMemberLocator To get identifier field (one var value). * @return String replacing vars with values. */ private String generateITDContent(String templateName, FieldMetadata checksumField, PersistenceMemberLocator persistenceMemberLocator, Map<String, String> params) { // We use a template for generate ITD because the class // org.springframework.roo.classpath.details.DefaultItdTypeDetailsBuilder // dosen't have metadata for manage 'pointcuts' and 'advices'. String template; try { template = IOUtils.toString(new InputStreamReader(this.getClass() .getResourceAsStream(templateName))); } catch (IOException ioe) { throw new IllegalStateException("Unable load " + templateName, ioe); } return replaceParams(template, params); } private Map<String, String> prepareParamsMap(FieldMetadata checksumField) { Map<String, String> params = new HashMap<String, String>(10); // Adds digest generator method ('digest_method') params.put("digest_method", this.digestMethod); // Adds entity class package ('entity_package') params.put("entity_package", governorTypeDetails.getName().getPackage() .getFullyQualifiedPackageName()); // Adds entity class name ('Entity_class') params.put("entity_class", governorTypeDetails.getName() .getSimpleTypeName()); // Adds find by id method name ('findById_method') params.put("findById_method", entityMetadata.getFindMethod() .getMethodName().getSymbolName()); // Adds id field name ('id_field') // TODO Now get identifier is a collection, temporaly getted first params.put("id_field", idField.getFieldName().getSymbolName()); // Adds checksum field name ('checksum_field') params.put("checksum_field", checksumField.getFieldName() .getSymbolName()); // Adds the code to transform local fields to a string // ('local_fields_to_String') params.put("local_fields_to_String", getCodeToTranformFieldsToString()); return params; } /** * Gets java code to generate the string that represents the object state. * Currently it uses all the entity's properties that fulfill this * conditions: * <ol> * <li>has accessor</li> * <li>relationship: only ManyToOne</li> * <li>no transient</li> * <li>no version(as it has no sense)</li> * </ol> * TODO Include embeddedID TODO Include support for relationship and * transient (but not by default) TODO Support include/exclude properties * (by adding attributes to GvNIXOCCCheck annotation) * * @return */ private String getCodeToTranformFieldsToString() { MemberDetails members = memberDetailsScanner.getMemberDetails( governorTypeDetails.getClass().getName(), governorTypeDetails); return getToStringCodeForRooBean(members, null); } /** * Get string of code required to generate the string representation of a * Roo Bean * * @param members of the Roo Bean * @param prefix for every property (use null for this bean itself) * @return */ private String getToStringCodeForRooBean(MemberDetails members, String prefix) { StringBuilder strb = new StringBuilder(); for (MemberHoldingTypeDetails memberHoldingTypeDetails : members .getDetails()) { for (MethodMetadata method : memberHoldingTypeDetails .getDeclaredMethods()) { if (BeanInfoUtils.isAccessorMethod(method)) { JavaSymbolName propertyName = BeanInfoUtils .getPropertyNameForJavaBeanMethod(method); FieldMetadata field = BeanInfoUtils .getFieldForPropertyName(members, propertyName); if (field != null) { if (MemberFindingUtils.getAnnotationOfType(field .getAnnotations(), new JavaType( "javax.persistence.Version")) != null) { continue; } if (MemberFindingUtils.getAnnotationOfType(field .getAnnotations(), new JavaType( "javax.persistence.Transient")) != null) { continue; } if (Modifier.isTransient(field.getModifier())) { continue; } if (MemberFindingUtils.getAnnotationOfType(field .getAnnotations(), new JavaType( "javax.persistence.ManyToMany")) != null) { continue; } if (MemberFindingUtils.getAnnotationOfType(field .getAnnotations(), new JavaType( "javax.persistence.OneToMany")) != null) { continue; } if (MemberFindingUtils.getAnnotationOfType(field .getAnnotations(), new JavaType( "javax.persistence.ManyToOne")) != null) { strb.append(getToStringManyToOneProperty(method, field, prefix)); continue; } if (MemberFindingUtils.getAnnotationOfType(field .getAnnotations(), new JavaType( "javax.persistence.EmbeddedId")) != null) { continue; } strb.append(getToStringCodeForBaseField(prefix, method.getMethodName(), field)); } } } } return strb.toString(); } private String getToStringCodeForBaseField(String prefix, JavaSymbolName fieldAccessorName, FieldMetadata field) { String formatStringToUse = CODE_FORMAT; if (field.getFieldType().isPrimitive()) { formatStringToUse = CODE_FORMAT_PRIMITIVE; } return StringUtils.replace( formatStringToUse, PROPERTY_KEY, getPropertyAccesorExpression(prefix, fieldAccessorName, field.getFieldName())); } /** * Generate to-string code for a Many-to-One member property. This method * uses the pk of related object. Supports simple pks or embeddedId pks. * * @param propAccessor * @param field * @param aPrefix * @return */ private Object getToStringManyToOneProperty(MethodMetadata propAccessor, FieldMetadata field, String prefix) { // Get related object type final JavaType propertyType = field.getFieldType(); // Get realted object PK accesor final MethodMetadata idAccesor = persistenceMemberLocator .getIdentifierAccessor(propertyType); // #9561: Avoid NullPointerException in strange cases if (idAccesor == null) return StringUtils.EMPTY; final JavaType reltatedObjecIdType = idAccesor.getReturnType(); String propAccessExpression = getPropertyAccesorExpression(prefix, propAccessor.getMethodName(), field.getFieldName()); // check related object id type if (reltatedObjecIdType.isCoreType() || reltatedObjecIdType.isPrimitive()) { // Is a primitive or core java type return StringUtils.replaceEach(MANY_TO_ONE_CODE_FORMAT, new String[] { PROPERTY_KEY, GET_ID_KEY }, new String[] { propAccessExpression, idAccesor.getMethodName().toString() }); } else { // is embededPk List<FieldMetadata> embeddedFields = persistenceMemberLocator .getEmbeddedIdentifierFields(propertyType); StringBuilder strb = new StringBuilder(); propAccessExpression = getPropertyAccesorExpression( propAccessExpression, idAccesor.getMethodName(), field.getFieldName()); JavaSymbolName embeddedAccessorName; for (FieldMetadata embeddedField : embeddedFields) { embeddedAccessorName = BeanInfoUtils .getAccessorMethodName(embeddedField); strb.append(getToStringCodeForBaseField(propAccessExpression, embeddedAccessorName, embeddedField)); } return strb.toString(); } } /** * Gets method body expression for property-getter method * * @param prefix * @param propAccessorName * @param fieldName * @return */ private String getPropertyAccesorExpression(String prefix, JavaSymbolName propAccessorName, JavaSymbolName fieldName) { if (StringUtils.isBlank(prefix)) { return fieldName.toString(); } else { return String.format("%s.%s()", prefix, propAccessorName); } } /** * Replace map values on a template string * * @param template * @param params * @return */ private String replaceParams(String template, Map<String, String> params) { for (Entry<String, String> entry : params.entrySet()) { template = StringUtils.replace(template, "${" + entry.getKey() + "}", entry.getValue()); } return template; } /** * Add checksum field (plus getter/setter) to builder * * @param field * @param getter * @param setter * @param typeManagementService */ private void addChecksumFieldToEntity(FieldMetadata field, MethodMetadata getter, MethodMetadata setter, TypeManagementService typeManagementService) { PhysicalTypeDetails ptd = governorPhysicalTypeMetadata .getMemberHoldingTypeDetails(); Validate.isInstanceOf( ClassOrInterfaceTypeDetails.class, ptd, "Java source code is immutable for type " + PhysicalTypeIdentifier .getFriendlyName(governorPhysicalTypeMetadata .getId())); ClassOrInterfaceTypeDetailsBuilder mutableTypeDetails = new ClassOrInterfaceTypeDetailsBuilder( (ClassOrInterfaceTypeDetails) ptd); // Try to locate an existing field with @javax.persistence.Version try { if (!fieldExist(mutableTypeDetails, field)) { mutableTypeDetails.addField(new FieldMetadataBuilder( governorTypeDetails.getDeclaredByMetadataId(), field)); } if (!methodExists(mutableTypeDetails, getter)) { mutableTypeDetails.addMethod(getter); } if (!methodExists(mutableTypeDetails, setter)) { mutableTypeDetails.addMethod(setter); } typeManagementService.createOrUpdateTypeOnDisk(mutableTypeDetails .build()); } catch (IllegalArgumentException e) { // TODO In some cases, one element is added more than one time LOGGER.finest("Problem adding version field: ".concat(e.toString())); } } /** * Check if received method exists * * @param mutableTypeDetails * @param method * @return */ private boolean methodExists( ClassOrInterfaceTypeDetailsBuilder mutableTypeDetails, MethodMetadata method) { if (method == null) { return false; } for (MethodMetadataBuilder methodBuilder : mutableTypeDetails .getDeclaredMethods()) { if (!methodBuilder.getReturnType().equals(method.getReturnType())) { continue; } if (!methodBuilder.getMethodName().equals(method.getMethodName())) { continue; } if (methodBuilder.getParameterTypes().size() != method .getParameterTypes().size()) { continue; } Iterator<AnnotatedJavaType> builderParamTypeIter = methodBuilder .getParameterTypes().iterator(); Iterator<AnnotatedJavaType> paramTypeIter = method .getParameterTypes().iterator(); AnnotatedJavaType builderParamType, paramType; boolean match = true; while (builderParamTypeIter.hasNext()) { builderParamType = builderParamTypeIter.next(); paramType = paramTypeIter.next(); if (!builderParamType.equals(paramType)) { match = false; break; } } if (!match) { continue; } return true; } return false; } /** * Check if recived filed exist * * @param mutableTypeDetails * @param field * @return */ private boolean fieldExist( ClassOrInterfaceTypeDetailsBuilder mutableTypeDetails, FieldMetadata field) { boolean annotationFound; boolean found = false; for (FieldMetadataBuilder fieldBuilder : mutableTypeDetails .getDeclaredFields()) { if (!fieldBuilder.getFieldType().equals(field.getFieldType())) { continue; } if (!fieldBuilder.getFieldName().equals(field.getFieldName())) { continue; } if (fieldBuilder.getAnnotations() != null && field.getAnnotations() == null) { continue; } else if (fieldBuilder.getAnnotations() == null && field.getAnnotations() != null) { continue; } else if (fieldBuilder.getAnnotations().size() != field .getAnnotations().size()) { continue; } found = true; for (AnnotationMetadataBuilder annotation : fieldBuilder .getAnnotations()) { annotationFound = false; for (AnnotationMetadata fieldAnnotation : field .getAnnotations()) { if (annotation.getAnnotationType().equals( fieldAnnotation.getAnnotationType())) { annotationFound = true; break; } } if (!annotationFound) { found = false; break; } } if (!found) { continue; } return true; } return false; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { ToStringBuilder tsc = new ToStringBuilder(this); tsc.append("identifier", getId()); tsc.append("valid", valid); tsc.append("aspectName", aspectName); tsc.append("destinationType", destination); tsc.append("governor", governorPhysicalTypeMetadata.getId()); return tsc.toString(); } /** * @return metadata identifier type */ public static final String getMetadataIdentiferType() { return PROVIDES_TYPE; } /** * Checks if Metadata is valid * * @param metadataIdentificationString * @return */ public static boolean isValid(String metadataIdentificationString) { return PhysicalTypeIdentifierNamingUtils.isValid(PROVIDES_TYPE_STRING, metadataIdentificationString); } /** * Locates the checksum field. * * @return the checksum (may return null) */ public FieldMetadata getChecksumField() { // Try to locate an existing field with @javax.persistence.Version List<FieldMetadata> found = MemberFindingUtils.getFieldsWithAnnotation( governorTypeDetails, new JavaType("javax.persistence.Version")); if (found.size() > 0) { Validate.isTrue(found.size() == 1, "More than 1 field was annotated with @javax.persistence.Version in '" + governorTypeDetails.getName() .getFullyQualifiedTypeName() + "'"); FieldMetadata field = found.get(0); Validate.isTrue( field.getFieldType().equals( new JavaType(String.class.getName())), "Field '" + field.getFieldName().getSymbolName() + "' must be java.lang.String"); Validate.isTrue(MemberFindingUtils.getAnnotationOfType(field .getAnnotations(), new JavaType( "javax.persistence.Transient")) != null, "Field '" + field.getFieldName().getSymbolName() + "' must have @Transient annotation"); if (!field.getFieldName().getSymbolName().equals(fieldName)) { LOGGER.warning(governorTypeDetails.getName() .getFullyQualifiedTypeName() + ": The @Version field name (" + field.getFieldName().getSymbolName() + ") does not match with @GvNIXEntityOCCChecksum.fieldName (" + fieldName + ")"); } return field; } // Ensure there isn't already a field called like fieldName; if so, // compute a // unique name (it's not really a fatal situation at the end of the day) StringBuilder fieldNameBuilder = new StringBuilder(); JavaSymbolName checksumField = null; // Compute the required field name while (true) { checksumField = new JavaSymbolName(fieldNameBuilder.toString() .concat(this.fieldName)); if (MemberFindingUtils.getField(governorTypeDetails, checksumField) == null) { // Found a usable field name break; } fieldNameBuilder.append('_'); } // We're creating one List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>(); AnnotationMetadataBuilder idAnnotation = new AnnotationMetadataBuilder( new JavaType("javax.persistence.Version"), new ArrayList<AnnotationAttributeValue<?>>()); annotations.add(idAnnotation); idAnnotation = new AnnotationMetadataBuilder(new JavaType( "javax.persistence.Transient"), new ArrayList<AnnotationAttributeValue<?>>()); annotations.add(idAnnotation); FieldMetadata field = new FieldMetadataBuilder(getId(), Modifier.PRIVATE, annotations, checksumField, JavaType.STRING) .build(); return field; } /** * Locates the checksum accessor method. * * @return the version identifier (may return null if there is no version * field declared in this class) */ public MethodMetadata getChecksumAccessor() { FieldMetadata checksum = getChecksumField(); // Compute the name of the accessor that will be produced String requiredAccessorName = "get" + StringUtils.capitalize(checksum.getFieldName() .getSymbolName()); // See if the user provided the field, and thus the accessor method if (!getId().equals(checksum.getDeclaredByMetadataId())) { MethodMetadata method = MemberFindingUtils.getMethod( governorTypeDetails, new JavaSymbolName( requiredAccessorName), new ArrayList<JavaType>()); return method; } // We declared the field in this ITD, so produce a public accessor for // it InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); bodyBuilder.appendFormalLine("return this." + checksum.getFieldName().getSymbolName() + ";"); return new MethodMetadataBuilder(getId(), Modifier.PUBLIC, new JavaSymbolName(requiredAccessorName), checksum.getFieldType(), new ArrayList<AnnotatedJavaType>(), new ArrayList<JavaSymbolName>(), bodyBuilder).build(); } /** * Locates the checksum mutator * * @return the version identifier (may return null if there is no version * field declared in this class) */ public MethodMetadata getChecksumMutator() { // Locate the version field, and compute the name of the mutator that // will be produced FieldMetadata chekcsum = getChecksumField(); if (chekcsum == null) { // There's no version field, so there certainly won't be a mutator // for it return null; } String requiredMutatorName = "set" + StringUtils.capitalize(chekcsum.getFieldName() .getSymbolName()); List<JavaType> paramTypes = new ArrayList<JavaType>(); paramTypes.add(chekcsum.getFieldType()); List<JavaSymbolName> paramNames = new ArrayList<JavaSymbolName>(); paramNames.add(new JavaSymbolName("checksum")); // See if the user provided the field, and thus the accessor method if (!getId().equals(chekcsum.getDeclaredByMetadataId())) { MethodMetadata method = MemberFindingUtils.getMethod( governorTypeDetails, new JavaSymbolName(requiredMutatorName), paramTypes); return method; } // We declared the field in this ITD, so produce a public mutator for it InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); bodyBuilder.appendFormalLine("this." + chekcsum.getFieldName().getSymbolName() + " = checksum;"); return new MethodMetadataBuilder(getId(), Modifier.PUBLIC, new JavaSymbolName(requiredMutatorName), JavaType.VOID_PRIMITIVE, AnnotatedJavaType.convertFromJavaTypes(paramTypes), paramNames, bodyBuilder).build(); } public static final String createIdentifier(JavaType javaType, LogicalPath path) { return PhysicalTypeIdentifierNamingUtils.createIdentifier( PROVIDES_TYPE_STRING, javaType, path); } public static final JavaType getJavaType(String metadataIdentificationString) { return PhysicalTypeIdentifierNamingUtils.getJavaType( PROVIDES_TYPE_STRING, metadataIdentificationString); } public static final LogicalPath getPath(String metadataIdentificationString) { return PhysicalTypeIdentifierNamingUtils.getPath(PROVIDES_TYPE_STRING, metadataIdentificationString); } /** * @return gets Itd file contents */ public String getItdFileContents() { return itdFileContents; } /* (non-Javadoc) * @see org.springframework.roo.classpath.itd.MemberHoldingTypeDetailsMetadataItem#getMemberHoldingTypeDetails() */ public ItdTypeDetails getMemberHoldingTypeDetails() { // TODO Auto-generated method stub return null; } }