/*
* 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 java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.enterprise.util.TypeLiteral;
import org.jboss.errai.codegen.BooleanExpression;
import org.jboss.errai.codegen.Cast;
import org.jboss.errai.codegen.Comment;
import org.jboss.errai.codegen.Context;
import org.jboss.errai.codegen.Statement;
import org.jboss.errai.codegen.Variable;
import org.jboss.errai.codegen.builder.ArrayInitializationBuilder;
import org.jboss.errai.codegen.builder.BlockBuilder;
import org.jboss.errai.codegen.builder.CaseBlockBuilder;
import org.jboss.errai.codegen.builder.CatchBlockBuilder;
import org.jboss.errai.codegen.builder.ContextualStatementBuilder;
import org.jboss.errai.codegen.builder.ElseBlockBuilder;
import org.jboss.errai.codegen.builder.StatementBegin;
import org.jboss.errai.codegen.builder.StatementEnd;
import org.jboss.errai.codegen.builder.VariableDeclarationInitializer;
import org.jboss.errai.codegen.builder.VariableDeclarationNamed;
import org.jboss.errai.codegen.builder.VariableDeclarationStart;
import org.jboss.errai.codegen.builder.VariableReferenceContextualStatementBuilder;
import org.jboss.errai.codegen.builder.WhileBuilder;
import org.jboss.errai.codegen.builder.callstack.BranchCallElement;
import org.jboss.errai.codegen.builder.callstack.DeclareVariable;
import org.jboss.errai.codegen.builder.callstack.DefineLabel;
import org.jboss.errai.codegen.builder.callstack.DynamicLoad;
import org.jboss.errai.codegen.builder.callstack.LoadClassReference;
import org.jboss.errai.codegen.builder.callstack.LoadField;
import org.jboss.errai.codegen.builder.callstack.LoadLiteral;
import org.jboss.errai.codegen.builder.callstack.LoadNested;
import org.jboss.errai.codegen.builder.callstack.LoadVariable;
import org.jboss.errai.codegen.builder.callstack.MethodCall;
import org.jboss.errai.codegen.builder.callstack.ResetCallElement;
import org.jboss.errai.codegen.builder.callstack.ThrowException;
import org.jboss.errai.codegen.control.branch.BreakStatement;
import org.jboss.errai.codegen.control.branch.ContinueStatement;
import org.jboss.errai.codegen.meta.MetaClass;
import org.jboss.errai.codegen.meta.MetaClassFactory;
/**
* The root of our fluent StatementBuilder API.
*
* @author Christian Sadilek <csadilek@redhat.com>
* @author Mike Brock <cbrock@redhat.com>
*/
public class StatementBuilder extends AbstractStatementBuilder implements StatementBegin {
private static final Pattern THIS_OR_SUPPER_PATTERN = Pattern.compile("(this|super)");
private static final Pattern THIS_PATTERN = Pattern.compile("(this\\.)(.)*");
public StatementBuilder(final Context context) {
super(context);
if (context != null) {
context.getDeclaredVariables().stream()
.filter(v -> !THIS_OR_SUPPER_PATTERN.matcher(v.getName()).matches())
.forEach(v -> appendCallElement(new DeclareVariable(v)));
}
appendCallElement(new ResetCallElement());
}
public static StatementBegin create() {
return new StatementBuilder(null);
}
public static StatementBegin create(final Context context) {
return new StatementBuilder(context);
}
@Override
public VariableDeclarationStart declareVariable(final Class<?> type) {
return declareVariable(MetaClassFactory.get(type));
}
@Override
public VariableDeclarationStart declareVariable(final MetaClass type) {
return new VariableDeclarationStart<StatementBuilder>() {
boolean isFinal;
String name;
Object initialization;
@Override
public VariableDeclarationNamed<StatementBuilder> asFinal() {
isFinal = true;
return this;
}
@Override
public VariableDeclarationInitializer<StatementBuilder> named(final String name) {
this.name = name;
return this;
}
@Override
public StatementBuilder initializeWith(final Object initialization) {
this.initialization = initialization;
return finish();
}
@Override
public StatementBuilder initializeWith(final Statement initialization) {
this.initialization = initialization;
return finish();
}
@Override
public StatementBuilder finish() {
if (initialization == null) {
declareVariable(isFinal ? Variable.createFinal(name, type) : Variable.create(name, type));
}
else {
declareVariable(isFinal ? Variable.createFinal(name, type, initialization) : Variable.create(name, type,
initialization));
}
return StatementBuilder.this;
}
};
}
@Override
public StatementBuilder declareVariable(final String name, final Class<?> type) {
return declareVariable(Variable.create(name, type));
}
@Override
public StatementBuilder declareVariable(final String name, final TypeLiteral<?> type) {
return declareVariable(Variable.create(name, type));
}
@Override
public StatementBuilder declareVariable(final String name, final Object initialization) {
return declareVariable(Variable.create(name, initialization));
}
@Override
public StatementBuilder declareVariable(final String name, final MetaClass type, final Object initialization) {
return declareVariable(Variable.create(name, type, initialization));
}
@Override
public StatementBuilder declareVariable(final String name, final Class<?> type, final Object initialization) {
return declareVariable(Variable.create(name, type, initialization));
}
@Override
public StatementBuilder declareVariable(final String name, final TypeLiteral<?> type, final Object initialization) {
return declareVariable(Variable.create(name, type, initialization));
}
@Override
public StatementBuilder declareFinalVariable(final String name, final Class<?> type) {
return declareVariable(Variable.createFinal(name, type));
}
@Override
public StatementBuilder declareFinalVariable(final String name, final TypeLiteral<?> type) {
return declareVariable(Variable.createFinal(name, type));
}
@Override
public StatementBuilder declareFinalVariable(final String name, final MetaClass type, final Object initialization) {
return declareVariable(Variable.createFinal(name, type, initialization));
}
@Override
public StatementBuilder declareFinalVariable(final String name, final Class<?> type, final Object initialization) {
return declareVariable(Variable.createFinal(name, type, initialization));
}
@Override
public StatementBuilder declareFinalVariable(final String name, final TypeLiteral<?> type, final Object initialization) {
return declareVariable(Variable.createFinal(name, type, initialization));
}
private StatementBuilder declareVariable(final Variable v) {
appendCallElement(new DeclareVariable(v));
return this;
}
@Override
public VariableReferenceContextualStatementBuilder loadVariable(final String name, final Object... indexes) {
final Matcher m = THIS_PATTERN.matcher(name);
if (m.matches()) {
return loadClassMember(name.replaceFirst("(this\\.)", ""), indexes);
}
appendCallElement(new LoadVariable(name, indexes));
return new ContextualStatementBuilderImpl(context, callElementBuilder);
}
@Override
public VariableReferenceContextualStatementBuilder loadClassMember(final String name, final Object... indexes) {
appendCallElement(new LoadVariable(name, true, indexes));
return new ContextualStatementBuilderImpl(context, callElementBuilder);
}
@Override
public ContextualStatementBuilder loadLiteral(final Object o) {
appendCallElement(new LoadLiteral(o));
return new ContextualStatementBuilderImpl(context, callElementBuilder);
}
@Override
public ContextualStatementBuilder load(final Object o) {
appendCallElement(new DynamicLoad(o));
return new ContextualStatementBuilderImpl(context, callElementBuilder);
}
@Override
public ContextualStatementBuilder loadClassReference(final Object o) {
final MetaClass c;
if (o instanceof MetaClass) {
c = (MetaClass) o;
}
else if (o instanceof Class) {
c = MetaClassFactory.get((Class) o);
}
else {
throw new RuntimeException("unknown class reference type: " + (o == null ? "null" : o.getClass().getName()));
}
appendCallElement(new LoadClassReference(c));
return new ContextualStatementBuilderImpl(context, callElementBuilder);
}
@Override
public ContextualStatementBuilder invokeStatic(final MetaClass clazz, final String methodName, final Object... parameters) {
appendCallElement(new LoadClassReference(clazz));
appendCallElement(new MethodCall(methodName, parameters, true));
return new ContextualStatementBuilderImpl(context, callElementBuilder);
}
@Override
public ContextualStatementBuilder invokeStatic(final Class<?> clazz, final String methodName, final Object... parameters) {
return invokeStatic(MetaClassFactory.get(clazz), methodName, parameters);
}
@Override
public ContextualStatementBuilder loadStatic(final Class<?> clazz, final String fieldName) {
return loadStatic(MetaClassFactory.get(clazz), fieldName);
}
@Override
public ContextualStatementBuilder loadStatic(final MetaClass clazz, final String fieldName) {
appendCallElement(new LoadClassReference(clazz));
appendCallElement(new LoadField(fieldName));
return new ContextualStatementBuilderImpl(context, callElementBuilder);
}
@Override
public ContextualStatementBuilder nestedCall(final Statement statement) {
appendCallElement(new LoadNested(statement));
return new ContextualStatementBuilderImpl(context, callElementBuilder);
}
@Override
public ObjectBuilder newObject(final Class<?> type) {
return ObjectBuilder.newInstanceOf(type, context, callElementBuilder);
}
@Override
public ObjectBuilder newObject(final MetaClass type) {
return ObjectBuilder.newInstanceOf(type, context, callElementBuilder);
}
@Override
public ObjectBuilder newObject(final TypeLiteral<?> type) {
return ObjectBuilder.newInstanceOf(type, context, callElementBuilder);
}
@Override
public Statement newObject(final Class<?> type, final Object... parameters) {
return ObjectBuilder.newInstanceOf(type, context, callElementBuilder)
.withParameters(parameters);
}
@Override
public Statement newObject(final MetaClass type, final Object... parameters) {
return ObjectBuilder.newInstanceOf(type, context, callElementBuilder)
.withParameters(parameters);
}
@Override
public Statement newObject(final TypeLiteral<?> type, final Object... parameters) {
return ObjectBuilder.newInstanceOf(type, context, callElementBuilder)
.withParameters(parameters);
}
@Override
public ArrayInitializationBuilder newArray(final MetaClass componentType, final Object... dimensions) {
return new ArrayBuilderImpl(context, callElementBuilder).newArray(componentType, dimensions);
}
@Override
public ArrayInitializationBuilder newArray(final Class<?> componentType, final Object... dimensions) {
return new ArrayBuilderImpl(context, callElementBuilder).newArray(componentType, dimensions);
}
@Override
public BlockBuilder<WhileBuilder> do_() {
return new LoopBuilderImpl(context, callElementBuilder).do_();
}
@Override
public BlockBuilder<ElseBlockBuilder> if_(final BooleanExpression stmt) {
return new IfBlockBuilderImpl(context, callElementBuilder).if_(stmt);
}
@Override
public BlockBuilder<StatementEnd> while_(final BooleanExpression stmt) {
return new LoopBuilderImpl(context, callElementBuilder).while_(stmt);
}
@Override
public BlockBuilder<StatementEnd> for_(final BooleanExpression condition) {
return new LoopBuilderImpl(context, callElementBuilder).for_(condition);
}
@Override
public BlockBuilder<StatementEnd> for_(final Statement initializer, final BooleanExpression condition) {
return new LoopBuilderImpl(context, callElementBuilder).for_(initializer, condition);
}
@Override
public BlockBuilder<StatementEnd> for_(final Statement initializer, final BooleanExpression condition,
final Statement countingExpression) {
return new LoopBuilderImpl(context, callElementBuilder).for_(initializer, condition, countingExpression);
}
@Override
public CaseBlockBuilder switch_(final Statement statement) {
return new SwitchBlockBuilderImpl(context, callElementBuilder).switch_(statement);
}
@Override
public BlockBuilder<CatchBlockBuilder> try_() {
return new TryBlockBuilderImpl(context, callElementBuilder).try_();
}
@Override
public StatementEnd throw_(final Class<? extends Throwable> throwableType, final Object... parameters) {
appendCallElement(new ThrowException(throwableType, parameters));
return this;
}
@Override
public StatementEnd throw_(final String exceptionVarName) {
appendCallElement(new ThrowException(exceptionVarName));
return this;
}
@Override
public StatementEnd label(final String label) {
appendCallElement(new DefineLabel(label));
return this;
}
@Override
public StatementEnd break_() {
appendCallElement(new BranchCallElement(new BreakStatement()));
return this;
}
@Override
public StatementEnd break_(final String label) {
appendCallElement(new BranchCallElement(new BreakStatement(label)));
return this;
}
@Override
public StatementEnd continue_() {
appendCallElement(new BranchCallElement(new ContinueStatement()));
return this;
}
@Override
public StatementEnd continue_(final String label) {
appendCallElement(new BranchCallElement(new ContinueStatement(label)));
return this;
}
@Override
public StatementEnd returnVoid() {
return new StatementEnd() {
@Override
public String toJavaString() {
return "return";
}
@Override
public String generate(final Context context) {
return toJavaString();
}
@Override
public MetaClass getType() {
return MetaClassFactory.get(void.class);
}
};
}
@Override
public ContextualStatementBuilder castTo(final Class<?> type, final Statement statement) {
return nestedCall(Cast.to(type, statement));
}
@Override
public ContextualStatementBuilder castTo(final MetaClass type, final Statement statement) {
return nestedCall(Cast.to(type, statement));
}
@Override
public Statement codeComment(final String comment) {
return new Comment(comment);
}
}