use of org.codehaus.groovy.ast.InnerClassNode in project groovy by apache.
the class EnumVisitor method addInit.
private void addInit(final ClassNode enumClass, final FieldNode minValue, final FieldNode maxValue, final FieldNode values, final boolean isAIC) {
// constructor helper
// This method is used instead of calling the constructor as
// calling the constructor may require a table with MetaClass
// selecting the constructor for each enum value. So instead we
// use this method to have a central point for constructor selection
// and only one table. The whole construction is needed because
// Reflection forbids access to the enum constructor.
// code:
// def $INIT(Object[] para) {
// return this(*para)
// }
ClassNode enumRef = enumClass.getPlainNodeReference();
Parameter[] parameter = new Parameter[] { new Parameter(ClassHelper.OBJECT_TYPE.makeArray(), "para") };
MethodNode initMethod = new MethodNode("$INIT", ACC_FINAL | ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC, enumRef, parameter, ClassNode.EMPTY_ARRAY, null);
initMethod.setSynthetic(true);
ConstructorCallExpression cce = new ConstructorCallExpression(ClassNode.THIS, new ArgumentListExpression(new SpreadExpression(new VariableExpression("para"))));
BlockStatement code = new BlockStatement();
code.addStatement(new ReturnStatement(cce));
initMethod.setCode(code);
addGeneratedMethod(enumClass, initMethod);
// static init
List<FieldNode> fields = enumClass.getFields();
List<Expression> arrayInit = new ArrayList<>();
int value = -1;
Token assign = Token.newSymbol(Types.ASSIGN, -1, -1);
List<Statement> block = new ArrayList<>();
FieldNode tempMin = null;
FieldNode tempMax = null;
for (FieldNode field : fields) {
if (!field.isEnum())
continue;
value += 1;
if (tempMin == null)
tempMin = field;
tempMax = field;
ClassNode enumBase = enumClass;
ArgumentListExpression args = new ArgumentListExpression();
args.addExpression(new ConstantExpression(field.getName()));
args.addExpression(new ConstantExpression(value));
if (field.getInitialExpression() == null) {
if (enumClass.isAbstract()) {
addError(field, "The enum constant " + field.getName() + " must override abstract methods from " + enumBase.getName() + ".");
continue;
}
} else {
ListExpression oldArgs = (ListExpression) field.getInitialExpression();
List<MapEntryExpression> savedMapEntries = new ArrayList<>();
for (Expression exp : oldArgs.getExpressions()) {
if (exp instanceof MapEntryExpression) {
savedMapEntries.add((MapEntryExpression) exp);
continue;
}
InnerClassNode inner = null;
if (exp instanceof ClassExpression) {
ClassExpression clazzExp = (ClassExpression) exp;
ClassNode ref = clazzExp.getType();
if (ref instanceof EnumConstantClassNode) {
inner = (InnerClassNode) ref;
}
}
if (inner != null) {
List<MethodNode> baseMethods = enumBase.getMethods();
for (MethodNode methodNode : baseMethods) {
if (!methodNode.isAbstract())
continue;
MethodNode enumConstMethod = inner.getMethod(methodNode.getName(), methodNode.getParameters());
if (enumConstMethod == null || enumConstMethod.isAbstract()) {
addError(field, "Can't have an abstract method in enum constant " + field.getName() + ". Implement method '" + methodNode.getTypeDescriptor() + "'.");
}
}
if (inner.getVariableScope() == null) {
enumBase = inner;
/*
* GROOVY-3985: Remove the final modifier from $INIT method in this case
* so that subclasses of enum generated for enum constants (aic) can provide
* their own $INIT method
*/
initMethod.setModifiers(initMethod.getModifiers() & ~ACC_FINAL);
continue;
}
}
args.addExpression(exp);
}
if (!savedMapEntries.isEmpty()) {
args.getExpressions().add(2, new MapExpression(savedMapEntries));
}
}
field.setInitialValueExpression(null);
block.add(new ExpressionStatement(new BinaryExpression(new FieldExpression(field), assign, new StaticMethodCallExpression(enumBase, "$INIT", args))));
arrayInit.add(new FieldExpression(field));
}
if (!isAIC) {
if (tempMin != null) {
block.add(new ExpressionStatement(new BinaryExpression(new FieldExpression(minValue), assign, new FieldExpression(tempMin))));
block.add(new ExpressionStatement(new BinaryExpression(new FieldExpression(maxValue), assign, new FieldExpression(tempMax))));
enumClass.addField(minValue);
enumClass.addField(maxValue);
}
block.add(new ExpressionStatement(new BinaryExpression(new FieldExpression(values), assign, new ArrayExpression(enumClass, arrayInit))));
enumClass.addField(values);
}
enumClass.addStaticInitializerStatements(block, true);
}
use of org.codehaus.groovy.ast.InnerClassNode in project groovy by apache.
the class AstBuilder method visitEnumConstant.
@Override
public FieldNode visitEnumConstant(final EnumConstantContext ctx) {
ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE);
Objects.requireNonNull(classNode, "classNode should not be null");
InnerClassNode anonymousInnerClassNode = null;
if (asBoolean(ctx.anonymousInnerClassDeclaration())) {
ctx.anonymousInnerClassDeclaration().putNodeMetaData(ANONYMOUS_INNER_CLASS_SUPER_CLASS, classNode);
anonymousInnerClassNode = this.visitAnonymousInnerClassDeclaration(ctx.anonymousInnerClassDeclaration());
}
FieldNode enumConstant = EnumHelper.addEnumConstant(classNode, this.visitIdentifier(ctx.identifier()), createEnumConstantInitExpression(ctx.arguments(), anonymousInnerClassNode));
enumConstant.addAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt()));
groovydocManager.handle(enumConstant, ctx);
return configureAST(enumConstant, ctx);
}
use of org.codehaus.groovy.ast.InnerClassNode in project gradle by gradle.
the class GradleResolveVisitor method resolveNestedClass.
protected boolean resolveNestedClass(ClassNode type) {
if (type instanceof ConstructedNestedClass) {
return false;
}
// we have for example a class name A, are in class X
// and there is a nested class A$X. we want to be able
// to access that class directly, so A becomes a valid
// name in X.
// GROOVY-4043: Do this check up the hierarchy, if needed
Map<String, ClassNode> classHierarchy = new LinkedHashMap<String, ClassNode>();
ClassNode val;
for (ClassNode classToCheck = currentClass; /*
* We know that DefaultScript & friends don't have user-visible nested types,
* so don't try to look up nonsensical types like org.gradle.Script#sourceCompatibility
*/
classToCheck != null && classToCheck != ClassHelper.OBJECT_TYPE && !SCRIPTS_PACKAGE.equals(classToCheck.getPackageName()); classToCheck = classToCheck.getSuperClass()) {
if (classHierarchy.containsKey(classToCheck.getName())) {
break;
}
classHierarchy.put(classToCheck.getName(), classToCheck);
}
for (ClassNode classToCheck : classHierarchy.values()) {
val = new ConstructedNestedClass(classToCheck, type.getName());
if (resolveFromCompileUnit(val)) {
type.setRedirect(val);
return true;
}
// also check interfaces in case we have interfaces with nested classes
for (ClassNode next : classToCheck.getAllInterfaces()) {
if (type.getName().contains(next.getName())) {
continue;
}
val = new ConstructedNestedClass(next, type.getName());
if (resolve(val, false, false, false)) {
type.setRedirect(val);
return true;
}
}
}
// there is nothing to be done.
if (!(currentClass instanceof InnerClassNode)) {
return false;
}
// since we have B and want to get A we start with the most
// outer class, put them together and then see if that does
// already exist. In case of B from within A$B we are done
// after the first step already. In case of for example
// A.B.C.D.E.F and accessing E from F we test A$E=failed,
// A$B$E=failed, A$B$C$E=fail, A$B$C$D$E=success
LinkedList<ClassNode> outerClasses = new LinkedList<ClassNode>();
ClassNode outer = currentClass.getOuterClass();
while (outer != null) {
outerClasses.addFirst(outer);
outer = outer.getOuterClass();
}
// most outer class is now element 0
for (ClassNode testNode : outerClasses) {
val = new ConstructedNestedClass(testNode, type.getName());
if (resolveFromCompileUnit(val)) {
type.setRedirect(val);
return true;
}
// also check interfaces in case we have interfaces with nested classes
for (ClassNode next : testNode.getAllInterfaces()) {
if (type.getName().contains(next.getName())) {
continue;
}
val = new ConstructedNestedClass(next, type.getName());
if (resolve(val, false, false, false)) {
type.setRedirect(val);
return true;
}
}
}
return false;
}
Aggregations