/*
* Copyright 2012 PRODYNA AG
*
* Licensed under the Eclipse Public License (EPL), Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php or
* http://www.nabucco.org/License.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.nabucco.framework.generator.compiler.transformation.java.common.reflection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.ArrayReference;
import org.eclipse.jdt.internal.compiler.ast.BinaryExpression;
import org.eclipse.jdt.internal.compiler.ast.CastExpression;
import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.IfStatement;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.Literal;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.nabucco.framework.generator.compiler.NabuccoCompilerSupport;
import org.nabucco.framework.generator.compiler.transformation.NabuccoTransformationConstants;
import org.nabucco.framework.generator.compiler.transformation.common.annotation.NabuccoAnnotation;
import org.nabucco.framework.generator.compiler.transformation.common.annotation.NabuccoAnnotationMapper;
import org.nabucco.framework.generator.compiler.transformation.common.annotation.NabuccoAnnotationType;
import org.nabucco.framework.generator.compiler.transformation.common.annotation.association.AssociationStrategyType;
import org.nabucco.framework.generator.compiler.transformation.common.annotation.association.OrderStrategyType;
import org.nabucco.framework.generator.compiler.transformation.common.collection.CollectionImplementationType;
import org.nabucco.framework.generator.compiler.transformation.java.common.ast.JavaAstSupport;
import org.nabucco.framework.generator.compiler.transformation.java.constants.JavaConstants;
import org.nabucco.framework.generator.compiler.transformation.java.datatype.CodePathSupport;
import org.nabucco.framework.generator.compiler.transformation.util.NabuccoTransformationUtility;
import org.nabucco.framework.generator.compiler.visitor.NabuccoVisitorException;
import org.nabucco.framework.generator.compiler.visitor.util.NabuccoPropertyKey;
import org.nabucco.framework.generator.parser.model.modifier.NabuccoModifierType;
import org.nabucco.framework.generator.parser.model.multiplicity.NabuccoMultiplicityType;
import org.nabucco.framework.generator.parser.model.multiplicity.NabuccoMultiplicityTypeMapper;
import org.nabucco.framework.generator.parser.syntaxtree.AnnotationDeclaration;
import org.nabucco.framework.generator.parser.syntaxtree.BasetypeDeclaration;
import org.nabucco.framework.generator.parser.syntaxtree.DatatypeDeclaration;
import org.nabucco.framework.generator.parser.syntaxtree.DatatypeStatement;
import org.nabucco.framework.generator.parser.syntaxtree.EnumerationDeclaration;
import org.nabucco.framework.generator.parser.syntaxtree.MessageStatement;
import org.nabucco.framework.generator.parser.syntaxtree.Node;
import org.nabucco.framework.generator.parser.syntaxtree.NodeToken;
import org.nabucco.framework.generator.parser.visitor.DepthFirstVisitor;
import org.nabucco.framework.mda.model.java.JavaCompilationUnit;
import org.nabucco.framework.mda.model.java.JavaModelException;
import org.nabucco.framework.mda.model.java.ast.JavaAstMethodCall;
import org.nabucco.framework.mda.model.java.ast.JavaAstType;
import org.nabucco.framework.mda.model.java.ast.element.JavaAstElementFactory;
import org.nabucco.framework.mda.model.java.ast.element.discriminator.BinaryExpressionType;
import org.nabucco.framework.mda.model.java.ast.element.discriminator.LiteralType;
import org.nabucco.framework.mda.model.java.ast.element.method.JavaAstMethodSignature;
import org.nabucco.framework.mda.model.java.ast.produce.JavaAstModelProducer;
/**
* NabuccoToJavaPropertiesVisitor
*
* @author Nicolas Moser, PRODYNA AG
*/
class NabuccoToJavaPropertiesVisitor extends DepthFirstVisitor {
/* Constants */
private static final String DESCRIPTOR_SUPPORT = "org.nabucco.framework.base.facade.datatype.property.PropertyDescriptorSupport";
private static final String ASSOCIATION_TYPE = "PropertyAssociationType";
private static final String METHOD_GET_TYPE = "getType";
private static final String METHOD_GET_PROPERTY_DESCRIPTOR = "getPropertyDescriptor";
private static final String METHOD_GET_NAME = "getName";
private static final String METHOD_CREATE = "create";
private static final String METHOD_CREATE_PROPERTY = METHOD_CREATE + "Property";
private static final String METHOD_CREATE_BASETYPE = METHOD_CREATE + "Basetype";
private static final String METHOD_CREATE_ENUMERATION = METHOD_CREATE + "Enumeration";
private static final String METHOD_CREATE_DATATYPE = METHOD_CREATE + "Datatype";
private static final String METHOD_CREATE_COLLECTION = METHOD_CREATE + "Collection";
private static final String METHOD_ADD = "add";
private static final String METHOD_PUT = "put";
private static final String VARIABLE_PROPERTY_MAP = "propertyMap";
private static final String VARIABLE_PROPERTIES = "properties";
private static final String VARIABLE_PROPERTY = "property";
private static final String FIELD_CONSTRAINTS = "PROPERTY_CONSTRAINTS";
private static final String CLASS_SUPPORT = "PropertyDescriptorSupport";
private static final JavaAstMethodSignature SIGNATURE_CREATEPROPERTYCONTAINER = new JavaAstMethodSignature(
"createPropertyContainer");
private static final JavaAstMethodSignature SIGNATURE_SETPROPERTY = new JavaAstMethodSignature("setProperty",
new String[] { "NabuccoProperty" });
/* Instance Variables */
private String qualifiedName;
private Set<String> imports;
private SingleNameReference propertyList;
private IfStatement setPropertyStatementRoot;
private List<MessageSend> getPropertyStatements = new ArrayList<MessageSend>();
private Set<PropertyType> propertyTypes = new HashSet<PropertyType>();
private List<SingleNameReference> propertyNames = new ArrayList<SingleNameReference>();
private List<FieldDeclaration> propertyNameFields = new ArrayList<FieldDeclaration>();
private List<Statement> propertyContainerStatements = new ArrayList<Statement>();
private String currentTypeName;
private String extention;
private CollectionImplementationType collectionImplementation;
private Map<NabuccoPropertyKey, Node> parentProperties;
/**
* Creates a new {@link NabuccoToJavaPropertiesVisitor} instance.
*
* @param qualifiedName
* the qualified nabucco type name to create properties for
* @param imports
* the nabucco imports of the nabucco type
* @param nabuccoExtension
* the nabucco extentsion definition if there is any, <code>null</code> otherwise
* @param properties
* the parent properties
*/
public NabuccoToJavaPropertiesVisitor(String qualifiedName, Set<String> imports, String nabuccoExtension,
Map<NabuccoPropertyKey, Node> properties, CollectionImplementationType collectionImplementation) {
if (qualifiedName == null) {
throw new IllegalArgumentException("Cannot create NabuccoToJavaPropertiesVisitor for name [null].");
}
this.qualifiedName = qualifiedName;
this.imports = imports;
this.extention = nabuccoExtension;
this.collectionImplementation = collectionImplementation;
this.parentProperties = properties;
}
@Override
public void visit(DatatypeStatement datatype) {
this.currentTypeName = datatype.nodeToken2.tokenImage;
super.visit(datatype);
}
@Override
public void visit(MessageStatement message) {
this.currentTypeName = message.nodeToken2.tokenImage;
super.visit(message);
}
@Override
public void visit(BasetypeDeclaration basetype) {
if (isRedefinition(basetype)) {
return;
}
String name = basetype.nodeToken3.tokenImage;
String type = basetype.nodeToken1.tokenImage;
NabuccoMultiplicityType multiplicity = NabuccoMultiplicityTypeMapper.getInstance().mapToMultiplicity(
basetype.nodeToken2.tokenImage);
boolean isTechnical = this.isTechnical(basetype.annotationDeclaration);
boolean isTransient = basetype.nodeOptional.present();
try {
if (multiplicity.isMultiple()) {
OrderStrategyType ordered = this.getOrderStrategy(basetype.annotationDeclaration);
this.createCollectionProperty(name, type, null, ordered, isTechnical, isTransient);
} else {
this.createBasetypeProperty(name, type, isTechnical, isTransient);
}
} catch (JavaModelException e) {
throw new NabuccoVisitorException("Error creating datatype reflection.", e);
}
}
@Override
public void visit(EnumerationDeclaration enumeration) {
if (isRedefinition(enumeration)) {
return;
}
String name = enumeration.nodeToken2.tokenImage;
String type = ((NodeToken) enumeration.nodeChoice1.choice).tokenImage;
NabuccoMultiplicityType multiplicity = NabuccoMultiplicityTypeMapper.getInstance().mapToMultiplicity(
enumeration.nodeToken1.tokenImage);
boolean isTechnical = this.isTechnical(enumeration.annotationDeclaration);
boolean isTransient = enumeration.nodeOptional.present();
try {
if (multiplicity.isMultiple()) {
OrderStrategyType ordered = this.getOrderStrategy(enumeration.annotationDeclaration);
this.createCollectionProperty(name, type, null, ordered, isTechnical, isTransient);
} else {
this.createEnumProperty(name, type, isTechnical, isTransient);
}
} catch (JavaModelException e) {
throw new NabuccoVisitorException("Error creating datatype reflection.", e);
}
}
@Override
public void visit(DatatypeDeclaration datatype) {
String name = datatype.nodeToken2.tokenImage;
String type = ((NodeToken) datatype.nodeChoice1.choice).tokenImage;
NabuccoMultiplicityType multiplicity = NabuccoMultiplicityTypeMapper.getInstance().mapToMultiplicity(
datatype.nodeToken1.tokenImage);
AssociationStrategyType strategy = this.getAssociationStrategy(datatype.annotationDeclaration);
boolean isTransient = datatype.nodeOptional.present();
boolean isTechnical = this.isTechnical(datatype.annotationDeclaration);
boolean isCode = this.isCode(datatype.annotationDeclaration);
try {
if (multiplicity.isMultiple()) {
OrderStrategyType ordered = this.getOrderStrategy(datatype.annotationDeclaration);
this.createCollectionProperty(name, type, strategy, ordered, isTechnical, isTransient);
} else {
this.createDatatypeProperty(name, type, strategy, isTechnical, isTransient, isCode);
}
} catch (JavaModelException e) {
throw new NabuccoVisitorException("Error creating datatype reflection.", e);
}
}
/**
* Extracts the association strategy type of the given annotations.
*
* @param annotations
* the datatype annotations
*
* @return the association strategy
*/
private AssociationStrategyType getAssociationStrategy(AnnotationDeclaration annotations) {
NabuccoAnnotation strategyAnnotation = NabuccoAnnotationMapper.getInstance().mapToAnnotation(annotations,
NabuccoAnnotationType.ASSOCIATION_STRATEGY);
AssociationStrategyType strategy = (strategyAnnotation != null) ? AssociationStrategyType
.getType(strategyAnnotation.getValue()) : AssociationStrategyType.COMPOSITION;
return strategy;
}
/**
* Extracts the order strategy type of the given annotations.
*
* @param annotations
* the datatype annotations
*
* @return the order strategy
*/
private OrderStrategyType getOrderStrategy(AnnotationDeclaration annotations) {
NabuccoAnnotation orderedAnnotation = NabuccoAnnotationMapper.getInstance().mapToAnnotation(annotations,
NabuccoAnnotationType.ORDER_STRATEGY);
OrderStrategyType ordered = (orderedAnnotation != null) ? OrderStrategyType.getType(orderedAnnotation
.getValue()) : OrderStrategyType.ORDERED;
return ordered;
}
/**
* Check whether a declaration is @Redefined or not.
*
* @param node
* the AST node to check.
*
* @return <b>true</b> if the node is redefined, <b>false</b> if not
*/
private static boolean isRedefinition(Node node) {
if (node instanceof BasetypeDeclaration) {
BasetypeDeclaration basetype = (BasetypeDeclaration) node;
if (NabuccoAnnotationMapper.getInstance().mapToAnnotation(basetype.annotationDeclaration,
NabuccoAnnotationType.REDEFINED) == null) {
return false;
}
return true;
} else if (node instanceof EnumerationDeclaration) {
EnumerationDeclaration nabuccoEnum = (EnumerationDeclaration) node;
if (NabuccoAnnotationMapper.getInstance().mapToAnnotation(nabuccoEnum.annotationDeclaration,
NabuccoAnnotationType.REDEFINED) == null) {
return false;
}
return true;
}
return false;
}
/**
* Check whether a declaration is @Technical or not.
*
* @param annotations
* the annotation declaration
*
* @return <b>true</b> if the declaration is technical, <b>false</b> if not
*/
private boolean isTechnical(AnnotationDeclaration annotations) {
NabuccoAnnotation technical = NabuccoAnnotationMapper.getInstance().mapToAnnotation(annotations,
NabuccoAnnotationType.TECHNICAL_PROPERTY);
return technical != null;
}
/**
* Check whether a code path is defined at the datatype declaration.
*
* @param annotations
* the annotations to check
*
* @return <b>true</b> if the declaration holds a code path, <b>false</b> if not
*/
private boolean isCode(AnnotationDeclaration annotations) {
NabuccoAnnotation codePath = NabuccoAnnotationMapper.getInstance().mapToAnnotation(annotations,
NabuccoAnnotationType.CODE_PATH);
if (codePath == null) {
return false;
}
return codePath.getValue() != null;
}
/**
* Create the BasetypeProperty allocation.
*
* @param name
* name of the property
* @param type
* type of the property
* @param isTechnical
* whether the property is technical or not
*
* @throws JavaModelException
*/
private void createBasetypeProperty(String name, String type, boolean isTechnical, boolean isTransient)
throws JavaModelException {
this.createProperty(name, type, PropertyType.BASETYPE, null, isTechnical, isTransient, null);
}
/**
* Create the EnumProperty allocation.
*
* @param name
* name of the property
* @param type
* type of the property
* @param isTechnical
* whether the property is technical or not
*
* @throws JavaModelException
*/
private void createEnumProperty(String name, String type, boolean isTechnical, boolean isTransient)
throws JavaModelException {
this.createProperty(name, type, PropertyType.ENUMERATION, null, isTechnical, isTransient, null);
}
/**
* Create the DatatypeProperty allocation.
*
* @param name
* name of the property
* @param type
* type of the property
* @param strategy
* the association strategy
* @param isTechnical
* the flag indicating whether the property is technical or not
*
* @throws JavaModelException
*/
private void createDatatypeProperty(String name, String type, AssociationStrategyType strategy,
boolean isTechnical, boolean isTransient, boolean isCode) throws JavaModelException {
QualifiedNameReference associationType = this.createStrategyType(type, strategy);
List<Expression> additionalArguments = new ArrayList<Expression>();
additionalArguments.add(associationType);
if (isCode) {
additionalArguments.add(this.createCodePath(name));
}
this.createProperty(name, type, PropertyType.DATATYPE, null, isTechnical, isTransient, additionalArguments);
}
/**
* Create the code path string literal.
*
* @param constant
* the constant holding the code path
*
* @return the java ast string literal
*
* @throws JavaModelException
* when the literal cannot be created
*/
private SingleNameReference createCodePath(String fieldName) throws JavaModelException {
String constant = CodePathSupport.createCodePathConstant(fieldName);
return JavaAstModelProducer.getInstance().createSingleNameReference(constant);
}
/**
* Create the ListProperty allocation.
*
* @param name
* name of the property
* @param type
* type of the property
* @param strategy
* the association strategy
* @param orderStrategy
* the order strategy
* @param isTechnical
* whether the property is technical or not
* @param isTransient
* whether the property is transient or not
*
* @throws JavaModelException
* when the creation fails
*/
private void createCollectionProperty(String name, String type, AssociationStrategyType strategy,
OrderStrategyType orderStrategy, boolean isTechnical, boolean isTransient) throws JavaModelException {
QualifiedNameReference associationType = this.createStrategyType(type, strategy);
this.createProperty(name, type, PropertyType.COLLECTION, orderStrategy, isTechnical, isTransient,
Arrays.<Expression> asList(associationType));
}
/**
* Create the strategy type for the related type and strategy annotation.
*
* @param type
* field type
* @param strategy
* the association strategy
*
* @return the name reference for the given strategy type
*
* @throws JavaModelException
*/
private QualifiedNameReference createStrategyType(String type, AssociationStrategyType strategy)
throws JavaModelException {
String importString = NabuccoCompilerSupport.resolveImport(type, this.imports);
if (NabuccoCompilerSupport.isOtherComponent(this.qualifiedName, importString)) {
return JavaAstModelProducer.getInstance().createQualifiedNameReference(ASSOCIATION_TYPE,
AssociationStrategyType.COMPONENT.getId());
}
if (strategy != null) {
return JavaAstModelProducer.getInstance().createQualifiedNameReference(ASSOCIATION_TYPE, strategy.getId());
}
return JavaAstModelProducer.getInstance().createQualifiedNameReference(ASSOCIATION_TYPE,
AssociationStrategyType.COMPOSITION.getId());
}
/**
* Create a single property allocation.
*
* @param fieldName
* the field name
* @param fieldType
* the field type
* @param propertyType
* the property type
* @param orderStrategy
* the order strategy
* @param isTechnical
* whether the property is technical or not
* @param additionalArguments
* additional constructor arguments
*
* @return the 'add()' method call including the property allocation
*
* @throws JavaModelException
*/
private void createProperty(String fieldName, String fieldType, PropertyType propertyType,
OrderStrategyType orderStrategy, boolean isTechnical, boolean isTransient,
List<Expression> additionalArguments) throws JavaModelException {
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
if (this.propertyList == null) {
this.propertyList = producer.createSingleNameReference(VARIABLE_PROPERTIES);
}
boolean isInherited = false;
if (this.parentProperties != null) {
for (NabuccoPropertyKey key : this.parentProperties.keySet()) {
if (key.getName().equals(fieldName)) {
isInherited = true;
}
}
}
if (isInherited) {
// Fragment for Method 'getProperties'
MessageSend addProperty = this.createGetPropertyStatement(fieldName, fieldType, isTransient, propertyType);
this.getPropertyStatements.add(addProperty);
return;
}
/** STATIC PROPERTIES */
// Create a static final field for each property to identifier the name
String staticFieldName = fieldName.toUpperCase();
FieldDeclaration field = JavaAstSupport.createField(JavaConstants.STRING, staticFieldName,
NabuccoModifierType.PUBLIC).getAstNode();
field.initialization = producer.createLiteral(fieldName, LiteralType.STRING_LITERAL);
JavaAstElementFactory.getInstance().getJavaAstField()
.addModifier(field, ClassFileConstants.AccFinal | ClassFileConstants.AccStatic);
this.propertyNameFields.add(field);
// Add name to constant array 'PROPERTY_NAME'
this.propertyNames.add(producer.createSingleNameReference(staticFieldName));
// create fragment for createPropertyContainer
Statement statement = this.createPropertyContainerFragment(fieldName, fieldType, propertyType, isTechnical,
additionalArguments);
/** DYNAMIC PROPERTIES */
// Fragment for Method 'getProperties'
MessageSend addProperty = this.createGetPropertyStatement(fieldName, fieldType, isTransient, propertyType);
// Fragment for Method 'setProperty'
IfStatement ifStatement = this.createSetPropertyStatement(fieldName, fieldType, propertyType, orderStrategy);
if (this.setPropertyStatementRoot == null) {
this.setPropertyStatementRoot = ifStatement;
} else {
IfStatement previousIf = this.setPropertyStatementRoot;
while (previousIf.elseStatement != null) {
previousIf = (IfStatement) previousIf.elseStatement;
}
previousIf.elseStatement = ifStatement;
}
this.getPropertyStatements.add(addProperty);
this.propertyTypes.add(propertyType);
this.propertyContainerStatements.add(statement);
}
/**
* Create an if statement for a single property in the setProperty() method.
*
* @param fieldName
* the field name
* @param fieldType
* the field type
* @param propertyType
* the property type
* @param orderStrategy
* the order strategy type
*
* @return the if statement
*
* @throws JavaModelException
*/
private IfStatement createSetPropertyStatement(String fieldName, String fieldType, PropertyType propertyType,
OrderStrategyType orderStrategy) throws JavaModelException {
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
// Common
SingleNameReference property = producer.createSingleNameReference(VARIABLE_PROPERTY);
TypeReference type = producer.createTypeReference(fieldType, false);
// Call 'property.getInstance()'
MessageSend getInstance = producer.createMessageSend(JavaConstants.SINGLETON_GETTER, property, null);
// TYPE or List<TYPE>?
TypeReference propertyTypeRef;
if (propertyType == PropertyType.COLLECTION) {
String collectionType = null;
switch (orderStrategy) {
case ORDERED:
collectionType = this.collectionImplementation.getList();
break;
case UNORDERED:
collectionType = this.collectionImplementation.getSet();
break;
case MAPPED:
collectionType = this.collectionImplementation.getMap();
break;
}
propertyTypeRef = producer.createParameterizedTypeReference(collectionType, false, Arrays.asList(type));
} else {
propertyTypeRef = type;
}
CastExpression cast = producer.createCastExpression(getInstance, propertyTypeRef);
Expression assignment;
// Alternative via field!
if (propertyType == PropertyType.COLLECTION) {
FieldReference field = producer.createFieldThisReference(fieldName);
assignment = producer.createAssignment(field, cast);
// Alternative via setter!
} else {
ThisReference thisReference = producer.createThisReference();
assignment = producer.createMessageSend(NabuccoTransformationUtility.toSetter(fieldName), thisReference,
Arrays.asList(cast));
}
ReturnStatement returnStatement = producer.createReturnStatement(producer.createLiteral(null,
LiteralType.TRUE_LITERAL));
// property.getName().equals(STATIC_PROPERTY_NAME_FIELD)
MessageSend getName = producer.createMessageSend(METHOD_GET_NAME, property, null);
MessageSend equals = producer.createMessageSend(JavaConstants.EQUALS, getName,
Arrays.asList(producer.createSingleNameReference(fieldName.toUpperCase())));
// property.getType() == XYZ.class
MessageSend getType = producer.createMessageSend(METHOD_GET_TYPE, property, null);
ClassLiteralAccess typeClass = producer.createClassLiteralAccess(fieldType);
BinaryExpression sameClass = producer.createBinaryExpression(BinaryExpressionType.EQUAL_EXPRESSION, getType,
typeClass, EqualExpression.EQUAL_EQUAL);
BinaryExpression outerCondition = producer.createBinaryExpression(BinaryExpressionType.AND_AND_EXPRESSION,
equals, sameClass, EqualExpression.AND_AND);
return producer.createIfStatement(outerCondition, producer.createBlock(assignment, returnStatement));
}
/**
* Create a property allocation for the getProperties() method.
*
* @param fieldName
* the field name
* @param fieldType
* the field type
* @param isTransient
* whether the field is transient or not
* @param propertyType
* the property type
*
* @return the statement for the <code>getProperties()</code> method
*
* @throws JavaModelException
* when a part of the java statement cannot be created
*/
private MessageSend createGetPropertyStatement(String fieldName, String fieldType, boolean isTransient,
PropertyType propertyType) throws JavaModelException {
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
SingleNameReference staticField = producer.createSingleNameReference(fieldName.toUpperCase());
SingleNameReference typeNameReference = producer.createSingleNameReference(this.currentTypeName);
MessageSend getPropertyDescriptorCall = producer.createMessageSend(METHOD_GET_PROPERTY_DESCRIPTOR,
typeNameReference, Arrays.asList(new Expression[] { staticField }));
List<Expression> arguments = new ArrayList<Expression>();
arguments.add(getPropertyDescriptorCall);
if (propertyType == PropertyType.COLLECTION || propertyType == PropertyType.BASETYPE) {
// Alternative via Field
arguments.add(producer.createFieldThisReference(fieldName));
} else {
// Alternative via Getter
String getterName = NabuccoTransformationUtility.toGetter(fieldName);
arguments.add(producer.createMessageSend(getterName, producer.createThisReference(), null));
}
Expression refId = this.createRefId(fieldName, fieldType, isTransient, propertyType);
if (refId != null) {
arguments.add(refId);
}
MessageSend containerInit = producer.createMessageSend(METHOD_CREATE_PROPERTY, producer.createSuperReference(),
arguments);
return producer.createMessageSend(METHOD_ADD, this.propertyList, Arrays.asList(containerInit));
}
/**
* Create the reference ID literal for the given relation.
*
* @param name
* the field name
* @param type
* the field type
* @param isTransient
* whether the property is transient or not
* @param propertyType
* the property type
*
* @return the refId field or a null literal if it is no component relation
*
* @throws JavaModelException
*/
private Expression createRefId(String name, String type, boolean isTransient, PropertyType propertyType)
throws JavaModelException {
// Messages do not have a propertyList and must not have ref IDs.
if (this.parentProperties == null) {
return null;
}
if (isTransient || propertyType != PropertyType.DATATYPE) {
return JavaAstModelProducer.getInstance().createLiteral(null, LiteralType.NULL_LITERAL);
}
String importString = NabuccoCompilerSupport.resolveImport(type, this.imports);
if (NabuccoCompilerSupport.isOtherComponent(this.qualifiedName, importString)) {
return JavaAstModelProducer.getInstance().createFieldThisReference(
name + NabuccoTransformationConstants.REF_ID);
}
return JavaAstModelProducer.getInstance().createLiteral(null, LiteralType.NULL_LITERAL);
}
/**
* Adjust the getProperties() method.
*
* @param fieldList
* list of field names
* @param type
* the type to modify
*
* @throws JavaModelException
* when an unexpected error during compilation unit modification occurs
*/
public void finish(JavaCompilationUnit unit) throws JavaModelException {
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
this.createCreatePropertyContainer(unit);
if (this.getPropertyStatements.isEmpty()) {
JavaAstType typeFactory = javaFactory.getJavaAstType();
FieldDeclaration constraints = typeFactory.getField(unit.getType(), FIELD_CONSTRAINTS);
typeFactory.removeField(unit.getType(), constraints);
MethodDeclaration setProperty = (MethodDeclaration) javaFactory.getJavaAstType().getMethod(unit.getType(),
NabuccoToJavaReflectionFacade.SET_PROPERTY);
typeFactory.removeMethod(unit.getType(), setProperty);
return;
}
this.createGetProperties(unit);
this.createSetProperty(unit);
this.createImports(unit);
if (this.propertyTypes.contains(PropertyType.COLLECTION)) {
// add suppress warnings to setPropertiy()
MethodDeclaration method = (MethodDeclaration) javaFactory.getJavaAstType().getMethod(unit.getType(),
SIGNATURE_SETPROPERTY);
Annotation suppressWarnings = JavaAstModelProducer.getInstance().createAnnotation(
JavaConstants.ANNOTATION_SUPPRESS_WARNINGS, "unchecked");
javaFactory.getJavaAstMethod().addAnnotation(method, suppressWarnings);
}
this.createPropertyNameFields(unit.getType());
}
/**
* Create the getProperties() method.
*
* @param unit
* the java compilation unit
*
* @throws JavaModelException
* when an unexpected error during compilation unit modification occurs
*/
private void createGetProperties(JavaCompilationUnit unit) throws JavaModelException {
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
MethodDeclaration getProperties = (MethodDeclaration) javaFactory.getJavaAstType().getMethod(unit.getType(),
NabuccoToJavaReflectionFacade.GET_PROPERTIES);
List<Statement> statements = new ArrayList<Statement>();
statements.add(getProperties.statements[0]);
statements.addAll(this.getPropertyStatements);
statements.add(getProperties.statements[1]);
getProperties.statements = statements.toArray(new Statement[statements.size()]);
}
/**
* Create the getProperties() method.
*
* @param unit
* the java compilation unit
*
* @throws JavaModelException
* when an unexpected error during compilation unit modification occurs
*/
private void createSetProperty(JavaCompilationUnit unit) throws JavaModelException {
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
MethodDeclaration setProperty = (MethodDeclaration) javaFactory.getJavaAstType().getMethod(unit.getType(),
NabuccoToJavaReflectionFacade.SET_PROPERTY);
if (this.setPropertyStatementRoot != null) {
List<Statement> statements = new ArrayList<Statement>();
statements.add(setProperty.statements[0]);
statements.add(this.setPropertyStatementRoot);
statements.add(setProperty.statements[1]);
setProperty.statements = statements.toArray(new Statement[statements.size()]);
}
}
/**
* Add the missing imports.
*
* @param unit
* the java compilation unit
*
* @throws JavaModelException
*/
private void createImports(JavaCompilationUnit unit) throws JavaModelException {
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
if (this.propertyTypes.contains(PropertyType.COLLECTION)) {
for (String importName : PropertyType.COLLECTION.getImports()) {
ImportReference importRef = producer.createImportReference(importName);
javaFactory.getJavaAstUnit().addImport(unit.getUnitDeclaration(), importRef);
}
}
if (this.propertyTypes.contains(PropertyType.DATATYPE)) {
for (String importName : PropertyType.DATATYPE.getImports()) {
ImportReference importRef = producer.createImportReference(importName);
javaFactory.getJavaAstUnit().addImport(unit.getUnitDeclaration(), importRef);
}
}
// added dynamically since only needed when we have actual properties
javaFactory.getJavaAstUnit().addImport(unit.getUnitDeclaration(),
producer.createImportReference(DESCRIPTOR_SUPPORT));
}
/**
* Modifies the template 'createPropertyContainer' method by <li>changing the parent class
* literal or removing it if there is no usable parent</li> <li>add all put statements
* previously created by the
* {@link NabuccoToJavaPropertiesVisitor#createPropertyContainerFragment(String, String, PropertyType, List)}
* method</li>
*
* @param unit
* resulting JavaUnit.
*
* @throws JavaModelException
* if model creation/modifications fail.
*/
private void createCreatePropertyContainer(JavaCompilationUnit unit) throws JavaModelException {
AbstractMethodDeclaration method = JavaAstElementFactory.getInstance().getJavaAstType()
.getMethod(unit.getType(), SIGNATURE_CREATEPROPERTYCONTAINER);
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
final TypeReference parentType = producer.createTypeReference(this.extention, false);
// Replace parent class literal
if (this.extention != null) {
ASTVisitor vistior = new ASTVisitor() {
@Override
public boolean visit(ClassLiteralAccess classLiteral, BlockScope scope) {
classLiteral.type = parentType;
return super.visit(classLiteral, scope);
}
};
for (Statement statement : method.statements) {
statement.traverse(vistior, null);
}
} else {
// remove message send for parent call
method.statements[method.statements.length - 2] = method.statements[method.statements.length - 1];
method.statements = Arrays.copyOf(method.statements, 2);
}
int returnStatementPos = method.statements.length - 1;
method.statements = Arrays.copyOf(method.statements, method.statements.length
+ this.propertyContainerStatements.size());
method.statements[method.statements.length - 1] = method.statements[returnStatementPos];
for (int i = 0; i < this.propertyContainerStatements.size(); i++) {
method.statements[returnStatementPos + i] = this.propertyContainerStatements.get(i);
}
}
/**
* Traverses the previously collected/created {@link FieldDeclaration}'s for each property there
* should be a public static final String contant for it's name
*
* @param type
* the resulting javatype.
*/
private void createPropertyNameFields(TypeDeclaration type) throws JavaModelException {
for (FieldDeclaration current : this.propertyNameFields) {
JavaAstElementFactory.getInstance().getJavaAstType().addField(type, current);
}
}
/**
* Creates a 'put' statement for the static 'createPropertyContainer' method. Adding one
* property of the given type to the map of NabuccoPropertyDescriptors utilizing the the
* matching PropertyDescriptorSupport create method.
*
* @param fieldName
* name of the property/field.
* @param fieldType
* type name for the property
* @param propertyType
* the type of property (Basetype,Datatype,List ...)
* @param isTechnical
* whether the property is technical or not
* @param additionalArguments
* additional arguments e.g. 'PropertyAssociationType'
*
* @return an statement that add a PropertyDescriptor to a map.
*
* @throws JavaModelException
* when model creation/modification fails.
*/
private Statement createPropertyContainerFragment(String fieldName, String fieldType, PropertyType propertyType,
boolean isTechnical, List<Expression> additionalArguments) throws JavaModelException {
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
JavaAstMethodCall methodFactory = JavaAstElementFactory.getInstance().getJavaAstMethodCall();
SingleNameReference nameReference = producer.createSingleNameReference(fieldName.toUpperCase());
SingleNameReference mapReference = producer.createSingleNameReference(VARIABLE_PROPERTY_MAP);
MessageSend result = producer.createMessageSend(METHOD_PUT, mapReference,
Arrays.asList(new Expression[] { nameReference }));
SingleNameReference propertyDescriptorSupport = producer.createSingleNameReference(CLASS_SUPPORT);
ClassLiteralAccess typeClassLiteralAccess = producer.createClassLiteralAccess(fieldType);
SingleNameReference constraints = producer.createSingleNameReference(FIELD_CONSTRAINTS);
Literal globalIndex = this.resolveIndex();
String stringIndex = String.valueOf(this.getPropertyStatements.size());
Literal localIndex = producer.createLiteral(stringIndex, LiteralType.INT_LITERAL);
Literal technicalFlag;
if (isTechnical) {
technicalFlag = producer.createLiteral(null, LiteralType.TRUE_LITERAL);
} else {
technicalFlag = producer.createLiteral(null, LiteralType.FALSE_LITERAL);
}
ArrayReference arrayReference = new ArrayReference(constraints, localIndex);
switch (propertyType) {
case BASETYPE: {
MessageSend createBasetype = producer.createMessageSend(METHOD_CREATE_BASETYPE, propertyDescriptorSupport,
Collections.<Expression> emptyList());
methodFactory.addArgument(nameReference, createBasetype);
methodFactory.addArgument(typeClassLiteralAccess, createBasetype);
methodFactory.addArgument(globalIndex, createBasetype);
methodFactory.addArgument(arrayReference, createBasetype);
methodFactory.addArgument(technicalFlag, createBasetype);
methodFactory.addArgument(createBasetype, result);
break;
}
case ENUMERATION: {
MessageSend createEnum = producer.createMessageSend(METHOD_CREATE_ENUMERATION, propertyDescriptorSupport,
Collections.<Expression> emptyList());
methodFactory.addArgument(nameReference, createEnum);
methodFactory.addArgument(typeClassLiteralAccess, createEnum);
methodFactory.addArgument(globalIndex, createEnum);
methodFactory.addArgument(arrayReference, createEnum);
methodFactory.addArgument(technicalFlag, createEnum);
methodFactory.addArgument(createEnum, result);
break;
}
case DATATYPE: {
MessageSend createDatatype = producer.createMessageSend(METHOD_CREATE_DATATYPE, propertyDescriptorSupport,
Collections.<Expression> emptyList());
methodFactory.addArgument(nameReference, createDatatype);
methodFactory.addArgument(typeClassLiteralAccess, createDatatype);
methodFactory.addArgument(globalIndex, createDatatype);
methodFactory.addArgument(arrayReference, createDatatype);
methodFactory.addArgument(technicalFlag, createDatatype);
for (Expression expression : additionalArguments) {
methodFactory.addArgument(expression, createDatatype);
}
methodFactory.addArgument(createDatatype, result);
break;
}
case COLLECTION: {
MessageSend createCollection = producer.createMessageSend(METHOD_CREATE_COLLECTION,
propertyDescriptorSupport, Collections.<Expression> emptyList());
methodFactory.addArgument(nameReference, createCollection);
methodFactory.addArgument(typeClassLiteralAccess, createCollection);
methodFactory.addArgument(globalIndex, createCollection);
methodFactory.addArgument(arrayReference, createCollection);
methodFactory.addArgument(technicalFlag, createCollection);
for (Expression expression : additionalArguments) {
methodFactory.addArgument(expression, createCollection);
}
methodFactory.addArgument(createCollection, result);
break;
}
}
return result;
}
/**
* Resolves the current index number (including all parent properties) and creates an integer
* literal for it.
*
* @return the integer literal
*
* @throws JavaModelException
*/
private Literal resolveIndex() throws JavaModelException {
int index = 0;
index += (this.parentProperties != null) ? this.parentProperties.size() : 0;
index += (this.getPropertyStatements != null) ? this.getPropertyStatements.size() : 0;
return JavaAstModelProducer.getInstance().createLiteral(String.valueOf(index), LiteralType.INT_LITERAL);
}
}