/*
* Copyright (C) 2011 Red Hat, Inc. and/or its affiliates.
*
* 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.jboss.errai.marshalling.rebind;
import static org.jboss.errai.codegen.meta.MetaClassFactory.parameterizedAs;
import static org.jboss.errai.codegen.meta.MetaClassFactory.typeParametersOf;
import static org.jboss.errai.codegen.util.Implementations.autoForLoop;
import static org.jboss.errai.codegen.util.Implementations.autoInitializedField;
import static org.jboss.errai.codegen.util.Implementations.implement;
import static org.jboss.errai.codegen.util.Stmt.loadVariable;
import static org.jboss.errai.marshalling.rebind.util.MarshallingGenUtil.getVarName;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.enterprise.context.Dependent;
import javax.enterprise.util.TypeLiteral;
import org.jboss.errai.codegen.Context;
import org.jboss.errai.codegen.InnerClass;
import org.jboss.errai.codegen.Parameter;
import org.jboss.errai.codegen.Statement;
import org.jboss.errai.codegen.StringStatement;
import org.jboss.errai.codegen.builder.BlockBuilder;
import org.jboss.errai.codegen.builder.ClassStructureBuilder;
import org.jboss.errai.codegen.builder.ConstructorBlockBuilder;
import org.jboss.errai.codegen.builder.ElseBlockBuilder;
import org.jboss.errai.codegen.builder.StatementEnd;
import org.jboss.errai.codegen.builder.impl.ClassBuilder;
import org.jboss.errai.codegen.meta.MetaClass;
import org.jboss.errai.codegen.meta.MetaClassFactory;
import org.jboss.errai.codegen.meta.impl.build.BuildMetaClass;
import org.jboss.errai.codegen.util.Bool;
import org.jboss.errai.codegen.util.GenUtil;
import org.jboss.errai.codegen.util.If;
import org.jboss.errai.codegen.util.Stmt;
import org.jboss.errai.common.rebind.NameUtil;
import org.jboss.errai.common.rebind.UniqueNameGenerator;
import org.jboss.errai.config.rebind.CommonConfigAttribs;
import org.jboss.errai.marshalling.client.api.DeferredMarshallerCreationCallback;
import org.jboss.errai.marshalling.client.api.GeneratedMarshaller;
import org.jboss.errai.marshalling.client.api.Marshaller;
import org.jboss.errai.marshalling.client.api.MarshallerFactory;
import org.jboss.errai.marshalling.client.api.MarshallingSession;
import org.jboss.errai.marshalling.client.api.annotations.AlwaysQualify;
import org.jboss.errai.marshalling.client.api.json.EJArray;
import org.jboss.errai.marshalling.client.api.json.EJValue;
import org.jboss.errai.marshalling.client.marshallers.QualifyingMarshallerWrapper;
import org.jboss.errai.marshalling.rebind.api.ArrayMarshallerCallback;
import org.jboss.errai.marshalling.rebind.api.GeneratorMappingContext;
import org.jboss.errai.marshalling.rebind.api.GeneratorMappingContextFactory;
import org.jboss.errai.marshalling.rebind.api.MappingStrategy;
import org.jboss.errai.marshalling.rebind.api.model.MappingDefinition;
import org.jboss.errai.marshalling.rebind.util.MarshallingGenUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.shared.GWT;
/**
* @author Mike Brock <cbrock@redhat.com>
* @author Christian Sadilek <csadilek@redhat.com>
*/
public class MarshallerGeneratorFactory {
private final class ArrayMarshallerCallbackImpl implements ArrayMarshallerCallback {
@Override
public Statement marshal(final MetaClass type, final Statement value) {
createDemarshallerIfNeeded(type);
return value;
}
@Override
public Statement demarshall(final MetaClass type, final Statement value) {
final String variable = createDemarshallerIfNeeded(type.asBoxed());
return Stmt.loadVariable(variable).invoke("demarshall", value, Stmt.loadVariable("a1"));
}
private String createDemarshallerIfNeeded(final MetaClass type) {
if (done) {
return getVarName(type);
}
return addArrayMarshaller(type.asBoxed(), target == MarshallerOutputTarget.GWT);
}
@Override
public Statement deferred(final MetaClass type, final MetaClass marshaller) {
return
Stmt.newObject(parameterizedAs(DeferredMarshallerCreationCallback.class, typeParametersOf(type)))
.extend()
.publicOverridesMethod("create", Parameter.of(Class.class, "type"))
.append(
Stmt.nestedCall(
Stmt.newObject(QualifyingMarshallerWrapper.class,
Stmt.castTo(Marshaller.class, Stmt.invokeStatic(GWT.class, "create", marshaller)), type))
.returnValue())
.finish()
.finish();
}
}
public static final String MARSHALLER_NAME_PREFIX = "Marshaller_for_";
public static final String SHORT_MARSHALLER_PREFIX = "Marshaller_";
public static final String VERY_SHORT_MARSHALLER_PREFIX = "M";
private static final String MARSHALLERS_VAR = "marshallers";
private static final boolean VERY_SHORT_MARSHALLER_NAMES = Boolean.parseBoolean(System.getProperty(MarshallingGenUtil.USE_VERY_SHORT_IMPL_NAMES, "false"));
private static final boolean SHORT_MARSHALLER_NAMES = Boolean.parseBoolean(System.getProperty(MarshallingGenUtil.USE_SHORT_IMPL_NAMES, "true"));
private static final int MARSHALLER_HELPER_METHOD_SIZE = 100;
private final MarshallerOutputTarget target;
private GeneratorMappingContext mappingContext;
private final GeneratorContext context;
private final Map<String, Statement> putStatementsByTypeName = new LinkedHashMap<>();
private ClassStructureBuilder<?> classStructureBuilder;
private Context classContext;
private boolean done;
private final Set<String> arrayMarshallers = new HashSet<>();
private final Set<String> unlazyMarshallers = new HashSet<>();
private static final Logger log = LoggerFactory.getLogger(MarshallerGeneratorFactory.class);
private static boolean refresh = false;
private static final UniqueNameGenerator uniqueGenerator = new UniqueNameGenerator();
private static final Map<String, String> leasedNamesByTypeName = new HashMap<>();
long startTime;
private MarshallerGeneratorFactory(final GeneratorContext context, final MarshallerOutputTarget target) {
this.context = context;
this.target = target;
}
public static MarshallerGeneratorFactory getFor(final GeneratorContext context, final MarshallerOutputTarget target) {
return new MarshallerGeneratorFactory(context, target);
}
public String generate(final String packageName, final String clazzName) {
return generate(packageName, clazzName, MarshallerGenerationCallback.NO_OP);
}
public String generate(final String packageName, final String clazzName, final MarshallerGenerationCallback callback) {
final String gen;
log.info("generating marshaller factory class for " + (((target == MarshallerOutputTarget.GWT) ? "client" : "server") + "..."));
final long time = System.currentTimeMillis();
if (target == MarshallerOutputTarget.GWT && refresh) {
DefinitionsFactorySingleton.get().resetDefinitionsAndReload();
}
gen = _generate(packageName, clazzName, callback);
log.info("generated marshaller factory class in " + (System.currentTimeMillis() - time) + "ms.");
return gen;
}
private String _generate(final String packageName, final String clazzName, final MarshallerGenerationCallback callback) {
startTime = System.currentTimeMillis();
classStructureBuilder = implement(MarshallerFactory.class, packageName, clazzName);
classContext = classStructureBuilder.getClassDefinition().getContext();
mappingContext = GeneratorMappingContextFactory.create(context, target, this, classStructureBuilder, new ArrayMarshallerCallbackImpl());
classStructureBuilder.getClassDefinition().addAnnotation(() -> Dependent.class);
@SuppressWarnings({ "serial", "rawtypes" })
final MetaClass javaUtilMap = MetaClassFactory.get(new TypeLiteral<Map<String, Marshaller>>() {});
final Class<?> mapClass = (MarshallerOutputTarget.GWT.equals(target) ? HashMap.class : ConcurrentHashMap.class);
autoInitializedField(classStructureBuilder, javaUtilMap, MARSHALLERS_VAR, mapClass);
final ConstructorBlockBuilder<?> constructor = classStructureBuilder.publicConstructor();
processExposedClasses(constructor);
constructor.finish();
final BlockBuilder<?> getMarshallerMethod =
classStructureBuilder.publicMethod(parameterizedAs(Marshaller.class, typeParametersOf(Object.class)),
"getMarshaller").parameters(String.class)
.body()
.append(
If.isNull(loadVariable("a0"))
.append(Stmt.loadLiteral(null).returnValue()).finish())
.append(Stmt.declareVariable("m", Marshaller.class, Stmt.loadVariable(MARSHALLERS_VAR).invoke("get", loadVariable("a0"))));
generateMarshallers(callback);
final ElseBlockBuilder getMarshallerConditional = generateGetMarshallerHelperMethods();
getMarshallerMethod.append(getMarshallerConditional);
getMarshallerMethod.append(Stmt.loadLiteral(null).returnValue()).finish();
if (CommonConfigAttribs.MAKE_DEFAULT_ARRAY_MARSHALLERS.getBoolean()) {
for (final MetaClass arrayType : MarshallingGenUtil.getDefaultArrayMarshallers()) {
addArrayMarshaller(arrayType, target == MarshallerOutputTarget.GWT);
}
}
for (final MetaClass metaClass : mappingContext.getDefinitionsFactory().getArraySignatures()) {
addArrayMarshaller(metaClass, target == MarshallerOutputTarget.GWT);
}
classStructureBuilder.publicMethod(void.class, "registerMarshaller").parameters(String.class, Marshaller.class)
.body()
.append(Stmt.loadVariable(MARSHALLERS_VAR).invoke("put", Stmt.loadVariable("a0"), Stmt.loadVariable("a1")))
.finish();
done = true;
if (target == MarshallerOutputTarget.GWT) {
refresh = true;
}
return classStructureBuilder.toJavaString();
}
private ElseBlockBuilder generateGetMarshallerHelperMethods() {
createPutIfNotNullMethod();
BlockBuilder<ElseBlockBuilder> getMarshallerConditionalBlock = If.isNotNull(Stmt.loadVariable("m")).append(Stmt.loadVariable("m").returnValue());
int methodIndex = 0, typeIndex = 0;
ElseBlockBuilder elseBlockBuilder = null;
final Iterator<Entry<String, Statement>> iter = putStatementsByTypeName.entrySet().iterator();
while (iter.hasNext()) {
final Entry<String, Statement> entry = iter.next();
final String typeName = entry.getKey();
final Statement stmt = entry.getValue();
if (typeIndex % MARSHALLER_HELPER_METHOD_SIZE == 0) {
if (typeIndex != 0) {
getMarshallerConditionalBlock = addLoadMarshallerMethod(getMarshallerConditionalBlock, methodIndex, elseBlockBuilder);
methodIndex++;
}
elseBlockBuilder = If.objEquals(Stmt.loadLiteral(typeName), Stmt.loadVariable("a0")).append(stmt).finish();
}
else {
elseBlockBuilder = elseBlockBuilder.elseif_(Stmt.loadLiteral(typeName).invoke("equals",
Stmt.loadVariable("a0"))).append(stmt).finish();
}
typeIndex += 1;
}
if (typeIndex % MARSHALLER_HELPER_METHOD_SIZE != 1) {
getMarshallerConditionalBlock = addLoadMarshallerMethod(getMarshallerConditionalBlock, methodIndex, elseBlockBuilder);
}
return getMarshallerConditionalBlock.finish();
}
private void createPutIfNotNullMethod() {
classStructureBuilder
.privateMethod(boolean.class, "putIfNotNull", Parameter.of(String.class, "fqcn"), Parameter.of(Marshaller.class, "m"))
.append(If.isNotNull(Stmt.loadVariable("m"))
.append(Stmt.loadVariable(MARSHALLERS_VAR).invoke("put", Stmt.loadVariable("fqcn"), Stmt.loadVariable("m")))
.append(Stmt.loadLiteral(true).returnValue())
.finish()
.else_()
.append(Stmt.loadLiteral(false).returnValue())
.finish())
.finish();
}
private BlockBuilder<ElseBlockBuilder> addLoadMarshallerMethod(BlockBuilder<ElseBlockBuilder> getMarshallerConditionalBlock,
final int methodIndex, final ElseBlockBuilder elseBlockBuilder) {
final String helperMethodName = "loadMarshaller" + methodIndex;
/*
* Using the StringStatement is a workaround because the following line exposes a codegen bug,
* resulting in an OutOfScopeException for "this":
* Stmt.loadVariable("this").invoke(helperMethodName, Stmt.loadVariable("a0"))
*/
getMarshallerConditionalBlock = updateGetMarshallerConditionalBlock(getMarshallerConditionalBlock, helperMethodName);
addLoadMarshallerMethod(elseBlockBuilder, helperMethodName);
return getMarshallerConditionalBlock;
}
private BlockBuilder<ElseBlockBuilder> updateGetMarshallerConditionalBlock(BlockBuilder<ElseBlockBuilder> getMarshallerConditionalBlock,
final String helperMethodName) {
getMarshallerConditionalBlock = getMarshallerConditionalBlock.finish().elseif_(StringStatement.of(helperMethodName + "(a0)", boolean.class))
.append(Stmt.loadVariable(MARSHALLERS_VAR).invoke("get", Stmt.loadVariable("a0")).returnValue());
return getMarshallerConditionalBlock;
}
private void addLoadMarshallerMethod(final ElseBlockBuilder elseBlockBuilder, final String helperMethodName) {
classStructureBuilder
.privateMethod(boolean.class, helperMethodName)
.parameters(String.class)
.body()
.append(Stmt.declareVariable("m", Marshaller.class, Stmt.loadLiteral(null)))
.append(elseBlockBuilder)
.append(Stmt.loadVariable("this").invoke("putIfNotNull", Stmt.loadVariable("a0"), Stmt.loadVariable("m")).returnValue())
.finish();
}
private void processExposedClasses(final ConstructorBlockBuilder<?> constructor) {
mappingContext
.getDefinitionsFactory()
.getExposedClasses()
.stream()
.forEachOrdered(cls -> processExposedClass(cls, constructor));
}
private void processExposedClass(final MetaClass cls, final ConstructorBlockBuilder<?> constructor) {
final String clsName = cls.getFullyQualifiedName();
if (!mappingContext.getDefinitionsFactory().hasDefinition(clsName)) {
return;
}
final MappingDefinition definition = mappingContext.getDefinitionsFactory().getDefinition(clsName);
@SuppressWarnings("rawtypes")
final Class<? extends Marshaller> marshallerCls = (target == MarshallerOutputTarget.GWT) ?
definition.getClientMarshallerClass() : definition.getServerMarshallerClass();
if (marshallerCls == null) {
return;
}
mappingContext.markRendered(cls);
final String varName = getVarName(clsName);
Statement marshaller = null;
if (marshallerCls.isAnnotationPresent(AlwaysQualify.class)) {
final MetaClass type = MetaClassFactory.parameterizedAs(QualifyingMarshallerWrapper.class,
MetaClassFactory.typeParametersOf(cls));
marshaller = Stmt.declareFinalVariable(varName, type, Stmt.newObject(QualifyingMarshallerWrapper.class)
.withParameters(Stmt.newObject(marshallerCls), marshallerCls));
}
else {
marshaller = Stmt.declareFinalVariable(varName, marshallerCls, Stmt.newObject(marshallerCls));
}
constructor.append(marshaller);
constructor.append(Stmt.create(classContext).loadVariable(MARSHALLERS_VAR).invoke("put", clsName,
loadVariable(varName)));
for (final Map.Entry<String, String> aliasEntry : mappingContext.getDefinitionsFactory().getMappingAliases()
.entrySet()) {
if (aliasEntry.getValue().equals(clsName)) {
constructor.append(Stmt.create(classContext).loadVariable(MARSHALLERS_VAR)
.invoke("put", aliasEntry.getKey(), loadVariable(varName)));
}
}
}
private void generateMarshallers(final MarshallerGenerationCallback callback) {
final Set<MetaClass> exposed = mappingContext.getDefinitionsFactory().getExposedClasses();
for (final MetaClass clazz : exposed) {
mappingContext.registerGeneratedMarshaller(clazz.getFullyQualifiedName());
}
final boolean lazyEnabled = CommonConfigAttribs.LAZY_LOAD_BUILTIN_MARSHALLERS.getBoolean();
for (final MetaClass cls : exposed) {
final MetaClass compType = cls.getOuterComponentType();
final MappingDefinition definition = mappingContext.getDefinitionsFactory().getDefinition(compType);
if (definition.getClientMarshallerClass() != null || definition.alreadyGenerated()) {
continue;
}
if (target == MarshallerOutputTarget.Java && lazyEnabled && definition.isLazy()) {
if (unlazyMarshallers.contains(compType.getFullyQualifiedName())) {
definition.setLazy(false);
}
else {
continue;
}
}
addMarshaller(compType);
callback.callback(compType);
}
}
public void addOrMarkMarshallerUnlazy(final MetaClass type) {
final MappingDefinition definition = mappingContext.getDefinitionsFactory().getDefinition(type);
if (definition == null) {
unlazyMarshallers.add(type.getFullyQualifiedName());
}
else if (definition.isLazy()) {
definition.setLazy(false);
addMarshaller(type);
}
}
public void addMarshaller(final MetaClass type) {
if (!mappingContext.isRendered(type)) {
mappingContext.markRendered(type);
BuildMetaClass customMarshaller = null;
if (target == MarshallerOutputTarget.GWT) {
customMarshaller =
ClassBuilder
.define(MARSHALLER_NAME_PREFIX + getVarName(type)).packageScope()
.abstractClass()
.implementsInterface(
MetaClassFactory.get(GeneratedMarshaller.class))
.body().getClassDefinition();
}
else {
final MappingStrategy strategy = MappingStrategyFactory
.createStrategy(false, GeneratorMappingContextFactory.getFor(context, target), type);
final String marshallerClassName = generateMarshallerImplClassName(type, target == MarshallerOutputTarget.GWT);
final ClassStructureBuilder<?> marshaller = strategy.getMapper().getMarshaller(marshallerClassName);
customMarshaller = marshaller.getClassDefinition();
}
classStructureBuilder.declaresInnerClass(new InnerClass(customMarshaller));
addMarshaller(customMarshaller, type);
}
}
private void addMarshaller(final BuildMetaClass marshaller, final MetaClass type) {
if (target == MarshallerOutputTarget.GWT) {
if (type.isAnnotationPresent(AlwaysQualify.class)) {
addConditionalAssignment(
type,
Stmt.loadVariable("m").assignValue(Stmt.nestedCall(
Stmt.newObject(QualifyingMarshallerWrapper.class,
Stmt.castTo(Marshaller.class, Stmt.invokeStatic(GWT.class, "create", marshaller)), type))));
}
else {
addConditionalAssignment(
type,
Stmt.loadVariable("m").assignValue(Stmt.invokeStatic(GWT.class, "create", marshaller)));
}
}
else {
if (type.isAnnotationPresent(AlwaysQualify.class)) {
addConditionalAssignment(
type,
Stmt.loadVariable("m").assignValue(Stmt.newObject(QualifyingMarshallerWrapper.class, marshaller, type)));
}
else {
addConditionalAssignment(type, Stmt.loadVariable("m").assignValue(Stmt.newObject(marshaller)));
}
}
for (final Map.Entry<String, String> aliasEntry : mappingContext.getDefinitionsFactory().getMappingAliases()
.entrySet()) {
if (aliasEntry.getValue().equals(type.getFullyQualifiedName())) {
final MetaClass aliasType = MetaClassFactory.get(aliasEntry.getKey());
if (!mappingContext.isRendered(aliasType)) {
addMarshaller(marshaller, aliasType);
}
}
}
}
private String addArrayMarshaller(final MetaClass type, final boolean gwtTarget) {
final String varName = getVarName(type);
if (!arrayMarshallers.contains(varName)) {
final String marshallerClassName = getMarshallerImplClassName(type, gwtTarget);
final InnerClass arrayMarshaller = new InnerClass(generateArrayMarshaller(type, marshallerClassName, gwtTarget));
classStructureBuilder.declaresInnerClass(arrayMarshaller);
addConditionalAssignment(type, Stmt.loadVariable("m").assignValue(
Stmt.newObject(QualifyingMarshallerWrapper.class, Stmt.newObject(arrayMarshaller.getType()), type
.asClass())));
}
arrayMarshallers.add(varName);
return varName;
}
public static String getMarshallerImplClassName(final MetaClass type, final boolean gwtTarget) {
String implName = leasedNamesByTypeName.get(type.getFullyQualifiedName());
if (implName == null) {
implName = generateMarshallerImplClassName(type, gwtTarget);
leasedNamesByTypeName.put(type.getFullyQualifiedName(), implName);
}
return implName;
}
private static String generateMarshallerImplClassName(final MetaClass type, final boolean gwtTarget) {
final String varName = getVarName(type);
if (VERY_SHORT_MARSHALLER_NAMES && !gwtTarget) {
return VERY_SHORT_MARSHALLER_PREFIX + uniqueGenerator.uniqueName(NameUtil.getShortHashString(varName));
}
else if (SHORT_MARSHALLER_NAMES) {
return SHORT_MARSHALLER_PREFIX + uniqueGenerator.uniqueName(NameUtil.shortenDerivedIdentifier(varName)) + "_Impl";
}
else {
return MARSHALLER_NAME_PREFIX + varName + "_Impl";
}
}
static BuildMetaClass generateArrayMarshaller(final MetaClass arrayType, final String marshallerClassName, final boolean gwtTarget) {
final MetaClass toMap = arrayType.getOuterComponentType();
final int dimensions = GenUtil.getArrayDimensions(arrayType);
final ClassStructureBuilder<?> classStructureBuilder =
ClassBuilder.define(marshallerClassName).publicScope().
implementsInterface(parameterizedAs(Marshaller.class, typeParametersOf(arrayType))).body();
BlockBuilder<?> initMethod = null;
if (gwtTarget) {
initMethod = classStructureBuilder.privateMethod(void.class, "lazyInit");
}
final MetaClass arrayOfArrayType = arrayType.asArrayOf(1);
classStructureBuilder.publicMethod(arrayOfArrayType, "getEmptyArray")
.append(Stmt.load(null).returnValue())
.finish();
final BlockBuilder<?> bBuilder = classStructureBuilder.publicMethod(arrayType, "demarshall",
Parameter.of(EJValue.class, "a0"), Parameter.of(MarshallingSession.class, "a1"));
bBuilder.append(
If.isNull(loadVariable("a0"))
.append(Stmt.load(null).returnValue())
.finish()
.else_()
.append(Stmt.nestedCall(Stmt.loadVariable("this")).invoke("_demarshall" + dimensions,
Stmt.loadVariable("a0").invoke("isArray"), loadVariable("a1")).returnValue())
.finish());
bBuilder.finish();
arrayDemarshallCode(toMap, dimensions, classStructureBuilder, initMethod);
final BlockBuilder<?> marshallMethodBlock = classStructureBuilder.publicMethod(String.class, "marshall",
Parameter.of(toMap.asArrayOf(dimensions), "a0"), Parameter.of(MarshallingSession.class, "a1"));
marshallMethodBlock.append(
If.isNull(loadVariable("a0"))
.append(Stmt.load(null).returnValue())
.finish()
.else_()
.append(Stmt.nestedCall(Stmt.loadVariable("this")).invoke("_marshall" + dimensions,
loadVariable("a0"), loadVariable("a1")).returnValue())
.finish()
);
marshallMethodBlock.finish();
if (initMethod != null) {
initMethod.finish();
}
return classStructureBuilder.getClassDefinition();
}
static void arrayDemarshallCode(final MetaClass toMap,
final int dim,
final ClassStructureBuilder<?> classBuilder,
final BlockBuilder<?> initMethod) {
final Object[] dimParms = new Object[dim];
dimParms[0] = Stmt.loadVariable("a0").invoke("size");
final MetaClass arrayType = toMap.asArrayOf(dim);
String marshallerVarName;
if (DefinitionsFactorySingleton.get().shouldUseObjectMarshaller(toMap)) {
marshallerVarName = getVarName(MetaClassFactory.get(Object.class));
MarshallingGenUtil.ensureMarshallerFieldCreated(classBuilder, toMap, MetaClassFactory.get(Object.class), initMethod);
}
else {
marshallerVarName = getVarName(toMap);
MarshallingGenUtil.ensureMarshallerFieldCreated(classBuilder, null, toMap, initMethod);
}
final Statement demarshallerStatement = Stmt.castTo(toMap.asBoxed().asClass(),
Stmt.loadVariable(marshallerVarName).invoke("demarshall", loadVariable("a0")
.invoke("get", loadVariable("i")), Stmt.loadVariable("a1")));
final Statement outerAccessorStatement =
loadVariable("newArray", loadVariable("i"))
.assignValue(demarshallerStatement);
final BlockBuilder<?> dmBuilder =
classBuilder.privateMethod(arrayType, "_demarshall" + dim)
.parameters(EJArray.class, MarshallingSession.class).body();
if (initMethod != null) {
dmBuilder.append(Stmt.loadVariable("this").invoke("lazyInit"));
}
dmBuilder.append(Stmt
.declareVariable(arrayType).named("newArray")
.initializeWith(Stmt.newArray(toMap, dimParms)));
dmBuilder.append(autoForLoop("i", Stmt.loadVariable("newArray").loadField("length"))
.append(dim == 1 ? outerAccessorStatement
: loadVariable("newArray", loadVariable("i")).assignValue(
Stmt.loadVariable("this").invoke(
"_demarshall" + (dim - 1),
Stmt.loadVariable("a0").invoke("get", Stmt.loadVariable("i")).invoke("isArray"),
Stmt.loadVariable("a1"))))
.finish())
.append(Stmt.loadVariable("newArray").returnValue());
dmBuilder.finish();
final BlockBuilder<?> mBuilder = classBuilder.privateMethod(String.class, "_marshall" + dim)
.parameters(arrayType, MarshallingSession.class).body();
MarshallingGenUtil.ensureMarshallerFieldCreated(classBuilder, null, MetaClassFactory.get(Object.class), initMethod);
if (initMethod != null) {
mBuilder.append(Stmt.loadVariable("this").invoke("lazyInit"));
}
mBuilder.append(Stmt.declareVariable(StringBuilder.class).named("sb")
.initializeWith(Stmt.newObject(StringBuilder.class, "[")))
.append(autoForLoop("i", Stmt.loadVariable("a0").loadField("length"))
.append(Stmt.if_(Bool.greaterThan(Stmt.loadVariable("i"), 0))
.append(Stmt.loadVariable("sb").invoke("append", ",")).finish())
.append(Stmt.loadVariable("sb").invoke("append", dim == 1 ?
Stmt.loadVariable(MarshallingGenUtil.getVarName(MetaClassFactory.get(Object.class)))
.invoke("marshall",
Stmt.loadVariable("a0", Stmt.loadVariable("i")),
Stmt.loadVariable("a1"))
:
Stmt.loadVariable("this").invoke(
"_marshall" + (dim - 1), Stmt.loadVariable("a0", Stmt.loadVariable("i")), loadVariable("a1"))))
.finish())
.append(Stmt.loadVariable("sb").invoke("append", "]").invoke("toString").returnValue())
.finish();
if (dim > 1) {
arrayDemarshallCode(toMap, dim - 1, classBuilder, initMethod);
}
}
public static BuildMetaClass createArrayMarshallerClass(final MetaClass type) {
final BuildMetaClass arrayMarshaller =
ClassBuilder
.define(MARSHALLER_NAME_PREFIX + getVarName(type)).packageScope()
.abstractClass()
.implementsInterface(
MetaClassFactory.get(GeneratedMarshaller.class))
.body().getClassDefinition();
return arrayMarshaller;
}
private void addConditionalAssignment(final MetaClass type, final StatementEnd assignment) {
putStatementsByTypeName.put(type.getFullyQualifiedName(), assignment);
}
}