/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.codehaus.groovy.macro.transform;
import org.codehaus.groovy.ast.*;
import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.tools.GeneralUtils;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.macro.methods.MacroGroovyMethods;
import org.codehaus.groovy.macro.runtime.MacroBuilder;
import org.codehaus.groovy.transform.GroovyASTTransformation;
import java.util.Iterator;
import java.util.List;
import static org.codehaus.groovy.ast.tools.GeneralUtils.*;
/**
* Transforms {@link MacroClass} calls into it's ClassNode
*
* @since 2.5.0
*/
@GroovyASTTransformation(phase = CompilePhase.CONVERSION)
public class MacroClassTransformation extends MethodCallTransformation {
private static final String MACRO_METHOD = "macro";
private static final ClassNode MACROCLASS_TYPE = ClassHelper.make(MacroClass.class);
@Override
protected GroovyCodeVisitor getTransformer(final ASTNode[] nodes, final SourceUnit sourceUnit) {
ClassCodeExpressionTransformer transformer = new MacroClassTransformer(sourceUnit);
return new MacroClassTransformingCodeVisitor(transformer, sourceUnit);
}
private static class MacroClassTransformer extends ClassCodeExpressionTransformer {
private final SourceUnit sourceUnit;
public MacroClassTransformer(SourceUnit sourceUnit) {
this.sourceUnit = sourceUnit;
}
@Override
protected SourceUnit getSourceUnit() {
return sourceUnit;
}
@Override
public Expression transform(final Expression exp) {
if (exp instanceof ConstructorCallExpression) {
MethodCallExpression call = exp.getNodeMetaData(MacroTransformation.class);
if (call != null) {
return call;
}
}
return super.transform(exp);
}
}
private static class MacroClassTransformingCodeVisitor extends TransformingCodeVisitor {
private final SourceUnit sourceUnit;
public MacroClassTransformingCodeVisitor(ClassCodeExpressionTransformer transformer, SourceUnit sourceUnit) {
super(transformer);
this.sourceUnit = sourceUnit;
}
@Override
public void visitConstructorCallExpression(final ConstructorCallExpression call) {
ClassNode type = call.getType();
if (type instanceof InnerClassNode) {
if (((InnerClassNode) type).isAnonymous() &&
MACROCLASS_TYPE.getNameWithoutPackage().equals(type.getSuperClass().getNameWithoutPackage())) {
try {
String source = convertInnerClassToSource(type);
MethodCallExpression macroCall = callX(
propX(classX(ClassHelper.makeWithoutCaching(MacroBuilder.class, false)), "INSTANCE"),
MACRO_METHOD,
args(
constX(source),
MacroGroovyMethods.buildSubstitutions(sourceUnit, type),
classX(ClassHelper.make(ClassNode.class))
)
);
macroCall.setSpreadSafe(false);
macroCall.setSafe(false);
macroCall.setImplicitThis(false);
call.putNodeMetaData(MacroTransformation.class, macroCall);
List<ClassNode> classes = sourceUnit.getAST().getClasses();
for (Iterator<ClassNode> iterator = classes.iterator(); iterator.hasNext(); ) {
final ClassNode aClass = iterator.next();
if (aClass == type || type == aClass.getOuterClass()) {
iterator.remove();
}
}
} catch (Exception e) {
// FIXME
e.printStackTrace();
}
return;
}
}
super.visitConstructorCallExpression(call);
}
private String convertInnerClassToSource(final ClassNode type) throws Exception {
String source = GeneralUtils.convertASTToSource(sourceUnit.getSource(), type);
// we need to remove the leading "{" and trailing "}"
source = source.substring(source.indexOf('{') + 1, source.lastIndexOf('}') - 1);
return source;
}
}
}