/*
* 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.codegen.builder.impl;
import static org.jboss.errai.codegen.builder.callstack.LoadClassReference.getClassReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.jboss.errai.codegen.AbstractStatement;
import org.jboss.errai.codegen.Context;
import org.jboss.errai.codegen.DefParameters;
import org.jboss.errai.codegen.Parameter;
import org.jboss.errai.codegen.Statement;
import org.jboss.errai.codegen.Variable;
import org.jboss.errai.codegen.builder.AnonymousClassStructureBuilder;
import org.jboss.errai.codegen.builder.BlockBuilder;
import org.jboss.errai.codegen.builder.BuildCallback;
import org.jboss.errai.codegen.exception.UndefinedMethodException;
import org.jboss.errai.codegen.meta.MetaClass;
import org.jboss.errai.codegen.meta.MetaMethod;
import org.jboss.errai.codegen.meta.impl.build.BuildMetaClass;
import org.jboss.errai.codegen.util.GenUtil;
/**
* @author Mike Brock <cbrock@redhat.com>
* @author Christian Sadilek <csadilek@redhat.com>
*/
public class AnonymousClassStructureBuilderImpl
extends ClassBuilder<AnonymousClassStructureBuilder>
implements AnonymousClassStructureBuilder {
private final BuildCallback<ObjectBuilder> callback;
private final List<DeferredGenerateCallback> deferredGenerateCallbacks;
private final Context context;
AnonymousClassStructureBuilderImpl(final MetaClass clazz, final BuildCallback<ObjectBuilder> builderBuildCallback) {
super(clazz.getFullyQualifiedName(), clazz, builderBuildCallback.getParentContext());
this.callback = builderBuildCallback;
this.context = builderBuildCallback.getParentContext();
deferredGenerateCallbacks = new ArrayList<DeferredGenerateCallback>();
}
@Override
public BlockBuilder<AnonymousClassStructureBuilder> initialize() {
return new BlockBuilderImpl<AnonymousClassStructureBuilder>(
new BuildCallback<AnonymousClassStructureBuilder>() {
@Override
public AnonymousClassStructureBuilderImpl callback(final Statement statement) {
addCallable(context -> {
final StringBuilder buf = new StringBuilder(256);
buf.append("{\n");
if (statement != null) {
buf.append(statement.generate(Context.create(context))).append("\n");
}
buf.append("}\n");
return buf.toString();
});
return AnonymousClassStructureBuilderImpl.this;
}
@Override
public Context getParentContext() {
return context;
}
});
}
private BlockBuilder<AnonymousClassStructureBuilder> publicOverridesMethod(final MetaMethod method,
final DefParameters parameters) {
return new BlockBuilderImpl<AnonymousClassStructureBuilder>(
new BuildCallback<AnonymousClassStructureBuilder>() {
@Override
public AnonymousClassStructureBuilder callback(final Statement statement) {
addCallable(context -> {
final Context subContext = Context.create(context);
parameters.getParameters()
.forEach(p -> subContext.addVariable(Variable.create(p.getName(), p.getType())));
final StringBuilder buf = new StringBuilder(256);
final String returnType = getClassReference(method.getReturnType(), context);
buf.append("public ").append(returnType)
.append(" ")
.append(method.getName())
.append(parameters.generate(context)).append(" {\n");
if (statement != null) {
buf.append(statement.generate(subContext)).append("\n");
}
buf.append("}\n");
return buf.toString();
});
return AnonymousClassStructureBuilderImpl.this;
}
@Override
public Context getParentContext() {
return context;
}
});
}
@Override
public BlockBuilder<AnonymousClassStructureBuilder> publicOverridesMethod(final String name, final Parameter... args) {
final List<MetaClass> types = new ArrayList<MetaClass>();
Arrays.stream(args).forEach(a -> types.add(a.getType()));
final MetaMethod method = classDefinition.getSuperClass()
.getBestMatchingMethod(name, types.toArray(new MetaClass[args.length]));
if (method == null)
throw new UndefinedMethodException("Can't override (inherited method not found):"
+ classDefinition.getFullyQualifiedNameWithTypeParms() + "." + name + "(" + types + ")");
return publicOverridesMethod(method, DefParameters.fromParameters(args));
}
@Override
public ObjectBuilder finish() {
if (callback != null) {
return callback.callback(new AbstractStatement() {
@Override
public String generate(final Context context) {
context.attachClass(getClassDefinition());
return doGenerate(context);
}
});
}
return null;
}
private void addCallable(final DeferredGenerateCallback callable) {
deferredGenerateCallbacks.add(callable);
}
String generatedCache;
private String doGenerate(final Context context) {
if (generatedCache != null) return generatedCache;
try {
if (deferredGenerateCallbacks == null)
return null;
final Context subContext = Context.create(context);
classDefinition.getContext().getDeclaredVariables().forEach((v) -> subContext.addVariable(v));
subContext.addVariable(Variable.create("this", getClassDefinition()));
classDefinition.setContext(subContext);
final StringBuilder buf = new StringBuilder(256);
buf.append(classDefinition.membersToString().trim()).append("\n");
deferredGenerateCallbacks.forEach(c -> buf.append(c.doGenerate(subContext).trim()).append('\n'));
return generatedCache = buf.toString().trim();
}
catch (final Exception e) {
GenUtil.throwIfUnhandled("while generating: " + classDefinition.getFullyQualifiedName(), e);
return null;
}
}
public static interface DeferredGenerateCallback {
public String doGenerate(Context context);
}
@Override
public BuildMetaClass getClassDefinition() {
return classDefinition;
}
}