/* * 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.clone; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.BinaryExpression; import org.eclipse.jdt.internal.compiler.ast.Block; import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.IfStatement; import org.eclipse.jdt.internal.compiler.ast.Literal; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; 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.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.nabucco.framework.generator.compiler.NabuccoCompilerSupport; 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.java.constants.CollectionConstants; import org.nabucco.framework.generator.compiler.transformation.java.visitor.NabuccoToJavaVisitorContext; import org.nabucco.framework.generator.compiler.transformation.java.visitor.NabuccoToJavaVisitorSupport; import org.nabucco.framework.generator.compiler.transformation.util.NabuccoTransformationUtility; import org.nabucco.framework.generator.compiler.visitor.NabuccoVisitorException; 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.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.NodeToken; import org.nabucco.framework.mda.model.MdaModel; import org.nabucco.framework.mda.model.java.JavaModel; import org.nabucco.framework.mda.model.java.JavaModelException; 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; /** * NabuccoToJavaCloneVisitor * * @author Nicolas Moser, PRODYNA AG */ public class NabuccoToJavaCloneVisitor extends NabuccoToJavaVisitorSupport implements CollectionConstants { private static final String CLONE = "clone"; private static final String CLONE_OBJECT_METHOD = CLONE + "Object"; private static final String CLONE_COLLECTION_METHOD = CLONE + "Collection"; private static final JavaAstMethodSignature CLONE_OBJECT = new JavaAstMethodSignature(CLONE_OBJECT_METHOD); private static final JavaAstMethodSignature CLONE_OBJECT_PARAM = new JavaAstMethodSignature(CLONE_OBJECT_METHOD, "Template"); /** Collected method statements for cloneObject() */ private List<Statement> cloneStatements = new ArrayList<Statement>(); /** Type containing the cloneObject() method */ private TypeDeclaration javaType; private JavaAstModelProducer producer = JavaAstModelProducer.getInstance(); /** * Creates a new {@link NabuccoToJavaCloneVisitor} instance. * * @param javaType * the java javaType * @param visitorContext * the visitor context */ public NabuccoToJavaCloneVisitor(TypeDeclaration type, NabuccoToJavaVisitorContext visitorContext) { super(visitorContext); this.javaType = type; } @Override public void visit(DatatypeStatement nabuccoDatatype, MdaModel<JavaModel> target) { // Visit sub-nodes first! super.visit(nabuccoDatatype, target); String name = nabuccoDatatype.nodeToken2.tokenImage; boolean isAbstract = nabuccoDatatype.nodeOptional.present(); this.modifyCloneObjectMethod(name, isAbstract); } @Override public void visit(MessageStatement nabuccoMessage, MdaModel<JavaModel> target) { // Visit sub-nodes first! super.visit(nabuccoMessage, target); String name = nabuccoMessage.nodeToken2.tokenImage; this.modifyCloneObjectMethod(name, false); } /** * Modifies the cloneObject() method of datatypes and messages. * * @param name * name of the statement * @param isAbstract * abstract types cannot be cloned */ private void modifyCloneObjectMethod(String name, boolean isAbstract) { JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance(); try { MethodDeclaration cloneObject = (MethodDeclaration) javaFactory.getJavaAstType().getMethod(javaType, CLONE_OBJECT); MethodDeclaration cloneObjectParam = (MethodDeclaration) javaFactory.getJavaAstType().getMethod(javaType, CLONE_OBJECT_PARAM); TypeReference type = producer.createTypeReference(name, false); // Abstract classes cannot be cloned directly! if (isAbstract) { javaFactory.getJavaAstMethod().addModifier(cloneObject, ClassFileConstants.AccAbstract); cloneObject.statements = null; } else { LocalDeclaration local = (LocalDeclaration) cloneObject.statements[0]; AllocationExpression allocation = (AllocationExpression) local.initialization; local.type = type; allocation.type = type; } javaFactory.getJavaAstMethod().setReturnType(cloneObject, type); Argument argument = cloneObjectParam.arguments[0]; javaFactory.getJavaAstArgument().setType(argument, type); for (Statement statement : this.cloneStatements) { javaFactory.getJavaAstMethod().addStatement(cloneObjectParam, statement); } } catch (JavaModelException e) { throw new NabuccoVisitorException("Cannot find cloneObject() method.", e); } } @Override public void visit(BasetypeDeclaration nabuccoBasetype, MdaModel<JavaModel> argu) { String name = nabuccoBasetype.nodeToken3.tokenImage; NabuccoMultiplicityType multiplicity = NabuccoMultiplicityTypeMapper.getInstance().mapToMultiplicity( nabuccoBasetype.nodeToken2.tokenImage); try { if (!multiplicity.isMultiple()) { if (NabuccoAnnotationMapper.getInstance().hasAnnotation(nabuccoBasetype.annotationDeclaration, NabuccoAnnotationType.PRIMARY, NabuccoAnnotationType.OPTIMISTIC_LOCK)) { this.addAssignmentWithoutClone(name); } else { this.addAssignment(name); } } } catch (JavaModelException e) { throw new NabuccoVisitorException("Error creating cloneObject() assignment.", e); } } @Override public void visit(EnumerationDeclaration nabuccoEnum, MdaModel<JavaModel> argu) { String name = nabuccoEnum.nodeToken2.tokenImage; NabuccoMultiplicityType multiplicity = NabuccoMultiplicityTypeMapper.getInstance().mapToMultiplicity( nabuccoEnum.nodeToken1.tokenImage); try { if (!multiplicity.isMultiple()) { this.addAssignmentWithoutClone(name); } } catch (JavaModelException e) { throw new NabuccoVisitorException("Error creating cloneObject() assignment.", e); } } @Override public void visit(DatatypeDeclaration nabuccoDatatype, MdaModel<JavaModel> target) { String name = nabuccoDatatype.nodeToken2.tokenImage; String type = ((NodeToken) nabuccoDatatype.nodeChoice1.choice).tokenImage; NabuccoMultiplicityType multiplicity = NabuccoMultiplicityTypeMapper.getInstance().mapToMultiplicity( nabuccoDatatype.nodeToken1.tokenImage); boolean isTransient = nabuccoDatatype.nodeOptional.present(); try { if (multiplicity.isMultiple()) { this.addListAssignment(name, type); } else { this.addAssignment(name); String importString = super.resolveImport(type); String pkg = super.getVisitorContext().getPackage(); if (!isTransient && NabuccoCompilerSupport.isOtherComponent(pkg, importString)) { this.addRefIdAssignment(name); } } } catch (JavaModelException e) { throw new NabuccoVisitorException("Error creating cloneObject() assignment.", e); } } /** * Adds an assignment to the collected list. * * @param fieldName * the field to create the assignment for. * * @throws JavaModelException */ private void addAssignment(String fieldName) throws JavaModelException { String getterName = PREFIX_GETTER + NabuccoTransformationUtility.firstToUpper(fieldName); String setterName = PREFIX_SETTER + NabuccoTransformationUtility.firstToUpper(fieldName); ThisReference thisReference = producer.createThisReference(); Literal nullLiteral = producer.createLiteral(null, LiteralType.NULL_LITERAL); SingleNameReference clone = producer.createSingleNameReference(CLONE); MessageSend getter = producer.createMessageSend(getterName, thisReference, null); MessageSend cloneObject = producer.createMessageSend(CLONE_OBJECT_METHOD, getter, null); MessageSend setter = producer.createMessageSend(setterName, clone, Arrays.asList(cloneObject)); BinaryExpression condition = producer.createBinaryExpression(BinaryExpressionType.EQUAL_EXPRESSION, getter, nullLiteral, BinaryExpression.NOT_EQUAL); Block then = producer.createBlock(setter); IfStatement ifStatement = producer.createIfStatement(condition, then); this.cloneStatements.add(ifStatement); } /** * Adds an assignment without cloning the elment (for enumerations and primitive types). * * @param fieldName * name of the field * * @throws JavaModelException */ private void addAssignmentWithoutClone(String fieldName) throws JavaModelException { String getterName = PREFIX_GETTER + NabuccoTransformationUtility.firstToUpper(fieldName); String setterName = PREFIX_SETTER + NabuccoTransformationUtility.firstToUpper(fieldName); ThisReference thisReference = producer.createThisReference(); SingleNameReference clone = producer.createSingleNameReference(CLONE); MessageSend getter = producer.createMessageSend(getterName, thisReference, null); MessageSend setter = producer.createMessageSend(setterName, clone, Arrays.asList(getter)); cloneStatements.add(setter); } /** * Adds a list assignment to the collected list. * * @param fieldName * the field to create the assignment for. * @param type * the collection type (parameter type) * @param orderStrategy * the collection order strategy (COLLECTION, SET, MAP) * * @throws JavaModelException */ private void addListAssignment(String fieldName, String type) throws JavaModelException { // Common FieldReference field = producer.createFieldThisReference(fieldName); QualifiedNameReference clone = producer.createQualifiedNameReference(CLONE, fieldName); Literal nullLiteral = producer.createLiteral(null, LiteralType.NULL_LITERAL); BinaryExpression condition = producer.createBinaryExpression(BinaryExpressionType.EQUAL_EXPRESSION, field, nullLiteral, BinaryExpression.NOT_EQUAL); MessageSend right = producer.createMessageSend(CLONE_COLLECTION_METHOD, field, null); Assignment assignment = producer.createAssignment(clone, right); // Then Block then = producer.createBlock(assignment); IfStatement listAssignment = producer.createIfStatement(condition, then); this.cloneStatements.add(listAssignment); } /** * Adds an assignment to the inter-component reference id. * * @param fieldName * the field having defining an inter-component relation * * @throws JavaModelException */ private void addRefIdAssignment(String fieldName) throws JavaModelException { fieldName = fieldName + REF_ID; String getterName = PREFIX_GETTER + NabuccoTransformationUtility.firstToUpper(fieldName); String setterName = PREFIX_SETTER + NabuccoTransformationUtility.firstToUpper(fieldName); ThisReference thisReference = producer.createThisReference(); Literal literal = producer.createLiteral(null, LiteralType.NULL_LITERAL); SingleNameReference clone = producer.createSingleNameReference(CLONE); MessageSend getter = producer.createMessageSend(getterName, thisReference, null); MessageSend setter = producer.createMessageSend(setterName, clone, Arrays.asList(getter)); BinaryExpression condition = producer.createBinaryExpression(BinaryExpressionType.EQUAL_EXPRESSION, getter, literal, BinaryExpression.NOT_EQUAL); Block then = producer.createBlock(setter); IfStatement ifStatement = producer.createIfStatement(condition, then); this.cloneStatements.add(ifStatement); } }