/*
* 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.application.connector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.CaseStatement;
import org.eclipse.jdt.internal.compiler.ast.IfStatement;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
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.ParameterizedSingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.nabucco.framework.generator.compiler.constants.NabuccoJavaTemplateConstants;
import org.nabucco.framework.generator.compiler.transformation.java.application.connector.util.DatatypeCollector;
import org.nabucco.framework.generator.compiler.transformation.java.application.connector.util.ServiceLinkResolver;
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.syntaxtree.ApplicationStatement;
import org.nabucco.framework.generator.parser.syntaxtree.ConnectorStatement;
import org.nabucco.framework.mda.model.MdaModel;
import org.nabucco.framework.mda.model.java.JavaCompilationUnit;
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.method.JavaAstMethodSignature;
import org.nabucco.framework.mda.model.java.ast.produce.JavaAstModelProducer;
import org.nabucco.framework.mda.template.java.JavaTemplateException;
/**
* NabuccoToJavaServiceTargetServiceLinkVisitor
*
* @author Nicolas Moser, PRODYNA AG
*/
class NabuccoToJavaDatatypeTargetServiceLinkVisitor extends NabuccoToJavaVisitorSupport {
private JavaCompilationUnit unit;
private ApplicationStatement application;
private static final String PRE = "pre";
private static final String POST = "post";
private static final String INVOKE = "invoke";
private static final JavaAstMethodSignature SIGNATURE_INTERNAL_CONNECT = new JavaAstMethodSignature(
"internalConnect");
private static final JavaAstMethodSignature SIGNATURE_INTERNAL_MAINTAIN = new JavaAstMethodSignature(
"internalMaintain", "ComponentRelation");
private static final JavaAstMethodSignature SIGNATURE_INTERNAL_RESOLVE = new JavaAstMethodSignature(
"internalResolve", "ComponentRelation");
private static final JavaAstMethodSignature SIGNATURE_PRE_OPERATION = new JavaAstMethodSignature(
"preServiceOperation", "ServiceMessageTemplate");
private static final JavaAstMethodSignature SIGNATURE_POST_OPERATION = new JavaAstMethodSignature(
"postServiceOperation", "ServiceMessageTemplate");
private static final JavaAstMethodSignature SIGNATURE_INVOKE = new JavaAstMethodSignature("invoke",
"ServiceMessageTemplate");
/**
* Creates a new {@link NabuccoToJavaDatatypeTargetServiceLinkVisitor} instance.
*
* @param visitorContext
* the visitor context
*/
public NabuccoToJavaDatatypeTargetServiceLinkVisitor(NabuccoToJavaVisitorContext visitorContext,
ApplicationStatement application, JavaCompilationUnit unit) {
super(visitorContext);
this.application = application;
this.unit = unit;
}
@Override
public void visit(ConnectorStatement connector, MdaModel<JavaModel> target) {
try {
DatatypeCollector collector = new DatatypeCollector(super.getVisitorContext(), this.application);
collector.accept(connector);
ServiceLinkResolver resolver;
JavaCompilationUnit template;
for (String targetName : collector.getTargetMap().keySet()) {
template = super.extractAst(NabuccoJavaTemplateConstants.CONNECTOR_CALLBACK_TEMPLATE);
resolver = collector.getMaintainServices().get(targetName);
this.createInternalMaintainStatements(collector, targetName, template);
this.createAbstractMethods(resolver, template);
this.createInvokeMethod(resolver, template);
template = super.extractAst(NabuccoJavaTemplateConstants.CONNECTOR_CALLBACK_TEMPLATE);
resolver = collector.getResolveServices().get(targetName);
this.createInternalResolveStatements(collector, targetName, template);
this.createAbstractMethods(resolver, template);
this.createInvokeMethod(resolver, template);
}
} catch (JavaModelException me) {
throw new NabuccoVisitorException("Cannot generate target service link.", me);
} catch (JavaTemplateException te) {
throw new NabuccoVisitorException("Cannot generate target service link.", te);
}
}
/**
* Modify the 'internalConnect()' method.
*
* @param collector
* the datatype collector
* @param targetName
* the target datatype name
* @param template
* the connector callback template
*
* @throws JavaModelException
*/
private void createInternalMaintainStatements(DatatypeCollector collector, String targetName,
JavaCompilationUnit template) throws JavaModelException {
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
AbstractMethodDeclaration internalMaintain = javaFactory.getJavaAstType().getMethod(this.unit.getType(),
SIGNATURE_INTERNAL_MAINTAIN);
AbstractMethodDeclaration internalConnectTemlate = javaFactory.getJavaAstType().getMethod(template.getType(),
SIGNATURE_INTERNAL_CONNECT);
IfStatement ifStatement = (IfStatement) internalMaintain.statements[0];
Block thenStatement = (Block) ifStatement.thenStatement;
SwitchStatement switchStatement = (SwitchStatement) thenStatement.statements[0];
List<CaseStatement> caseList;
List<Statement> statementList;
if (switchStatement.statements == null) {
caseList = new ArrayList<CaseStatement>();
statementList = new ArrayList<Statement>();
} else {
caseList = new ArrayList<CaseStatement>(Arrays.asList(switchStatement.cases));
statementList = new ArrayList<Statement>(Arrays.asList(switchStatement.statements));
}
CaseStatement caseStatement = producer.createCaseStatement(collector.getSourceName().toUpperCase()
+ CONSTANT_SEPARATOR + targetName.toUpperCase());
ServiceLinkResolver resolver = collector.getMaintainServices().get(targetName);
Statement[] statements = this.createStatements(resolver, internalConnectTemlate);
caseList.add(caseStatement);
statementList.add(caseStatement);
statementList.add(producer.createBlock(statements));
statementList.add(producer.createBreakStatement());
switchStatement.cases = caseList.toArray(new CaseStatement[caseList.size()]);
switchStatement.statements = statementList.toArray(new Statement[statementList.size()]);
}
/**
* Modify the 'internalResolve()' method.
*
* @param collector
* the datatype collector
* @param targetName
* the target datatype name
* @param template
* the connector callback template
*
* @throws JavaModelException
*/
private void createInternalResolveStatements(DatatypeCollector collector, String targetName,
JavaCompilationUnit template) throws JavaModelException {
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
AbstractMethodDeclaration internalResolve = javaFactory.getJavaAstType().getMethod(this.unit.getType(),
SIGNATURE_INTERNAL_RESOLVE);
AbstractMethodDeclaration internalConnectTemlate = javaFactory.getJavaAstType().getMethod(template.getType(),
SIGNATURE_INTERNAL_CONNECT);
IfStatement ifStatement = (IfStatement) internalResolve.statements[0];
Block thenStatement = (Block) ifStatement.thenStatement;
SwitchStatement switchStatement = (SwitchStatement) thenStatement.statements[0];
List<CaseStatement> caseList;
List<Statement> statementList;
if (switchStatement.statements == null) {
caseList = new ArrayList<CaseStatement>();
statementList = new ArrayList<Statement>();
} else {
caseList = new ArrayList<CaseStatement>(Arrays.asList(switchStatement.cases));
statementList = new ArrayList<Statement>(Arrays.asList(switchStatement.statements));
}
CaseStatement caseStatement = producer.createCaseStatement(collector.getSourceName().toUpperCase()
+ CONSTANT_SEPARATOR + targetName.toUpperCase());
ServiceLinkResolver resolver = collector.getResolveServices().get(targetName);
Statement[] statements = this.createStatements(resolver, internalConnectTemlate);
caseList.add(caseStatement);
statementList.add(caseStatement);
statementList.add(producer.createBlock(statements));
statementList.add(producer.createBreakStatement());
switchStatement.cases = caseList.toArray(new CaseStatement[caseList.size()]);
switchStatement.statements = statementList.toArray(new Statement[statementList.size()]);
}
/**
* Create the block statements calling the service operations.
*
* @param resolver
* the service link resolver
* @param template
* the internal connect template
*
* @return the statements
*
* @throws JavaModelException
*/
private Statement[] createStatements(ServiceLinkResolver resolver, AbstractMethodDeclaration template)
throws JavaModelException {
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
String operation = resolver.getServiceOperation();
Statement[] statements = new Statement[4];
if (template.statements[0] instanceof LocalDeclaration) {
LocalDeclaration rqMsg = (LocalDeclaration) template.statements[0];
String typeName = resolver.getRequestMessage();
ImportReference importReference = producer.createImportReference(typeName);
javaFactory.getJavaAstUnit().addImport(this.unit.getUnitDeclaration(), importReference);
typeName = typeName.substring(typeName.lastIndexOf(PKG_SEPARATOR) + 1);
TypeReference type = producer.createTypeReference(typeName, false);
rqMsg.type = type;
rqMsg.initialization = producer.createAllocationExpression(type, null);
statements[0] = rqMsg;
}
if (template.statements[1] instanceof MessageSend) {
MessageSend pre = (MessageSend) template.statements[1];
String name = PRE + NabuccoTransformationUtility.firstToUpper(operation);
javaFactory.getJavaAstMethodCall().setMethodName(name, pre);
statements[1] = pre;
}
if (template.statements[2] instanceof LocalDeclaration) {
LocalDeclaration rsMsg = (LocalDeclaration) template.statements[2];
String typeName = resolver.getResponseMessage();
ImportReference importReference = producer.createImportReference(typeName);
javaFactory.getJavaAstUnit().addImport(this.unit.getUnitDeclaration(), importReference);
typeName = typeName.substring(typeName.lastIndexOf(PKG_SEPARATOR) + 1);
rsMsg.type = producer.createTypeReference(typeName, false);
if (rsMsg.initialization instanceof MessageSend) {
MessageSend invoke = (MessageSend) rsMsg.initialization;
String methodName = INVOKE + NabuccoTransformationUtility.firstToUpper(operation);
javaFactory.getJavaAstMethodCall().setMethodName(methodName, invoke);
}
statements[2] = rsMsg;
}
if (template.statements[3] instanceof MessageSend) {
MessageSend post = (MessageSend) template.statements[3];
String name = POST + NabuccoTransformationUtility.firstToUpper(operation);
javaFactory.getJavaAstMethodCall().setMethodName(name, post);
statements[3] = post;
}
return statements;
}
/**
* Create the abstract pre/post callback methods.
*
* @param resolver
* the service link resolver
* @param template
* the template
*
* @throws JavaModelException
*/
private void createAbstractMethods(ServiceLinkResolver resolver, JavaCompilationUnit template)
throws JavaModelException {
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
String serviceOperation = resolver.getServiceOperation();
String preName = PRE + NabuccoTransformationUtility.firstToUpper(serviceOperation);
String postName = POST + NabuccoTransformationUtility.firstToUpper(serviceOperation);
String rqMsg = resolver.getRequestMessage();
String rsMsg = resolver.getResponseMessage();
MethodDeclaration preOperation = (MethodDeclaration) javaFactory.getJavaAstType().getMethod(template.getType(),
SIGNATURE_PRE_OPERATION);
MethodDeclaration postOperation = (MethodDeclaration) javaFactory.getJavaAstType().getMethod(
template.getType(), SIGNATURE_POST_OPERATION);
this.createAbstractMethod(rqMsg, preName, preOperation);
this.createAbstractMethod(rsMsg, postName, postOperation);
}
/**
* Create the callback method.
*
* @param messageName
* the message name
* @param operationName
* the operation name
* @param operation
* the method declaration
*
* @throws JavaModelException
*/
private void createAbstractMethod(String messageName, String operationName, MethodDeclaration operation)
throws JavaModelException {
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
javaFactory.getJavaAstMethod().setMethodName(operation, operationName);
List<Argument> arguments = javaFactory.getJavaAstMethod().getAllArguments(operation);
if (arguments.size() != 1) {
throw new IllegalStateException("Connector Callback Template is not valid.");
}
Argument argument = arguments.get(0);
messageName = messageName.substring(messageName.lastIndexOf(PKG_SEPARATOR) + 1);
TypeReference type = producer.createTypeReference(messageName, false);
javaFactory.getJavaAstArgument().setType(argument, type);
javaFactory.getJavaAstType().addMethod(this.unit.getType(), operation);
}
/**
* Create the 'invoke()' method for concrete service invocation.
*
* @param resolver
* the service link resolver
* @param template
* the template
*
* @throws JavaModelException
*/
private void createInvokeMethod(ServiceLinkResolver resolver, JavaCompilationUnit template)
throws JavaModelException {
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
MethodDeclaration invoke = (MethodDeclaration) javaFactory.getJavaAstType().getMethod(template.getType(),
SIGNATURE_INVOKE);
this.createInvokeSignature(resolver, invoke);
this.createInvokeStatements(resolver, invoke);
javaFactory.getJavaAstType().addMethod(this.unit.getType(), invoke);
}
/**
* Create the method signature.
*
* @param resolver
* the service link resolver
* @param invoke
* the invoke method
*
* @throws JavaModelException
*/
private void createInvokeSignature(ServiceLinkResolver resolver, MethodDeclaration invoke)
throws JavaModelException {
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
TypeReference rqMsg, rsMsg;
{
String message = resolver.getRequestMessage();
message = message.substring(message.lastIndexOf(PKG_SEPARATOR) + 1);
rqMsg = producer.createTypeReference(message, false);
}
{
String message = resolver.getResponseMessage();
message = message.substring(message.lastIndexOf(PKG_SEPARATOR) + 1);
rsMsg = producer.createTypeReference(message, false);
}
String methodName = INVOKE + NabuccoTransformationUtility.firstToUpper(resolver.getServiceOperation());
javaFactory.getJavaAstMethod().setMethodName(invoke, methodName);
javaFactory.getJavaAstMethod().setReturnType(invoke, rsMsg);
List<Argument> arguments = javaFactory.getJavaAstMethod().getAllArguments(invoke);
if (arguments.size() != 1) {
throw new IllegalStateException("Connector Callback Template is not valid.");
}
javaFactory.getJavaAstArgument().setType(arguments.get(0), rqMsg);
}
/**
* Create the method body.
*
* @param resolver
* the service link resolver
* @param invoke
* the invoke method
*
* @throws JavaModelException
*/
private void createInvokeStatements(ServiceLinkResolver resolver, MethodDeclaration invoke)
throws JavaModelException {
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
String componentImport = resolver.getComponent();
String component = componentImport.substring(componentImport.lastIndexOf(PKG_SEPARATOR) + 1);
String componentLocator = component + LOCATOR;
String componentLocatorImport = componentImport + LOCATOR;
String serviceImport = resolver.getService();
String service = serviceImport.substring(serviceImport.lastIndexOf(PKG_SEPARATOR) + 1);
String serviceOperation = resolver.getServiceOperation();
String requestMsgImport = resolver.getRequestMessage();
String requestMsg = requestMsgImport.substring(requestMsgImport.lastIndexOf(PKG_SEPARATOR) + 1);
String responseMsgImport = resolver.getResponseMessage();
String responseMsg = responseMsgImport.substring(responseMsgImport.lastIndexOf(PKG_SEPARATOR) + 1);
javaFactory.getJavaAstUnit().addImport(this.unit.getUnitDeclaration(),
producer.createImportReference(componentImport));
javaFactory.getJavaAstUnit().addImport(this.unit.getUnitDeclaration(),
producer.createImportReference(componentLocatorImport));
javaFactory.getJavaAstUnit().addImport(this.unit.getUnitDeclaration(),
producer.createImportReference(serviceImport));
if (invoke.statements[0] instanceof LocalDeclaration) {
LocalDeclaration componentDeclaration = (LocalDeclaration) invoke.statements[0];
componentDeclaration.type = producer.createTypeReference(component, false);
if (componentDeclaration.initialization instanceof MessageSend) {
MessageSend getInstance = (MessageSend) componentDeclaration.initialization;
MessageSend locator = (MessageSend) getInstance.receiver;
locator.receiver = producer.createTypeReference(componentLocator, false);
}
}
if (invoke.statements[1] instanceof LocalDeclaration) {
LocalDeclaration serviceDeclaration = (LocalDeclaration) invoke.statements[1];
serviceDeclaration.type = producer.createTypeReference(service, false);
MessageSend getService = (MessageSend) serviceDeclaration.initialization;
javaFactory.getJavaAstMethodCall().setMethodName(PREFIX_GETTER + service, getService);
}
if (invoke.statements[2] instanceof LocalDeclaration) {
LocalDeclaration request = (LocalDeclaration) invoke.statements[2];
ParameterizedSingleTypeReference type = (ParameterizedSingleTypeReference) request.type;
type.typeArguments[0] = producer.createTypeReference(requestMsg, false);
AllocationExpression allocation = (AllocationExpression) request.initialization;
allocation.type = type;
}
if (invoke.statements[4] instanceof LocalDeclaration) {
LocalDeclaration response = (LocalDeclaration) invoke.statements[4];
ParameterizedSingleTypeReference type = (ParameterizedSingleTypeReference) response.type;
type.typeArguments[0] = producer.createTypeReference(responseMsg, false);
MessageSend operation = (MessageSend) response.initialization;
javaFactory.getJavaAstMethodCall().setMethodName(serviceOperation, operation);
}
}
}