/*
* 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.datatype;
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.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.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.nabucco.framework.generator.compiler.transformation.NabuccoTransformationException;
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.java.common.ast.container.JavaAstContainter;
import org.nabucco.framework.generator.compiler.transformation.java.common.ast.container.JavaAstType;
import org.nabucco.framework.generator.compiler.transformation.java.common.basetype.BasetypeFacade;
import org.nabucco.framework.generator.compiler.transformation.java.common.extension.NabuccoToJavaExtensionEvaluator;
import org.nabucco.framework.generator.compiler.transformation.java.constants.ServerConstants;
import org.nabucco.framework.generator.compiler.transformation.util.NabuccoTransformationUtility;
import org.nabucco.framework.generator.compiler.transformation.util.dependency.NabuccoDependencyResolver;
import org.nabucco.framework.generator.compiler.visitor.NabuccoVisitorException;
import org.nabucco.framework.generator.parser.syntaxtree.BasetypeDeclaration;
import org.nabucco.framework.generator.parser.syntaxtree.EnumerationDeclaration;
import org.nabucco.framework.generator.parser.syntaxtree.NabuccoUnit;
import org.nabucco.framework.generator.parser.syntaxtree.NodeToken;
import org.nabucco.framework.mda.logger.MdaLogger;
import org.nabucco.framework.mda.logger.MdaLoggingFactory;
import org.nabucco.framework.mda.model.java.JavaModelException;
import org.nabucco.framework.mda.model.java.ast.JavaAstMethod;
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;
/**
* NabuccoToJavaDatatypeVisitorSupport
*
* @author Nicolas Moser, PRODYNA AG
*/
final public class NabuccoToJavaDatatypeVisitorSupport implements ServerConstants {
private static final String GET_VALUE = "getValue";
private static final String SET_VALUE = "setValue";
private static final String VALUE_OF = "valueOf";
private static final String ENUM_ID_TYPE = "String";
private static final String INIT_METHOD_NAME = "initDefaults";
private static final String SERIAL_VERSION_UID_FIELDNAME = "serialVersionUID";
private static final JavaAstMethodSignature INIT_METHOD = new JavaAstMethodSignature(
INIT_METHOD_NAME, new String[] {});
private static MdaLogger logger = MdaLoggingFactory.getInstance().getLogger(
NabuccoToJavaDatatypeVisitorSupport.class);
private NabuccoToJavaDatatypeVisitorSupport() {
}
/**
* Creates a basetype wrapper getter for a field name and type.
* <p/>
*
* E.g. a reference to a basetype of type NString gets a getter containing the simple type as
* argument <code>String getName()</code>.
*
* @param fieldName
* name of the field
* @param fieldType
* type of the field
* @param basetypeType
* type of the basetype delegate
*
* @return a {@link JavaAstContainter} with the getter method
*
* @throws NabuccoVisitorException
*/
public static JavaAstContainter<MethodDeclaration> createBasetypeWrapperGetter(
String fieldName, String fieldType, String basetypeType) throws NabuccoVisitorException {
try {
String methodName = PREFIX_GETTER
+ NabuccoTransformationUtility.firstToUpper(fieldName);
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
JavaAstMethod methodFactory = JavaAstElementFactory.getInstance().getJavaAstMethod();
TypeReference delegateType = producer.createTypeReference(basetypeType, false);
MethodDeclaration method = producer.createMethodDeclaration(methodName, null, false);
methodFactory.setMethodName(method, methodName);
methodFactory.setReturnType(method, delegateType);
FieldReference fieldReference = producer.createFieldThisReference(fieldName);
// If statement
FieldReference thisReference = producer.createFieldThisReference(String
.valueOf(fieldName));
Literal nullLiteral = producer.createLiteral(null, LiteralType.NULL_LITERAL);
BinaryExpression condition = producer.createBinaryExpression(
BinaryExpressionType.EQUAL_EXPRESSION, thisReference, nullLiteral,
EqualExpression.EQUAL_EQUAL);
Block then = producer.createBlock(producer.createReturnStatement(nullLiteral));
IfStatement ifStatement = producer.createIfStatement(condition, then, null);
MessageSend getValue = producer.createMessageSend(GET_VALUE, fieldReference, null);
ReturnStatement returnStatement = producer.createReturnStatement(getValue);
method.statements = new Statement[] { ifStatement, returnStatement };
return new JavaAstContainter<MethodDeclaration>(method, JavaAstType.METHOD);
} catch (JavaModelException jme) {
logger.error(jme, "Error during Java AST field modification.");
throw new NabuccoVisitorException("Error during Java AST field modification.", jme);
}
}
/**
* Creates a basetype wrapper setter for a field name and type.
* <p/>
*
* E.g. a reference to a basetype of type NString gets a setter containing the simple type as
* argument <code>void setName(String name)</code>.
*
* @param fieldName
* name of the field
* @param fieldType
* type of the field
* @param basetypeType
* type of the basetype delegate
*
* @return a {@link JavaAstContainter} with the setter method
*
* @throws NabuccoVisitorException
*/
public static JavaAstContainter<MethodDeclaration> createBasetypeWrapperSetter(
String fieldName, String fieldType, String basetypeType) throws NabuccoVisitorException {
try {
String methodName = PREFIX_SETTER
+ NabuccoTransformationUtility.firstToUpper(fieldName);
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
// Common
TypeReference type = producer.createTypeReference(fieldType, false);
TypeReference delegateType = producer.createTypeReference(basetypeType, false);
Literal nullLiteral = producer.createLiteral(null, LiteralType.NULL_LITERAL);
FieldReference fieldReference = producer.createFieldThisReference(fieldName);
SingleNameReference nameReference = producer.createSingleNameReference(fieldName);
// Method Signature
MethodDeclaration method = producer.createMethodDeclaration(methodName, null, false);
javaFactory.getJavaAstMethod().setMethodName(method, methodName);
Argument argument = producer.createArgument(fieldName, delegateType);
javaFactory.getJavaAstMethod().addArgument(method, argument);
// 1. Inner If
BinaryExpression innerCondition = producer.createBinaryExpression(
BinaryExpressionType.EQUAL_EXPRESSION, nameReference, nullLiteral,
EqualExpression.EQUAL_EQUAL);
Block innerThen = producer.createBlock(producer.createReturnStatement(null));
IfStatement innerIf = producer.createIfStatement(innerCondition, innerThen);
// 2. Outer If
FieldReference thisReference = producer.createFieldThisReference(String
.valueOf(fieldName));
BinaryExpression outerCondition = producer.createBinaryExpression(
BinaryExpressionType.EQUAL_EXPRESSION, thisReference, nullLiteral,
EqualExpression.EQUAL_EQUAL);
AllocationExpression constructor = producer.createAllocationExpression(type, null);
Assignment assignment = producer.createAssignment(fieldReference, constructor);
Block outerThen = producer.createBlock(new Statement[] { innerIf, assignment });
IfStatement outerIf = producer.createIfStatement(outerCondition, outerThen, null);
// Setter
MessageSend setValue = producer.createMessageSend(SET_VALUE, fieldReference,
Arrays.asList(nameReference));
method.statements = new Statement[] { outerIf, setValue };
return new JavaAstContainter<MethodDeclaration>(method, JavaAstType.METHOD);
} catch (JavaModelException jme) {
logger.error(jme, "Error during Java AST field modification.");
throw new NabuccoVisitorException("Error during Java AST field modification.", jme);
}
}
/**
* Creates an enumeration ID setter for a field name and type.
* <p/>
*
* E.g. a reference to an enum gets a setter containing the enum itself as argument
* <code>setType(Type type)</code> and a setter containing the enum ID as argument
* <code>setType(String type)</code>.
*
* @param enumName
* name of the field
* @param enumType
* type of the field
*
* @return a {@link JavaAstContainter} with the setter method
*
* @throws NabuccoVisitorException
*/
public static JavaAstContainter<MethodDeclaration> createEnumIdSetter(String enumName,
String enumType) throws NabuccoVisitorException {
try {
String methodName = PREFIX_SETTER + NabuccoTransformationUtility.firstToUpper(enumName);
JavaAstModelProducer modelProducer = JavaAstModelProducer.getInstance();
TypeReference type = modelProducer.createTypeReference(enumType, false);
TypeReference delegateType = modelProducer.createTypeReference(ENUM_ID_TYPE, false);
MethodDeclaration method = modelProducer.createMethodDeclaration(methodName.toString(),
null, false);
JavaAstElementFactory.getInstance().getJavaAstMethod()
.setMethodName(method, methodName);
Argument argument = modelProducer.createArgument(enumName, delegateType);
JavaAstElementFactory.getInstance().getJavaAstMethod().addArgument(method, argument);
FieldReference fieldReference = modelProducer.createFieldThisReference(enumName);
SingleNameReference nameReference = modelProducer.createSingleNameReference(enumName);
// If statement
Literal nullLiteral = modelProducer.createLiteral(null, LiteralType.NULL_LITERAL);
BinaryExpression condition = modelProducer.createBinaryExpression(
BinaryExpressionType.EQUAL_EXPRESSION, nameReference, nullLiteral,
EqualExpression.EQUAL_EQUAL);
Block thenStatement = modelProducer.createBlock(new Statement[] { modelProducer
.createAssignment(fieldReference, nullLiteral) });
MessageSend valueOf = modelProducer.createMessageSend(VALUE_OF, type,
Arrays.asList(nameReference));
Block elseStatement = modelProducer.createBlock(modelProducer.createAssignment(
fieldReference, valueOf));
IfStatement ifStatement = modelProducer.createIfStatement(condition, thenStatement,
elseStatement);
method.statements = new Statement[] { ifStatement };
return new JavaAstContainter<MethodDeclaration>(method, JavaAstType.METHOD);
} catch (JavaModelException jme) {
logger.error(jme, "Error during Java AST field modification.");
throw new NabuccoVisitorException("Error during Java AST field modification.", jme);
}
}
/**
* Resolves a basetype to its original java delegate.
*
* @param rootDirectory
* the project root
* @param importString
* the import string of the basetype
* @param outDirectory
* the project out directory
*
* @return the basetype delegate
*/
public static String resolveBasetypeDelegate(String rootDirectory, String pkg,
String importString, String outDirectory) {
try {
NabuccoUnit unit = NabuccoDependencyResolver.getInstance()
.resolveDependency(rootDirectory, pkg, importString, outDirectory).getModel()
.getUnit();
String extension = NabuccoToJavaExtensionEvaluator.getInstance().getExtension(unit);
return BasetypeFacade.mapToPrimitiveType(extension);
} catch (NabuccoTransformationException e) {
throw new NabuccoVisitorException(e);
}
}
/**
* Adds the expressions to the given datatype {@link TypeDeclaration}'s init method.
*
* @param type
* the datatype type declaration
* @param expressionList
* the list of expressions
*/
public static void handleInitDefaults(TypeDeclaration type, List<Assignment> expressionList) {
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
try {
for (Assignment current : expressionList) {
if (current.lhs instanceof SingleNameReference) {
SingleNameReference name = (SingleNameReference) current.lhs;
String defaultFieldName = new String(name.token).toUpperCase().concat(
"_DEFAULT");
FieldDeclaration createFieldDeclaration = producer.createFieldDeclaration(
defaultFieldName, ClassFileConstants.AccFinal
| ClassFileConstants.AccStatic
| ClassFileConstants.AccPrivate);
TypeReference fieldType = null;
if (current.expression instanceof QualifiedNameReference) {
QualifiedNameReference qnr = (QualifiedNameReference) current.expression;
fieldType = producer.createTypeReference(new String(qnr.tokens[0]), false);
} else if (current.expression instanceof AllocationExpression) {
AllocationExpression ae = (AllocationExpression) current.expression;
fieldType = ae.type;
}
javaFactory.getJavaAstField().setFieldType(createFieldDeclaration, fieldType);
javaFactory.getJavaAstField().setFieldInitializer(createFieldDeclaration,
current.expression);
// placement of the static field... should be below the serialVersionUID
FieldDeclaration propConst = javaFactory.getJavaAstType().getField(type,
SERIAL_VERSION_UID_FIELDNAME);
type.fields = Arrays.copyOf(type.fields, type.fields.length + 1);
int pos = -1;
for (int i = 0; i < type.fields.length; i++) {
if (type.fields[i] == propConst) {
pos = i;
}
}
FieldDeclaration[] tmp = Arrays.copyOfRange(type.fields, pos + 1,
type.fields.length - 1);
type.fields[pos + 1] = createFieldDeclaration;
for (int i = 0; i < tmp.length; i++) {
type.fields[pos + 2 + i] = tmp[i];
}
current.expression = producer.createSingleNameReference(defaultFieldName);
}
}
MethodDeclaration setDefaultMethod = (MethodDeclaration) javaFactory.getJavaAstType()
.getMethod(type, INIT_METHOD);
Statement[] methodBody = new Statement[expressionList.size()];
for (int i = 0; i < expressionList.size(); i++) {
methodBody[i] = expressionList.get(i);
}
setDefaultMethod.statements = methodBody;
} catch (JavaModelException e) {
throw new NabuccoVisitorException(
"Exception during appending of initDefaults() method content", e);
}
}
/**
* Creates an enumeration default initialization expression.
*
* @param nabuccoEnum
* the appropriate enum declaration
*
* @return the expression
*/
public static Assignment createEnumInitializer(EnumerationDeclaration nabuccoEnum) {
String name = nabuccoEnum.nodeToken2.tokenImage;
String type = ((NodeToken) nabuccoEnum.nodeChoice1.choice).tokenImage;
// contribute to default value setting
try {
NabuccoAnnotation defaultValue = NabuccoAnnotationMapper.getInstance().mapToAnnotation(
nabuccoEnum.annotationDeclaration, NabuccoAnnotationType.DEFAULT);
if (defaultValue != null) {
JavaAstModelProducer jamp = JavaAstModelProducer.getInstance();
SingleNameReference fieldReference = jamp.createSingleNameReference(name);
QualifiedNameReference literalToSet = jamp.createQualifiedNameReference(type,
defaultValue.getValue());
return jamp.createAssignment(fieldReference, literalToSet);
}
return null;
} catch (JavaModelException e) {
throw new NabuccoVisitorException(
"Exception creating element while generating content for initDefaults() of enum declaration "
+ name, e);
}
}
/**
* Creates an basetype default initialization expression.
*
* @param nabuccoBasetype
* the appropriate basetype declaration
* @param type
* the internal basetype type
*
* @return the expression
*/
public static Assignment createBasetypeInitializer(BasetypeDeclaration nabuccoBasetype,
String type) {
String name = nabuccoBasetype.nodeToken3.tokenImage;
String typeName = nabuccoBasetype.nodeToken1.tokenImage;
// contribute to default value setting
try {
NabuccoAnnotation defaultValue = NabuccoAnnotationMapper.getInstance().mapToAnnotation(
nabuccoBasetype.annotationDeclaration, NabuccoAnnotationType.DEFAULT);
if (defaultValue != null) {
JavaAstModelProducer jamp = JavaAstModelProducer.getInstance();
SingleNameReference fieldReference = jamp.createSingleNameReference(name);
Literal defaultLiteral = jamp.createLiteral(defaultValue.getValue(),
LiteralType.mapFromString(type));
TypeReference createTypeReference = jamp.createTypeReference(typeName, false);
AllocationExpression createAllocationExpression = jamp.createAllocationExpression(
createTypeReference, Arrays.asList(new Expression[] { defaultLiteral }));
return jamp.createAssignment(fieldReference, createAllocationExpression);
}
return null;
} catch (JavaModelException e) {
throw new NabuccoVisitorException(
"Exception creating element while generating content for initDefaults() for basetype declaration "
+ name, e);
}
}
/**
* Checks whether a basetype is redefined or not.
*
* @param nabuccoBasetype
* the basetype to check
*
* @return <b>true</b> if the basetype is redefined, <b>false</b> if not
*/
public static boolean isRedefinition(BasetypeDeclaration nabuccoBasetype) {
if (NabuccoAnnotationMapper.getInstance().mapToAnnotation(
nabuccoBasetype.annotationDeclaration, NabuccoAnnotationType.REDEFINED) == null) {
return false;
}
return true;
}
/**
* Checks whether an enumeration is redefined or not.
*
* @param nabuccoEnum
* the enum to check
*
* @return <b>true</b> if the enum is redefined, <b>false</b> if not
*/
public static boolean isRedefinition(EnumerationDeclaration nabuccoEnum) {
if (NabuccoAnnotationMapper.getInstance().mapToAnnotation(
nabuccoEnum.annotationDeclaration, NabuccoAnnotationType.REDEFINED) == null) {
return false;
}
return true;
}
/**
* Adds component comprehensive functionality to the datatype setter.
* <p/>
* Sets the refId implicitly by calling the datatype setter.
*
* @param container
* container for the setter container
*
* @throws JavaModelException
*/
public static void prepareSetterForRefId(JavaAstContainter<MethodDeclaration> container)
throws JavaModelException {
if (container == null || container.getAstNode() == null) {
throw new IllegalArgumentException("Cannot prepare setter for ref ID [null].");
}
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
JavaAstMethod methodFactory = javaFactory.getJavaAstMethod();
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
MethodDeclaration methodDeclaration = container.getAstNode();
String name = javaFactory.getJavaAstArgument().getName(methodDeclaration.arguments[0]);
SingleNameReference nameReference = producer.createSingleNameReference(name);
String methodName = methodFactory.getMethodName(methodDeclaration);
Literal nullLiteral = producer.createLiteral(null, LiteralType.NULL_LITERAL);
BinaryExpression condition = producer.createBinaryExpression(
BinaryExpressionType.EQUAL_EXPRESSION, nameReference, nullLiteral,
BinaryExpression.NOT_EQUAL);
ThisReference thisReference = producer.createThisReference();
MessageSend getId = producer.createMessageSend("getId", nameReference, null);
MessageSend thenStatement = producer.createMessageSend(methodName + REF_ID, thisReference,
Arrays.asList(getId));
MessageSend elseStatement = producer.createMessageSend(methodName + REF_ID, thisReference,
Arrays.asList(nullLiteral));
IfStatement ifStatement = producer.createIfStatement(condition,
producer.createBlock(thenStatement), producer.createBlock(elseStatement));
methodFactory.addStatement(methodDeclaration, ifStatement);
}
}