/*
* 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.List;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.nabucco.framework.generator.compiler.constants.NabuccoJavaTemplateConstants;
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.application.connector.util.ServiceLinkResolver;
import org.nabucco.framework.generator.compiler.transformation.java.common.ast.JavaAstSupport;
import org.nabucco.framework.generator.compiler.transformation.java.constants.ServerConstants;
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.mapper.NabuccoModifierComponentMapper;
import org.nabucco.framework.generator.compiler.visitor.NabuccoVisitorException;
import org.nabucco.framework.generator.parser.model.NabuccoModelType;
import org.nabucco.framework.generator.parser.model.modifier.NabuccoModifierType;
import org.nabucco.framework.generator.parser.syntaxtree.ApplicationStatement;
import org.nabucco.framework.generator.parser.syntaxtree.ConnectorStatement;
import org.nabucco.framework.generator.parser.syntaxtree.Node;
import org.nabucco.framework.generator.parser.syntaxtree.ServiceLinkDeclaration;
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.discriminator.LiteralType;
import org.nabucco.framework.mda.model.java.ast.produce.JavaAstModelProducer;
import org.nabucco.framework.mda.template.java.JavaTemplateException;
/**
* NabuccoToJavaServiceConnectorVisitor
*
* @author Nicolas Moser, PRODYNA AG
*/
class NabuccoToJavaServiceConnectorVisitor extends NabuccoToJavaVisitorSupport implements NabuccoJavaTemplateConstants,
ServerConstants {
private JavaCompilationUnit unit;
private ApplicationStatement nabuccoApplication;
private static final String FIELD_SOURCE_SERVICE = "SOURCE_SERVICE";
private static final String FIELD_SOURCE_OPERATION = "SOURCE_OPERATION";
private static final String FIELD_SOURCE_MESSAGE = "SOURCE_MESSAGE";
private static final String STRATEGY_BEFORE = "BEFORE";
/**
* Creates a new {@link NabuccoToJavaServiceConnectorVisitor} instance.
*
* @param visitorContext
* the visitor context
* @param nabuccoApplication
* the application statement
*/
public NabuccoToJavaServiceConnectorVisitor(NabuccoToJavaVisitorContext visitorContext,
ApplicationStatement nabuccoApplication) {
super(visitorContext);
this.nabuccoApplication = nabuccoApplication;
}
@Override
public void visit(ConnectorStatement nabuccoConnector, MdaModel<JavaModel> target) {
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
String name = nabuccoConnector.nodeToken2.tokenImage;
NabuccoModifierType modifier = NabuccoModifierComponentMapper
.getModifierType(nabuccoConnector.nodeToken.tokenImage);
String projectName = super.getProjectName(NabuccoModelType.CONNECTOR, modifier);
try {
// Load Template
this.unit = super.extractAst(SERVICE_CONNECTOR_TEMPLATE);
TypeDeclaration type = this.unit.getType(SERVICE_CONNECTOR_TEMPLATE);
javaFactory.getJavaAstType().setTypeName(type, name);
// Package
this.createPackage(this.unit);
// Javadoc
this.createJavadoc(nabuccoConnector, type);
// Constructor
this.createConstructor(nabuccoConnector);
// File creation
this.unit.setProjectName(projectName);
this.unit.setSourceFolder(super.getSourceFolder());
target.getModel().getUnitList().add(unit);
} catch (JavaModelException jme) {
throw new NabuccoVisitorException("Error creating Java Application.", jme);
} catch (JavaTemplateException te) {
throw new NabuccoVisitorException("Error creating Java Application.", te);
}
super.visit(nabuccoConnector, target);
}
/**
* Create the class javadoc depending on the application information.
*
* @param nabuccoConnector
* the connector holding the annotations
* @param type
* the java type to create the javadoc for
*/
private void createJavadoc(ConnectorStatement nabuccoConnector, TypeDeclaration type) {
Node application = nabuccoConnector.getParent().getParent().getParent().getParent();
if (application instanceof ApplicationStatement) {
JavaAstSupport.convertJavadocAnnotations(((ApplicationStatement) application).annotationDeclaration, type);
}
}
/**
* Create the package of the connector.
*
* @param unit
* the java compilation unit
*
* @throws JavaModelException
*/
private void createPackage(JavaCompilationUnit unit) throws JavaModelException {
StringBuilder pkg = new StringBuilder();
pkg.append(super.getVisitorContext().getPackage());
pkg.append(PKG_SEPARATOR);
pkg.append(PKG_CONNECTOR);
JavaAstElementFactory.getInstance().getJavaAstUnit().setPackage(unit.getUnitDeclaration(), pkg.toString());
}
/**
* Modify the strategy type in the connector constructor.
*
* @param nabuccoConnector
*
* @throws JavaModelException
*/
private void createConstructor(ConnectorStatement nabuccoConnector) throws JavaModelException {
NabuccoAnnotation connectorStrategy = NabuccoAnnotationMapper.getInstance().mapToAnnotation(
nabuccoConnector.annotationDeclaration, NabuccoAnnotationType.CONNECTOR_STRATEGY);
// Skip because template defaults to AFTER
if (connectorStrategy == null || connectorStrategy.getValue() == null) {
return;
}
String value = connectorStrategy.getValue();
// Skip because template defaults to AFTER
if (!value.equalsIgnoreCase(STRATEGY_BEFORE)) {
return;
}
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
TypeDeclaration type = this.unit.getType();
List<ConstructorDeclaration> constructors = javaFactory.getJavaAstType().getConstructors(type);
if (constructors.size() != 1) {
throw new IllegalStateException("Connector Template not valid. Multiple constructors found.");
}
ConstructorDeclaration constructor = constructors.get(0);
ExplicitConstructorCall superCall = constructor.constructorCall;
if (superCall == null || superCall.arguments == null || superCall.arguments.length != 1) {
throw new IllegalStateException("Connector Template not valid. No super call defined.");
}
Expression strategy = superCall.arguments[0];
if (!(strategy instanceof QualifiedNameReference)) {
throw new IllegalStateException("Connector Template not valid.");
}
QualifiedNameReference reference = (QualifiedNameReference) strategy;
if (reference.tokens == null || reference.tokens.length != 2) {
throw new IllegalStateException("Connector Template not valid.");
}
reference.tokens[reference.tokens.length - 1] = value.toUpperCase().toCharArray();
}
@Override
public void visit(ServiceLinkDeclaration serviceLink, MdaModel<JavaModel> target) {
boolean isSource = JavaAstSupport
.hasAnnotation(serviceLink.annotationDeclaration, NabuccoAnnotationType.SOURCE);
if (isSource) {
this.createSourceConstants(serviceLink);
} else {
this.createTargetCallbacks(serviceLink, target);
}
super.visit(serviceLink, target);
}
/**
* Modifies the constants for the source operation signature.
*
* @param serviceLink
* the service link for the source service
*/
private void createSourceConstants(ServiceLinkDeclaration serviceLink) {
ServiceLinkResolver resolver = new ServiceLinkResolver(this.nabuccoApplication, super.getVisitorContext());
resolver.resolve(serviceLink);
String service = resolver.getService();
String operation = resolver.getServiceOperation();
String message = resolver.getRequestMessage();
JavaAstModelProducer producer = JavaAstModelProducer.getInstance();
JavaAstElementFactory javaFactory = JavaAstElementFactory.getInstance();
try {
FieldDeclaration field;
TypeDeclaration type = this.unit.getType();
field = javaFactory.getJavaAstType().getField(type, FIELD_SOURCE_SERVICE);
field.initialization = producer.createLiteral(service, LiteralType.STRING_LITERAL);
field = javaFactory.getJavaAstType().getField(type, FIELD_SOURCE_OPERATION);
field.initialization = producer.createLiteral(operation, LiteralType.STRING_LITERAL);
field = javaFactory.getJavaAstType().getField(type, FIELD_SOURCE_MESSAGE);
field.initialization = producer.createLiteral(message, LiteralType.STRING_LITERAL);
} catch (JavaModelException e) {
throw new NabuccoVisitorException("Error creating service link signature.", e);
}
}
/**
* Create the callback methods for each target servicelink.
*
* @param serviceLink
* the target servicelink
* @param target
* the target model
*/
private void createTargetCallbacks(ServiceLinkDeclaration serviceLink, MdaModel<JavaModel> target) {
NabuccoToJavaServiceTargetServiceLinkVisitor visitor = new NabuccoToJavaServiceTargetServiceLinkVisitor(
super.getVisitorContext(), this.nabuccoApplication, this.unit);
serviceLink.accept(visitor, target);
}
}