/**
* Mule Development Kit
* Copyright 2010-2011 (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.mule.devkit.generation.mule.transfomer;
import org.apache.commons.lang.StringUtils;
import org.mule.api.annotations.Connector;
import org.mule.api.annotations.Module;
import org.mule.api.annotations.Processor;
import org.mule.api.transformer.DiscoverableTransformer;
import org.mule.api.transformer.TransformerException;
import org.mule.config.i18n.CoreMessages;
import org.mule.devkit.generation.AbstractModuleGenerator;
import org.mule.devkit.generation.DevKitTypeElement;
import org.mule.devkit.generation.NamingContants;
import org.mule.devkit.model.code.CatchBlock;
import org.mule.devkit.model.code.DefinedClass;
import org.mule.devkit.model.code.ExpressionFactory;
import org.mule.devkit.model.code.FieldVariable;
import org.mule.devkit.model.code.Invocation;
import org.mule.devkit.model.code.Method;
import org.mule.devkit.model.code.Modifier;
import org.mule.devkit.model.code.Op;
import org.mule.devkit.model.code.Package;
import org.mule.devkit.model.code.TryStatement;
import org.mule.devkit.model.code.Variable;
import org.mule.transformer.AbstractTransformer;
import org.mule.transformer.types.DataTypeFactory;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.util.ElementFilter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlType;
import javax.xml.transform.stream.StreamSource;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class JaxbTransformerGenerator extends AbstractModuleGenerator {
@Override
protected boolean shouldGenerate(DevKitTypeElement typeElement) {
return typeElement.hasAnnotation(Module.class) || typeElement.hasAnnotation(Connector.class);
}
@Override
protected void doGenerate(DevKitTypeElement typeElement) {
for (ExecutableElement executableElement : typeElement.getMethodsAnnotatedWith(Processor.class)) {
for (VariableElement variable : executableElement.getParameters()) {
if (context.getTypeMirrorUtils().isXmlType(variable.asType()) && !context.isJaxbElementRegistered(variable.asType())) {
// get class
DefinedClass jaxbTransformerClass = getJaxbTransformerClass(executableElement, variable);
// declare weight
FieldVariable weighting = jaxbTransformerClass.field(Modifier.PRIVATE, context.getCodeModel().INT, "weighting", Op.plus(ref(DiscoverableTransformer.class).staticRef("DEFAULT_PRIORITY_WEIGHTING"), ExpressionFactory.lit(1)));
// load JAXB context
Method loadJaxbContext = generateLoadJaxbContext(jaxbTransformerClass);
// declare JAXB context
FieldVariable jaxbContext = jaxbTransformerClass.field(Modifier.PRIVATE | Modifier.STATIC, JAXBContext.class, "JAXB_CONTEXT", ExpressionFactory.invoke(loadJaxbContext).arg(ref(variable.asType()).boxify().dotclass()));
//generate constructor
generateConstructor(jaxbTransformerClass, variable);
// doTransform
generateDoTransform(jaxbTransformerClass, jaxbContext, variable);
// set and get weight
generateGetPriorityWeighting(jaxbTransformerClass, weighting);
generateSetPriorityWeighting(jaxbTransformerClass, weighting);
context.registerAtBoot(jaxbTransformerClass);
context.registerJaxbElement(variable.asType());
}
}
}
}
private void generateSetPriorityWeighting(DefinedClass jaxbTransformerClass, FieldVariable weighting) {
Method setPriorityWeighting = jaxbTransformerClass.method(Modifier.PUBLIC, context.getCodeModel().VOID, "setPriorityWeighting");
Variable localWeighting = setPriorityWeighting.param(context.getCodeModel().INT, "weighting");
setPriorityWeighting.body().assign(ExpressionFactory._this().ref(weighting), localWeighting);
}
private void generateGetPriorityWeighting(DefinedClass jaxbTransformerClass, FieldVariable weighting) {
Method getPriorityWeighting = jaxbTransformerClass.method(Modifier.PUBLIC, context.getCodeModel().INT, "getPriorityWeighting");
getPriorityWeighting.body()._return(weighting);
}
private void generateDoTransform(DefinedClass jaxbTransformerClass, FieldVariable jaxbContext, VariableElement variable) {
Method doTransform = jaxbTransformerClass.method(Modifier.PROTECTED, Object.class, "doTransform");
doTransform._throws(TransformerException.class);
Variable src = doTransform.param(Object.class, "src");
Variable encoding = doTransform.param(String.class, "encoding");
Variable result = doTransform.body().decl(ref(variable.asType()).boxify(), "result", ExpressionFactory._null());
TryStatement tryBlock = doTransform.body()._try();
Variable unmarshaller = tryBlock.body().decl(ref(Unmarshaller.class), "unmarshaller");
tryBlock.body().assign(unmarshaller, jaxbContext.invoke("createUnmarshaller"));
Variable inputStream = tryBlock.body().decl(ref(InputStream.class), "is", ExpressionFactory._new(ref(ByteArrayInputStream.class)).arg(
ExpressionFactory.invoke(ExpressionFactory.cast(ref(String.class), src), "getBytes").arg(encoding)
));
Variable streamSource = tryBlock.body().decl(ref(StreamSource.class), "ss", ExpressionFactory._new(ref(StreamSource.class)).arg(inputStream));
Invocation unmarshal = unmarshaller.invoke("unmarshal");
unmarshal.arg(streamSource);
unmarshal.arg(ExpressionFactory.dotclass(ref(variable.asType()).boxify()));
tryBlock.body().assign(result, unmarshal.invoke("getValue"));
CatchBlock unsupportedEncodingCatch = tryBlock._catch(ref(UnsupportedEncodingException.class));
Variable unsupportedEncoding = unsupportedEncodingCatch.param("unsupportedEncoding");
generateThrowTransformFailedException(unsupportedEncodingCatch, unsupportedEncoding, variable);
CatchBlock jaxbExceptionCatch = tryBlock._catch(ref(JAXBException.class));
Variable jaxbException = jaxbExceptionCatch.param("jaxbException");
generateThrowTransformFailedException(jaxbExceptionCatch, jaxbException, variable);
doTransform.body()._return(result);
}
private void generateThrowTransformFailedException(CatchBlock catchBlock, Variable exception, VariableElement variable) {
Invocation transformFailedInvoke = ref(CoreMessages.class).staticInvoke("transformFailed");
transformFailedInvoke.arg("String");
transformFailedInvoke.arg(ExpressionFactory.lit(ref(variable.asType()).boxify().fullName()));
Invocation transformerException = ExpressionFactory._new(ref(TransformerException.class));
transformerException.arg(transformFailedInvoke);
transformerException.arg(ExpressionFactory._this());
transformerException.arg(exception);
catchBlock.body()._throw(transformerException);
}
private Method generateLoadJaxbContext(DefinedClass jaxbTransformerClass) {
Method loadJaxbContext = jaxbTransformerClass.method(Modifier.PRIVATE | Modifier.STATIC, ref(JAXBContext.class), "loadJaxbContext");
Variable clazz = loadJaxbContext.param(ref(Class.class), "clazz");
Variable innerJaxbContext = loadJaxbContext.body().decl(ref(JAXBContext.class), "context");
TryStatement tryBlock = loadJaxbContext.body()._try();
tryBlock.body().assign(innerJaxbContext, ref(JAXBContext.class).staticInvoke("newInstance").arg(clazz));
CatchBlock catchBlock = tryBlock._catch(ref(JAXBException.class));
Variable e = catchBlock.param("e");
catchBlock.body()._throw(ExpressionFactory._new(ref(RuntimeException.class)).arg(e));
loadJaxbContext.body()._return(innerJaxbContext);
return loadJaxbContext;
}
private void generateConstructor(DefinedClass jaxbTransformerClass, VariableElement variable) {
// generate constructor
Method constructor = jaxbTransformerClass.constructor(Modifier.PUBLIC);
// register source data type
registerSourceType(constructor);
// register destination data type
registerDestinationType(constructor, variable);
DeclaredType declaredType = (DeclaredType) variable.asType();
XmlType xmlType = declaredType.asElement().getAnnotation(XmlType.class);
constructor.body().invoke("setName").arg(StringUtils.capitalize(xmlType.name()) + "JaxbTransformer");
}
private void registerDestinationType(Method constructor, VariableElement variable) {
Invocation setReturnClass = constructor.body().invoke("setReturnClass");
setReturnClass.arg(ExpressionFactory.dotclass(ref(variable.asType()).boxify()));
}
private void registerSourceType(Method constructor) {
Invocation registerSourceType = constructor.body().invoke("registerSourceType");
registerSourceType.arg(ref(DataTypeFactory.class).staticRef("STRING"));
}
private DefinedClass getJaxbTransformerClass(ExecutableElement executableElement, VariableElement variable) {
DeclaredType declaredType = (DeclaredType) variable.asType();
XmlType xmlType = declaredType.asElement().getAnnotation(XmlType.class);
TypeElement parentClass = ElementFilter.typesIn(Arrays.asList(executableElement.getEnclosingElement())).get(0);
String packageName = context.getNameUtils().getPackageName(context.getNameUtils().getBinaryName(parentClass)) + NamingContants.TRANSFORMERS_NAMESPACE;
Package pkg = context.getCodeModel()._package(packageName);
return pkg._class(StringUtils.capitalize(xmlType.name()) + "JaxbTransformer", AbstractTransformer.class, new Class<?>[]{DiscoverableTransformer.class});
}
}