You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2017/12/14 21:57:01 UTC
[36/57] [abbrv] [partial] groovy git commit: Move Java source set
into `src/main/java`
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/ast/FieldNode.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/FieldNode.java b/src/main/java/org/codehaus/groovy/ast/FieldNode.java
new file mode 100644
index 0000000..d38f4f4
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/ast/FieldNode.java
@@ -0,0 +1,205 @@
+/*
+ * 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.ast;
+
+import groovy.lang.groovydoc.Groovydoc;
+import groovy.lang.groovydoc.GroovydocHolder;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.objectweb.asm.Opcodes;
+
+import java.lang.reflect.Field;
+
+/**
+ * Represents a field (member variable)
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class FieldNode extends AnnotatedNode implements Opcodes, Variable, GroovydocHolder<FieldNode> {
+
+ private String name;
+ private int modifiers;
+ private ClassNode type;
+ private ClassNode owner;
+ private Expression initialValueExpression;
+ private boolean dynamicTyped;
+ private boolean holder;
+ private ClassNode originType = ClassHelper.DYNAMIC_TYPE;
+
+ public static FieldNode newStatic(Class theClass, String name) throws SecurityException, NoSuchFieldException {
+ Field field = theClass.getField(name);
+ ClassNode fldType = ClassHelper.make(field.getType());
+ return new FieldNode(name, ACC_PUBLIC | ACC_STATIC, fldType, ClassHelper.make(theClass), null);
+ }
+
+ public FieldNode(String name, int modifiers, ClassNode type, ClassNode owner, Expression initialValueExpression) {
+ this.name = name;
+ this.modifiers = modifiers;
+ this.type = type;
+ if (this.type == ClassHelper.DYNAMIC_TYPE && initialValueExpression != null)
+ this.setType(initialValueExpression.getType());
+ this.setType(type);
+ this.originType = type;
+ this.owner = owner;
+ this.initialValueExpression = initialValueExpression;
+ }
+
+ public Expression getInitialExpression() {
+ return initialValueExpression;
+ }
+
+ public int getModifiers() {
+ return modifiers;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public ClassNode getType() {
+ return type;
+ }
+
+ public void setType(ClassNode type) {
+ this.type = type;
+ this.originType = type;
+ dynamicTyped |= type == ClassHelper.DYNAMIC_TYPE;
+ }
+
+ public ClassNode getOwner() {
+ return owner;
+ }
+
+ public boolean isHolder() {
+ return holder;
+ }
+
+ public void setHolder(boolean holder) {
+ this.holder = holder;
+ }
+
+ public boolean isDynamicTyped() {
+ return dynamicTyped;
+ }
+
+ public void setModifiers(int modifiers) {
+ this.modifiers = modifiers;
+ }
+
+ /**
+ * @return true if the field is static
+ */
+ public boolean isStatic() {
+ return (modifiers & ACC_STATIC) != 0;
+ }
+
+ /**
+ * @return true if the field is an enum
+ */
+ public boolean isEnum() {
+ return (modifiers & ACC_ENUM) != 0;
+ }
+
+ /**
+ * @return true if the field is final
+ */
+ public boolean isFinal() {
+ return (modifiers & ACC_FINAL) != 0;
+ }
+
+ /**
+ * @return true if the field is volatile
+ */
+ public boolean isVolatile() {
+ return (modifiers & ACC_VOLATILE) != 0;
+ }
+
+ /**
+ * @return true if the field is public
+ */
+ public boolean isPublic() {
+ return (modifiers & ACC_PUBLIC) != 0;
+ }
+
+ /**
+ * @return true if the field is protected
+ */
+ public boolean isProtected() {
+ return (modifiers & ACC_PROTECTED) != 0;
+ }
+
+ /**
+ * @param owner The owner to set.
+ */
+ public void setOwner(ClassNode owner) {
+ this.owner = owner;
+ }
+
+ public boolean hasInitialExpression() {
+ return initialValueExpression != null;
+ }
+
+ public boolean isInStaticContext() {
+ return isStatic();
+ }
+
+ public Expression getInitialValueExpression() {
+ return initialValueExpression;
+ }
+
+ public void setInitialValueExpression(Expression initialValueExpression) {
+ this.initialValueExpression = initialValueExpression;
+ }
+
+ /**
+ * @deprecated
+ */
+ @Deprecated
+ public boolean isClosureSharedVariable() {
+ return false;
+ }
+ /**
+ * @deprecated
+ */
+ @Deprecated
+ public void setClosureSharedVariable(boolean inClosure) {
+ }
+
+ public ClassNode getOriginType() {
+ return originType;
+ }
+
+ public void setOriginType(ClassNode cn) {
+ originType = cn;
+ }
+
+ public void rename(String name) {
+ declaringClass.renameField(this.name, name);
+ this.name = name;
+ }
+
+ @Override
+ public Groovydoc getGroovydoc() {
+ return this.<Groovydoc>getNodeMetaData(DOC_COMMENT);
+ }
+
+ @Override
+ public FieldNode getInstance() {
+ return this;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/ast/GenericsType.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
new file mode 100644
index 0000000..57d4919
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
@@ -0,0 +1,500 @@
+/*
+ * 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.ast;
+
+import org.codehaus.groovy.ast.tools.GenericsUtils;
+import org.codehaus.groovy.ast.tools.WideningCategories;
+
+import java.lang.reflect.Modifier;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.codehaus.groovy.ast.ClassHelper.GROOVY_OBJECT_TYPE;
+
+/**
+ * This class is used to describe generic type signatures for ClassNodes.
+ *
+ * @author Jochen Theodorou
+ * @see ClassNode
+ */
+public class GenericsType extends ASTNode {
+ public static final GenericsType[] EMPTY_ARRAY = new GenericsType[0];
+
+ private final ClassNode[] upperBounds;
+ private final ClassNode lowerBound;
+ private ClassNode type;
+ private String name;
+ private boolean placeholder;
+ private boolean resolved;
+ private boolean wildcard;
+
+ public GenericsType(ClassNode type, ClassNode[] upperBounds, ClassNode lowerBound) {
+ this.type = type;
+ this.name = type.isGenericsPlaceHolder() ? type.getUnresolvedName() : type.getName();
+ this.upperBounds = upperBounds;
+ this.lowerBound = lowerBound;
+ placeholder = type.isGenericsPlaceHolder();
+ resolved = false;
+ }
+
+ public GenericsType(ClassNode basicType) {
+ this(basicType, null, null);
+ }
+
+ public ClassNode getType() {
+ return type;
+ }
+
+ public void setType(ClassNode type) {
+ this.type = type;
+ }
+
+ public String toString() {
+ Set<String> visited = new HashSet<String>();
+ return toString(visited);
+ }
+
+ private String toString(Set<String> visited) {
+ if (placeholder) visited.add(name);
+ StringBuilder ret = new StringBuilder(wildcard ? "?" : ((type == null || placeholder) ? name : genericsBounds(type, visited)));
+ if (upperBounds != null) {
+ if (placeholder && upperBounds.length==1 && !upperBounds[0].isGenericsPlaceHolder() && upperBounds[0].getName().equals("java.lang.Object")) {
+ // T extends Object should just be printed as T
+ } else {
+ ret.append(" extends ");
+ for (int i = 0; i < upperBounds.length; i++) {
+ ret.append(genericsBounds(upperBounds[i], visited));
+ if (i + 1 < upperBounds.length) ret.append(" & ");
+ }
+ }
+ } else if (lowerBound != null) {
+ ret.append(" super ").append(genericsBounds(lowerBound, visited));
+ }
+ return ret.toString();
+ }
+
+ private String nameOf(ClassNode theType) {
+ StringBuilder ret = new StringBuilder();
+ if (theType.isArray()) {
+ ret.append(nameOf(theType.getComponentType()));
+ ret.append("[]");
+ } else {
+ ret.append(theType.getName());
+ }
+ return ret.toString();
+ }
+
+ private String genericsBounds(ClassNode theType, Set<String> visited) {
+
+ StringBuilder ret = new StringBuilder();
+
+ if (theType.isArray()) {
+ ret.append(nameOf(theType));
+ } else if (theType.redirect() instanceof InnerClassNode) {
+ InnerClassNode innerClassNode = (InnerClassNode) theType.redirect();
+ String parentClassNodeName = innerClassNode.getOuterClass().getName();
+ if (Modifier.isStatic(innerClassNode.getModifiers()) || innerClassNode.isInterface()) {
+ ret.append(innerClassNode.getOuterClass().getName());
+ } else {
+ ret.append(genericsBounds(innerClassNode.getOuterClass(), new HashSet<String>()));
+ }
+ ret.append(".");
+ String typeName = theType.getName();
+ ret.append(typeName.substring(parentClassNodeName.length() + 1));
+ } else {
+ ret.append(theType.getName());
+ }
+
+ GenericsType[] genericsTypes = theType.getGenericsTypes();
+ if (genericsTypes == null || genericsTypes.length == 0)
+ return ret.toString();
+
+ // TODO instead of catching Object<T> here stop it from being placed into type in first place
+ if (genericsTypes.length == 1 && genericsTypes[0].isPlaceholder() && theType.getName().equals("java.lang.Object")) {
+ return genericsTypes[0].getName();
+ }
+
+ ret.append("<");
+ for (int i = 0; i < genericsTypes.length; i++) {
+ if (i != 0) ret.append(", ");
+
+ GenericsType type = genericsTypes[i];
+ if (type.isPlaceholder() && visited.contains(type.getName())) {
+ ret.append(type.getName());
+ }
+ else {
+ ret.append(type.toString(visited));
+ }
+ }
+ ret.append(">");
+
+ return ret.toString();
+ }
+
+ public ClassNode[] getUpperBounds() {
+ return upperBounds;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isPlaceholder() {
+ return placeholder;
+ }
+
+ public void setPlaceholder(boolean placeholder) {
+ this.placeholder = placeholder;
+ type.setGenericsPlaceHolder(placeholder);
+ }
+
+ public boolean isResolved() {
+ return resolved || placeholder;
+ }
+
+ public void setResolved(boolean res) {
+ resolved = res;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean isWildcard() {
+ return wildcard;
+ }
+
+ public void setWildcard(boolean wildcard) {
+ this.wildcard = wildcard;
+ }
+
+ public ClassNode getLowerBound() {
+ return lowerBound;
+ }
+
+ /**
+ * Tells if the provided class node is compatible with this generic type definition
+ * @param classNode the class node to be checked
+ * @return true if the class node is compatible with this generics type definition
+ */
+ public boolean isCompatibleWith(ClassNode classNode) {
+ return new GenericsTypeMatcher().matches(classNode);
+ }
+
+ /**
+ * Implements generics type comparison.
+ */
+ private class GenericsTypeMatcher {
+
+ public boolean implementsInterfaceOrIsSubclassOf(ClassNode type, ClassNode superOrInterface) {
+ boolean result = type.equals(superOrInterface)
+ || type.isDerivedFrom(superOrInterface)
+ || type.implementsInterface(superOrInterface);
+ if (result) {
+ return true;
+ }
+ if (GROOVY_OBJECT_TYPE.equals(superOrInterface) && type.getCompileUnit()!=null) {
+ // type is being compiled so it will implement GroovyObject later
+ return true;
+ }
+ if (superOrInterface instanceof WideningCategories.LowestUpperBoundClassNode) {
+ WideningCategories.LowestUpperBoundClassNode cn = (WideningCategories.LowestUpperBoundClassNode) superOrInterface;
+ result = implementsInterfaceOrIsSubclassOf(type, cn.getSuperClass());
+ if (result) {
+ for (ClassNode interfaceNode : cn.getInterfaces()) {
+ result = implementsInterfaceOrIsSubclassOf(type,interfaceNode);
+ if (!result) break;
+ }
+ }
+ if (result) return true;
+ }
+ if (type.isArray() && superOrInterface.isArray()) {
+ return implementsInterfaceOrIsSubclassOf(type.getComponentType(), superOrInterface.getComponentType());
+ }
+ return false;
+ }
+
+ /**
+ * Compares this generics type with the one represented by the provided class node. If the provided
+ * classnode is compatible with the generics specification, returns true. Otherwise, returns false.
+ * The check is complete, meaning that we also check "nested" generics.
+ * @param classNode the classnode to be checked
+ * @return true iff the classnode is compatible with this generics specification
+ */
+ public boolean matches(ClassNode classNode) {
+ GenericsType[] genericsTypes = classNode.getGenericsTypes();
+ // diamond always matches
+ if (genericsTypes!=null && genericsTypes.length==0) return true;
+ if (classNode.isGenericsPlaceHolder()) {
+ // if the classnode we compare to is a generics placeholder (like <E>) then we
+ // only need to check that the names are equal
+ if (genericsTypes==null) return true;
+ if (isWildcard()) {
+ if (lowerBound!=null) return genericsTypes[0].getName().equals(lowerBound.getUnresolvedName());
+ if (upperBounds!=null) {
+ for (ClassNode upperBound : upperBounds) {
+ String name = upperBound.getGenericsTypes()[0].getName();
+ if (genericsTypes[0].getName().equals(name)) return true;
+ }
+ return false;
+ }
+ }
+ return genericsTypes[0].getName().equals(name);
+ }
+ if (wildcard || placeholder) {
+ // if the current generics spec is a wildcard spec or a placeholder spec
+ // then we must check upper and lower bounds
+ if (upperBounds != null) {
+ // check that the provided classnode is a subclass of all provided upper bounds
+ boolean upIsOk = true;
+ for (int i = 0, upperBoundsLength = upperBounds.length; i < upperBoundsLength && upIsOk; i++) {
+ final ClassNode upperBound = upperBounds[i];
+ upIsOk = implementsInterfaceOrIsSubclassOf(classNode, upperBound);
+ }
+ // if the provided classnode is a subclass of the upper bound
+ // then check that the generic types supplied by the class node are compatible with
+ // this generics specification
+ // for example, we could have the spec saying List<String> but provided classnode
+ // saying List<Integer>
+ upIsOk = upIsOk && checkGenerics(classNode);
+ return upIsOk;
+ }
+ if (lowerBound != null) {
+ // if a lower bound is declared, then we must perform the same checks that for an upper bound
+ // but with reversed arguments
+ return implementsInterfaceOrIsSubclassOf(lowerBound, classNode) && checkGenerics(classNode);
+ }
+ // If there are no bounds, the generic type is basically Object, and everything is compatible.
+ return true;
+ }
+ // if this is not a generics placeholder, first compare that types represent the same type
+ if ((type!=null && !type.equals(classNode))) {
+ return false;
+ }
+ // last, we could have the spec saying List<String> and a classnode saying List<Integer> so
+ // we must check that generics are compatible.
+ // The null check is normally not required but done to prevent from NPEs
+ return type == null || compareGenericsWithBound(classNode, type);
+ }
+
+ /**
+ * Iterates over each generics bound of this generics specification, and checks
+ * that the generics defined by the bound are compatible with the generics specified
+ * by the type.
+ * @param classNode the classnode the bounds should be compared with
+ * @return true if generics from bounds are compatible
+ */
+ private boolean checkGenerics(final ClassNode classNode) {
+ if (upperBounds!=null) {
+ for (ClassNode upperBound : upperBounds) {
+ if (!compareGenericsWithBound(classNode, upperBound)) return false;
+ }
+ }
+ if (lowerBound!=null) {
+ if (!lowerBound.redirect().isUsingGenerics()) {
+ if (!compareGenericsWithBound(classNode, lowerBound)) return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Given a parameterized type (List<String> for example), checks that its
+ * generic types are compatible with those from a bound.
+ * @param classNode the classnode from which we will compare generics types
+ * @param bound the bound to which the types will be compared
+ * @return true if generics are compatible
+ */
+ private boolean compareGenericsWithBound(final ClassNode classNode, final ClassNode bound) {
+ if (classNode==null) return false;
+ if (!bound.isUsingGenerics() || (classNode.getGenericsTypes()==null && classNode.redirect().getGenericsTypes()!=null)) {
+ // if the bound is not using generics, there's nothing to compare with
+ return true;
+ }
+ if (!classNode.equals(bound)) {
+ // the class nodes are on different types
+ // in this situation, we must choose the correct execution path : either the bound
+ // is an interface and we must find the implementing interface from the classnode
+ // to compare their parameterized generics, or the bound is a regular class and we
+ // must compare the bound with a superclass
+ if (bound.isInterface()) {
+ Set<ClassNode> interfaces = classNode.getAllInterfaces();
+ // iterate over all interfaces to check if any corresponds to the bound we are
+ // comparing to
+ for (ClassNode anInterface : interfaces) {
+ if (anInterface.equals(bound)) {
+ // when we obtain an interface, the types represented by the interface
+ // class node are not parameterized. This means that we must create a
+ // new class node with the parameterized types that the current class node
+ // has defined.
+ ClassNode node = GenericsUtils.parameterizeType(classNode, anInterface);
+ return compareGenericsWithBound(node, bound);
+ }
+ }
+ }
+ if (bound instanceof WideningCategories.LowestUpperBoundClassNode) {
+ // another special case here, where the bound is a "virtual" type
+ // we must then check the superclass and the interfaces
+ boolean success = compareGenericsWithBound(classNode, bound.getSuperClass());
+ if (success) {
+ ClassNode[] interfaces = bound.getInterfaces();
+ for (ClassNode anInterface : interfaces) {
+ success &= compareGenericsWithBound(classNode, anInterface);
+ if (!success) break;
+ }
+ if (success) return true;
+ }
+ }
+ return compareGenericsWithBound(getParameterizedSuperClass(classNode), bound);
+ }
+ GenericsType[] cnTypes = classNode.getGenericsTypes();
+ if (cnTypes==null && classNode.isRedirectNode()) cnTypes=classNode.redirect().getGenericsTypes();
+ if (cnTypes==null) {
+ // may happen if generic type is Foo<T extends Foo> and classnode is Foo -> Foo
+ return true;
+ }
+ GenericsType[] redirectBoundGenericTypes = bound.redirect().getGenericsTypes();
+ Map<String, GenericsType> classNodePlaceholders = GenericsUtils.extractPlaceholders(classNode);
+ Map<String, GenericsType> boundPlaceHolders = GenericsUtils.extractPlaceholders(bound);
+ boolean match = true;
+ for (int i = 0; redirectBoundGenericTypes!=null && i < redirectBoundGenericTypes.length && match; i++) {
+ GenericsType redirectBoundType = redirectBoundGenericTypes[i];
+ GenericsType classNodeType = cnTypes[i];
+ if (classNodeType.isPlaceholder()) {
+ String name = classNodeType.getName();
+ if (redirectBoundType.isPlaceholder()) {
+ match = name.equals(redirectBoundType.getName());
+ if (!match) {
+ GenericsType genericsType = boundPlaceHolders.get(redirectBoundType.getName());
+ match = false;
+ if (genericsType!=null) {
+ if (genericsType.isPlaceholder()) {
+ match = true;
+ } else if (genericsType.isWildcard()) {
+ if (genericsType.getUpperBounds()!=null) {
+ for (ClassNode up : genericsType.getUpperBounds()) {
+ match |= redirectBoundType.isCompatibleWith(up);
+ }
+ if (genericsType.getLowerBound()!=null) {
+ match |= redirectBoundType.isCompatibleWith(genericsType.getLowerBound());
+ }
+ }
+ }
+ }
+ }
+ } else {
+ if (classNodePlaceholders.containsKey(name)) classNodeType=classNodePlaceholders.get(name);
+ match = classNodeType.isCompatibleWith(redirectBoundType.getType());
+ }
+ } else {
+ if (redirectBoundType.isPlaceholder()) {
+ if (classNodeType.isPlaceholder()) {
+ match = classNodeType.getName().equals(redirectBoundType.getName());
+ } else {
+ String name = redirectBoundType.getName();
+ if (boundPlaceHolders.containsKey(name)) {
+ redirectBoundType = boundPlaceHolders.get(name);
+ boolean wildcard = redirectBoundType.isWildcard();
+ boolean placeholder = redirectBoundType.isPlaceholder();
+ if (placeholder || wildcard) {
+ // placeholder aliases, like Map<U,V> -> Map<K,V>
+// redirectBoundType = classNodePlaceholders.get(name);
+ if (wildcard) {
+ // ex: Comparable<Integer> <=> Comparable<? super T>
+ if (redirectBoundType.lowerBound!=null) {
+ GenericsType gt = new GenericsType(redirectBoundType.lowerBound);
+ if (gt.isPlaceholder()) {
+ // check for recursive generic typedef, like in
+ // <T extends Comparable<? super T>>
+ if (classNodePlaceholders.containsKey(gt.getName())) {
+ gt = classNodePlaceholders.get(gt.getName());
+ }
+ }
+ match = implementsInterfaceOrIsSubclassOf(gt.getType(), classNodeType.getType());
+ }
+ if (match && redirectBoundType.upperBounds!=null) {
+ for (ClassNode upperBound : redirectBoundType.upperBounds) {
+ GenericsType gt = new GenericsType(upperBound);
+ if (gt.isPlaceholder()) {
+ // check for recursive generic typedef, like in
+ // <T extends Comparable<? super T>>
+ if (classNodePlaceholders.containsKey(gt.getName())) {
+ gt = classNodePlaceholders.get(gt.getName());
+ }
+ }
+ match = implementsInterfaceOrIsSubclassOf(classNodeType.getType(), gt.getType())
+ || classNodeType.isCompatibleWith(gt.getType()); // workaround for GROOVY-6095
+ if (!match) break;
+ }
+ }
+ return match;
+ } else if (classNodePlaceholders.containsKey(name)) {
+ redirectBoundType = classNodePlaceholders.get(name);
+ }
+ }
+ }
+ match = redirectBoundType.isCompatibleWith(classNodeType.getType());
+ }
+ } else {
+ // todo: the check for isWildcard should be replaced with a more complete check
+ match = redirectBoundType.isWildcard() || classNodeType.isCompatibleWith(redirectBoundType.getType());
+ }
+ }
+ }
+ return match;
+ }
+ }
+
+ /**
+ * If you have a class which extends a class using generics, returns the superclass with parameterized types. For
+ * example, if you have:
+ * <code>class MyList<T> extends LinkedList<T>
+ * def list = new MyList<String>
+ * </code>
+ * then the parameterized superclass for MyList<String> is LinkedList<String>
+ * @param classNode the class for which we want to return the parameterized superclass
+ * @return the parameterized superclass
+ */
+ private static ClassNode getParameterizedSuperClass(ClassNode classNode) {
+ if (ClassHelper.OBJECT_TYPE.equals(classNode)) return null;
+ ClassNode superClass = classNode.getUnresolvedSuperClass();
+ if (superClass==null) {
+ return ClassHelper.OBJECT_TYPE;
+ }
+ if (!classNode.isUsingGenerics() || !superClass.isUsingGenerics()) return superClass;
+ GenericsType[] genericsTypes = classNode.getGenericsTypes();
+ GenericsType[] redirectGenericTypes = classNode.redirect().getGenericsTypes();
+ superClass = superClass.getPlainNodeReference();
+ if (genericsTypes==null || redirectGenericTypes==null || superClass.getGenericsTypes()==null) return superClass;
+ for (int i = 0, genericsTypesLength = genericsTypes.length; i < genericsTypesLength; i++) {
+ if (redirectGenericTypes[i].isPlaceholder()) {
+ final GenericsType genericsType = genericsTypes[i];
+ GenericsType[] superGenericTypes = superClass.getGenericsTypes();
+ for (int j = 0, superGenericTypesLength = superGenericTypes.length; j < superGenericTypesLength; j++) {
+ final GenericsType superGenericType = superGenericTypes[j];
+ if (superGenericType.isPlaceholder() && superGenericType.getName().equals(redirectGenericTypes[i].getName())) {
+ superGenericTypes[j] = genericsType;
+ }
+ }
+ }
+ }
+ return superClass;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/ast/GroovyClassVisitor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/GroovyClassVisitor.java b/src/main/java/org/codehaus/groovy/ast/GroovyClassVisitor.java
new file mode 100644
index 0000000..e5dff83
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/ast/GroovyClassVisitor.java
@@ -0,0 +1,56 @@
+/*
+ * 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.ast;
+
+/**
+ * A special visitor for working with the structure of a class. In general, your
+ * will want to use the Abstract class based on this class {@link ClassCodeVisitorSupport}.
+ *
+ * @see org.codehaus.groovy.ast.ClassNode
+ * @see org.codehaus.groovy.ast.ClassCodeVisitorSupport
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public interface GroovyClassVisitor {
+
+ /**
+ * Visit a ClassNode.
+ */
+ void visitClass(ClassNode node);
+
+ /**
+ * Visit a ConstructorNode.
+ */
+ void visitConstructor(ConstructorNode node);
+
+ /**
+ * Visit a MethodNode.
+ */
+ void visitMethod(MethodNode node);
+
+ /**
+ * Visit a FieldNode.
+ */
+ void visitField(FieldNode node);
+
+ /**
+ * Visit a PropertyNode.
+ */
+ void visitProperty(PropertyNode node);
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/ast/GroovyCodeVisitor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/GroovyCodeVisitor.java b/src/main/java/org/codehaus/groovy/ast/GroovyCodeVisitor.java
new file mode 100644
index 0000000..12787c0
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/ast/GroovyCodeVisitor.java
@@ -0,0 +1,191 @@
+/*
+ * 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.ast;
+
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ArrayExpression;
+import org.codehaus.groovy.ast.expr.AttributeExpression;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.BitwiseNegationExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.CastExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.ClosureListExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.DeclarationExpression;
+import org.codehaus.groovy.ast.expr.ElvisOperatorExpression;
+import org.codehaus.groovy.ast.expr.FieldExpression;
+import org.codehaus.groovy.ast.expr.GStringExpression;
+import org.codehaus.groovy.ast.expr.ListExpression;
+import org.codehaus.groovy.ast.expr.MapEntryExpression;
+import org.codehaus.groovy.ast.expr.MapExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.MethodPointerExpression;
+import org.codehaus.groovy.ast.expr.NotExpression;
+import org.codehaus.groovy.ast.expr.PostfixExpression;
+import org.codehaus.groovy.ast.expr.PrefixExpression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.RangeExpression;
+import org.codehaus.groovy.ast.expr.SpreadExpression;
+import org.codehaus.groovy.ast.expr.SpreadMapExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+import org.codehaus.groovy.ast.expr.TernaryExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.expr.UnaryMinusExpression;
+import org.codehaus.groovy.ast.expr.UnaryPlusExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.AssertStatement;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.BreakStatement;
+import org.codehaus.groovy.ast.stmt.CaseStatement;
+import org.codehaus.groovy.ast.stmt.CatchStatement;
+import org.codehaus.groovy.ast.stmt.ContinueStatement;
+import org.codehaus.groovy.ast.stmt.DoWhileStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.ast.stmt.IfStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.SwitchStatement;
+import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
+import org.codehaus.groovy.ast.stmt.ThrowStatement;
+import org.codehaus.groovy.ast.stmt.TryCatchStatement;
+import org.codehaus.groovy.ast.stmt.WhileStatement;
+import org.codehaus.groovy.classgen.BytecodeExpression;
+
+/**
+ * An implementation of the visitor pattern for working with ASTNodes
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+
+public interface GroovyCodeVisitor {
+
+ // statements
+
+ //-------------------------------------------------------------------------
+
+ void visitBlockStatement(BlockStatement statement);
+
+ void visitForLoop(ForStatement forLoop);
+
+ void visitWhileLoop(WhileStatement loop);
+
+ void visitDoWhileLoop(DoWhileStatement loop);
+
+ void visitIfElse(IfStatement ifElse);
+
+ void visitExpressionStatement(ExpressionStatement statement);
+
+ void visitReturnStatement(ReturnStatement statement);
+
+ void visitAssertStatement(AssertStatement statement);
+
+ void visitTryCatchFinally(TryCatchStatement finally1);
+
+ void visitSwitch(SwitchStatement statement);
+
+ void visitCaseStatement(CaseStatement statement);
+
+ void visitBreakStatement(BreakStatement statement);
+
+ void visitContinueStatement(ContinueStatement statement);
+
+ void visitThrowStatement(ThrowStatement statement);
+
+ void visitSynchronizedStatement(SynchronizedStatement statement);
+
+ void visitCatchStatement(CatchStatement statement);
+
+ // expressions
+
+ //-------------------------------------------------------------------------
+
+ void visitMethodCallExpression(MethodCallExpression call);
+
+ void visitStaticMethodCallExpression(StaticMethodCallExpression expression);
+
+ void visitConstructorCallExpression(ConstructorCallExpression expression);
+
+ void visitTernaryExpression(TernaryExpression expression);
+
+ void visitShortTernaryExpression(ElvisOperatorExpression expression);
+
+ void visitBinaryExpression(BinaryExpression expression);
+
+ void visitPrefixExpression(PrefixExpression expression);
+
+ void visitPostfixExpression(PostfixExpression expression);
+
+ void visitBooleanExpression(BooleanExpression expression);
+
+ void visitClosureExpression(ClosureExpression expression);
+
+ void visitTupleExpression(TupleExpression expression);
+
+ void visitMapExpression(MapExpression expression);
+
+ void visitMapEntryExpression(MapEntryExpression expression);
+
+ void visitListExpression(ListExpression expression);
+
+ void visitRangeExpression(RangeExpression expression);
+
+ void visitPropertyExpression(PropertyExpression expression);
+
+ void visitAttributeExpression(AttributeExpression attributeExpression);
+
+ void visitFieldExpression(FieldExpression expression);
+
+ void visitMethodPointerExpression(MethodPointerExpression expression);
+
+ void visitConstantExpression(ConstantExpression expression);
+
+ void visitClassExpression(ClassExpression expression);
+
+ void visitVariableExpression(VariableExpression expression);
+
+ void visitDeclarationExpression(DeclarationExpression expression);
+
+ void visitGStringExpression(GStringExpression expression);
+
+ void visitArrayExpression(ArrayExpression expression);
+
+ void visitSpreadExpression(SpreadExpression expression);
+
+ void visitSpreadMapExpression(SpreadMapExpression expression);
+
+ void visitNotExpression(NotExpression expression);
+
+ void visitUnaryMinusExpression(UnaryMinusExpression expression);
+
+ void visitUnaryPlusExpression(UnaryPlusExpression expression);
+
+ void visitBitwiseNegationExpression(BitwiseNegationExpression expression);
+
+ void visitCastExpression(CastExpression expression);
+
+ void visitArgumentlistExpression(ArgumentListExpression expression);
+
+ void visitClosureListExpression(ClosureListExpression closureListExpression);
+
+ void visitBytecodeExpression(BytecodeExpression expression);
+}
+
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/ast/ImportNode.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/ImportNode.java b/src/main/java/org/codehaus/groovy/ast/ImportNode.java
new file mode 100644
index 0000000..5f01494
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/ast/ImportNode.java
@@ -0,0 +1,153 @@
+/*
+ * 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.ast;
+
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Represents an import statement of a single class
+ *
+ * @author Jochen Theodorou
+ * @author Paul King
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class ImportNode extends AnnotatedNode implements Opcodes {
+
+ private final ClassNode type;
+ private final String alias;
+ private final String fieldName;
+ // TODO use PackageNode instead here?
+ private final String packageName;
+ private final boolean isStar;
+ private final boolean isStatic;
+
+ /**
+ * Represent an import of an entire package, i.e. import package.Classname
+ *
+ * @param type the referenced class
+ * @param alias optional alias
+ */
+ public ImportNode(ClassNode type, String alias) {
+ this.type = type;
+ this.alias = alias;
+ this.isStar = false;
+ this.isStatic = false;
+ this.packageName = null;
+ this.fieldName = null;
+ }
+
+ /**
+ * Represent an import of an entire package, i.e. import package.*
+ *
+ * @param packageName the name of the package
+ */
+ public ImportNode(String packageName) {
+ this.type = null;
+ this.alias = null;
+ this.isStar = true;
+ this.isStatic = false;
+ this.packageName = packageName;
+ this.fieldName = null;
+ }
+
+ /**
+ * Represent a static import of a Class, i.e. import static package.Classname.*
+ *
+ * @param type the referenced class
+ */
+ public ImportNode(ClassNode type) {
+ this.type = type;
+ this.alias = null;
+ this.isStar = true;
+ this.isStatic = true;
+ this.packageName = null;
+ this.fieldName = null;
+ }
+
+ /**
+ * Represent a static import of a field or method, i.e. import static package.Classname.name
+ *
+ * @param type the referenced class
+ * @param fieldName the field name
+ * @param alias optional alias
+ */
+ public ImportNode(ClassNode type, String fieldName, String alias) {
+ this.type = type;
+ this.alias = alias;
+ this.isStar = false;
+ this.isStatic = true;
+ this.packageName = null;
+ this.fieldName = fieldName;
+ }
+
+ /**
+ * @return the text display of this import
+ */
+ public String getText() {
+ String typeName = getClassName();
+ if (isStar && !isStatic) {
+ return "import " + packageName + "*";
+ }
+ if (isStar) {
+ return "import static " + typeName + ".*";
+ }
+ if (isStatic) {
+ if (alias != null && alias.length() != 0 && !alias.equals(fieldName)) {
+ return "import static " + typeName + "." + fieldName + " as " + alias;
+ }
+ return "import static " + typeName + "." + fieldName;
+ }
+ if (alias == null || alias.length() == 0) {
+ return "import " + typeName;
+ }
+ return "import " + typeName + " as " + alias;
+ }
+
+ public String getPackageName() {
+ return packageName;
+ }
+
+ public String getFieldName() {
+ return fieldName;
+ }
+
+ public boolean isStar() {
+ return isStar;
+ }
+
+ public boolean isStatic() {
+ return isStatic;
+ }
+
+ public String getAlias() {
+ return alias;
+ }
+
+ public ClassNode getType() {
+ return type;
+ }
+
+ public String getClassName() {
+ return type == null ? null : type.getName();
+ }
+
+ public void visit(GroovyCodeVisitor visitor) {
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/ast/InnerClassNode.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/InnerClassNode.java b/src/main/java/org/codehaus/groovy/ast/InnerClassNode.java
new file mode 100644
index 0000000..f20c457
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/ast/InnerClassNode.java
@@ -0,0 +1,103 @@
+/*
+ * 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.ast;
+
+import org.codehaus.groovy.ast.stmt.Statement;
+
+import java.util.LinkedList;
+
+/**
+ * Represents an inner class declaration
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class InnerClassNode extends ClassNode {
+
+ private final ClassNode outerClass;
+ private VariableScope scope;
+ private boolean anonymous;
+
+ /**
+ * @param name is the full name of the class
+ * @param modifiers the modifiers, @see org.objectweb.asm.Opcodes
+ * @param superClass the base class name - use "java.lang.Object" if no direct base class
+ */
+ public InnerClassNode(ClassNode outerClass, String name, int modifiers, ClassNode superClass) {
+ this(outerClass, name, modifiers, superClass, ClassHelper.EMPTY_TYPE_ARRAY, MixinNode.EMPTY_ARRAY);
+ }
+
+ /**
+ * @param name is the full name of the class
+ * @param modifiers the modifiers, @see org.objectweb.asm.Opcodes
+ * @param superClass the base class name - use "java.lang.Object" if no direct base class
+ */
+ public InnerClassNode(ClassNode outerClass, String name, int modifiers, ClassNode superClass, ClassNode[] interfaces, MixinNode[] mixins) {
+ super(name, modifiers, superClass, interfaces, mixins);
+ this.outerClass = outerClass;
+
+ if (outerClass.innerClasses == null)
+ outerClass.innerClasses = new LinkedList<InnerClassNode> ();
+ outerClass.innerClasses.add(this);
+ }
+
+ public ClassNode getOuterClass() {
+ return outerClass;
+ }
+
+ public ClassNode getOuterMostClass() {
+ ClassNode outerClass = getOuterClass();
+ while (outerClass instanceof InnerClassNode) {
+ outerClass = outerClass.getOuterClass();
+ }
+ return outerClass;
+ }
+
+ /**
+ * @return the field node on the outer class or null if this is not an inner class
+ */
+ public FieldNode getOuterField(String name) {
+ return outerClass.getDeclaredField(name);
+ }
+
+ public VariableScope getVariableScope() {
+ return scope;
+ }
+
+ public void setVariableScope(VariableScope scope) {
+ this.scope = scope;
+ }
+
+ public boolean isAnonymous() {
+ return anonymous;
+ }
+
+ public void setAnonymous(boolean anonymous) {
+ this.anonymous = anonymous;
+ }
+
+ @Override
+ public void addConstructor(final ConstructorNode node) {
+ super.addConstructor(node);
+ }
+
+ @Override
+ public ConstructorNode addConstructor(final int modifiers, final Parameter[] parameters, final ClassNode[] exceptions, final Statement code) {
+ return super.addConstructor(modifiers, parameters, exceptions, code);
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/ast/InterfaceHelperClassNode.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/InterfaceHelperClassNode.java b/src/main/java/org/codehaus/groovy/ast/InterfaceHelperClassNode.java
new file mode 100644
index 0000000..d2e952f
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/ast/InterfaceHelperClassNode.java
@@ -0,0 +1,51 @@
+/*
+ * 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.ast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents an inner class defined as helper for an interface
+ *
+ * @author Roshan Dawrani
+ */
+public class InterfaceHelperClassNode extends InnerClassNode {
+
+ private List callSites = new ArrayList();
+
+ /**
+ * @param name is the full name of the class
+ * @param modifiers the modifiers, @see org.objectweb.asm.Opcodes
+ * @param superClass the base class name - use "java.lang.Object" if no direct base class
+ * @param callSites list of callsites used in the interface
+ */
+ public InterfaceHelperClassNode(ClassNode outerClass, String name, int modifiers, ClassNode superClass, List<String> callSites) {
+ super(outerClass, name, modifiers, superClass, ClassHelper.EMPTY_TYPE_ARRAY, MixinNode.EMPTY_ARRAY);
+ setCallSites(callSites);
+ }
+
+ public void setCallSites(List<String> cs) {
+ callSites = (cs != null) ? cs : new ArrayList<String>();
+ }
+
+ public List<String> getCallSites() {
+ return callSites;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/ast/MethodCallTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/MethodCallTransformation.java b/src/main/java/org/codehaus/groovy/ast/MethodCallTransformation.java
new file mode 100644
index 0000000..69ab6c0
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/ast/MethodCallTransformation.java
@@ -0,0 +1,116 @@
+/*
+ * 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.ast;
+
+import groovy.lang.MissingPropertyException;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.transform.ASTTransformation;
+
+/**
+ *
+ * @author Hamlet D'Arcy
+ * @author Sergei Egorov <bs...@gmail.com>
+ */
+
+public abstract class MethodCallTransformation implements ASTTransformation {
+
+ public void visit(ASTNode[] nodes, SourceUnit sourceUnit) {
+
+ GroovyCodeVisitor transformer = getTransformer(nodes, sourceUnit);
+
+ if (nodes != null) {
+ for (ASTNode it : nodes) {
+ if (!(it instanceof AnnotationNode) && !(it instanceof ClassNode)) {
+ it.visit(transformer);
+ }
+ }
+ }
+ if (sourceUnit.getAST() != null) {
+ sourceUnit.getAST().visit(transformer);
+ if (sourceUnit.getAST().getStatementBlock() != null) {
+ sourceUnit.getAST().getStatementBlock().visit(transformer);
+ }
+ if (sourceUnit.getAST().getClasses() != null) {
+ for (ClassNode classNode : sourceUnit.getAST().getClasses()) {
+ if (classNode.getMethods() != null) {
+ for (MethodNode node : classNode.getMethods()) {
+ if (node != null && node.getCode() != null) {
+ node.getCode().visit(transformer);
+ }
+ }
+ }
+
+ try {
+ if (classNode.getDeclaredConstructors() != null) {
+ for (MethodNode node : classNode.getDeclaredConstructors()) {
+ if (node != null && node.getCode() != null) {
+ node.getCode().visit(transformer);
+ }
+ }
+ }
+ } catch (MissingPropertyException ignored) {
+ // todo: inner class nodes don't have a constructors field available
+ }
+
+ // all properties are also always fields
+ if (classNode.getFields() != null) {
+ for (FieldNode node : classNode.getFields()) {
+ if (node.getInitialValueExpression() != null) {
+ node.getInitialValueExpression().visit(transformer);
+ }
+ }
+ }
+
+ try {
+ if (classNode.getObjectInitializerStatements() != null) {
+ for (Statement node : classNode.getObjectInitializerStatements()) {
+ if (node != null) {
+ node.visit(transformer);
+ }
+ }
+ }
+ } catch (MissingPropertyException ignored) {
+ // todo: inner class nodes don't have an objectInitializers field available
+ }
+
+ // todo: is there anything to do with the module ???
+ }
+ }
+ if (sourceUnit.getAST().getMethods() != null) {
+ for (MethodNode node : sourceUnit.getAST().getMethods()) {
+ if (node != null) {
+ if (node.getParameters() != null) {
+ for (Parameter parameter : node.getParameters()) {
+ if (parameter != null && parameter.getInitialExpression() != null) {
+ parameter.getInitialExpression().visit(transformer);
+ }
+ }
+ }
+ if (node.getCode() != null) {
+ node.getCode().visit(transformer);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ protected abstract GroovyCodeVisitor getTransformer(ASTNode[] nodes, SourceUnit sourceUnit);
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/ast/MethodInvocationTrap.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/MethodInvocationTrap.java b/src/main/java/org/codehaus/groovy/ast/MethodInvocationTrap.java
new file mode 100644
index 0000000..9801c60
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/ast/MethodInvocationTrap.java
@@ -0,0 +1,97 @@
+/*
+ * 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.ast;
+
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.tools.ClosureUtils;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
+import org.codehaus.groovy.syntax.SyntaxException;
+
+/**
+ *
+ * @author Hamlet D'Arcy
+ * @author Sergei Egorov <bs...@gmail.com>
+ */
+public abstract class MethodInvocationTrap extends CodeVisitorSupport {
+
+ protected final ReaderSource source;
+ protected final SourceUnit sourceUnit;
+
+ public MethodInvocationTrap(ReaderSource source, SourceUnit sourceUnit) {
+ if (source == null) throw new IllegalArgumentException("Null: source");
+ if (sourceUnit == null) throw new IllegalArgumentException("Null: sourceUnit");
+ this.source = source;
+ this.sourceUnit = sourceUnit;
+ }
+
+ /**
+ * Attempts to find AstBuilder 'from code' invocations. When found, converts them into calls
+ * to the 'from string' approach.
+ *
+ * @param call the method call expression that may or may not be an AstBuilder 'from code' invocation.
+ */
+ public void visitMethodCallExpression(MethodCallExpression call) {
+ boolean shouldContinueWalking = true;
+
+ if (isBuildInvocation(call)) {
+ shouldContinueWalking = handleTargetMethodCallExpression(call);
+ }
+
+ if(shouldContinueWalking) {
+ // continue normal tree walking
+ call.getObjectExpression().visit(this);
+ call.getMethod().visit(this);
+ call.getArguments().visit(this);
+ }
+ }
+
+ /**
+ * Reports an error back to the source unit.
+ *
+ * @param msg the error message
+ * @param expr the expression that caused the error message.
+ */
+ protected void addError(String msg, ASTNode expr) {
+ sourceUnit.getErrorCollector().addErrorAndContinue(
+ new SyntaxErrorMessage(new SyntaxException(msg + '\n', expr.getLineNumber(), expr.getColumnNumber(), expr.getLastLineNumber(), expr.getLastColumnNumber()), sourceUnit)
+ );
+ }
+
+ /**
+ * Converts a ClosureExpression into the String source.
+ *
+ * @param expression a closure
+ * @return the source the closure was created from
+ */
+ protected String convertClosureToSource(ClosureExpression expression) {
+ try {
+ return ClosureUtils.convertClosureToSource(source, expression);
+ } catch(Exception e) {
+ addError(e.getMessage(), expression);
+ }
+ return null;
+ }
+
+ protected abstract boolean handleTargetMethodCallExpression(MethodCallExpression call);
+
+ protected abstract boolean isBuildInvocation(MethodCallExpression call);
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/ast/MethodNode.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/MethodNode.java b/src/main/java/org/codehaus/groovy/ast/MethodNode.java
new file mode 100644
index 0000000..a43286f
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/ast/MethodNode.java
@@ -0,0 +1,284 @@
+/*
+ * 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.ast;
+
+import groovy.lang.groovydoc.Groovydoc;
+import groovy.lang.groovydoc.GroovydocHolder;
+import org.apache.groovy.ast.tools.MethodNodeUtils;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.objectweb.asm.Opcodes;
+
+import java.util.List;
+
+/**
+ * Represents a method declaration
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Hamlet D'Arcy
+ */
+public class MethodNode extends AnnotatedNode implements Opcodes, GroovydocHolder<MethodNode> {
+
+ public static final String SCRIPT_BODY_METHOD_KEY = "org.codehaus.groovy.ast.MethodNode.isScriptBody";
+ private final String name;
+ private int modifiers;
+ private boolean syntheticPublic;
+ private ClassNode returnType;
+ private Parameter[] parameters;
+ private boolean hasDefaultValue = false;
+ private Statement code;
+ private boolean dynamicReturnType;
+ private VariableScope variableScope;
+ private final ClassNode[] exceptions;
+ private final boolean staticConstructor;
+
+ // type spec for generics
+ private GenericsType[] genericsTypes = null;
+ private boolean hasDefault;
+
+ // cached data
+ String typeDescriptor;
+
+ public MethodNode(String name, int modifiers, ClassNode returnType, Parameter[] parameters, ClassNode[] exceptions, Statement code) {
+ this.name = name;
+ this.modifiers = modifiers;
+ this.code = code;
+ setReturnType(returnType);
+ VariableScope scope = new VariableScope();
+ setVariableScope(scope);
+ setParameters(parameters);
+ this.hasDefault = false;
+ this.exceptions = exceptions;
+ this.staticConstructor = (name != null && name.equals("<clinit>"));
+ }
+
+ /**
+ * The type descriptor for a method node is a string containing the name of the method, its return type,
+ * and its parameter types in a canonical form. For simplicity, we use the format of a Java declaration
+ * without parameter names or generics.
+ *
+ * @return the type descriptor
+ */
+ public String getTypeDescriptor() {
+ if (typeDescriptor == null) {
+ typeDescriptor = MethodNodeUtils.methodDescriptor(this);
+ }
+ return typeDescriptor;
+ }
+
+ private void invalidateCachedData() {
+ typeDescriptor = null;
+ }
+
+ public boolean isVoidMethod() {
+ return returnType == ClassHelper.VOID_TYPE;
+ }
+
+ public Statement getCode() {
+ return code;
+ }
+
+ public void setCode(Statement code) {
+ this.code = code;
+ }
+
+ public int getModifiers() {
+ return modifiers;
+ }
+
+ public void setModifiers(int modifiers) {
+ invalidateCachedData();
+ this.modifiers = modifiers;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Parameter[] getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(Parameter[] parameters) {
+ invalidateCachedData();
+ VariableScope scope = new VariableScope();
+ this.parameters = parameters;
+ if (parameters != null && parameters.length > 0) {
+ for (Parameter para : parameters) {
+ if (para.hasInitialExpression()) {
+ this.hasDefaultValue = true;
+ }
+ para.setInStaticContext(isStatic());
+ scope.putDeclaredVariable(para);
+ }
+ }
+ setVariableScope(scope);
+ }
+
+ public ClassNode getReturnType() {
+ return returnType;
+ }
+
+ public VariableScope getVariableScope() {
+ return variableScope;
+ }
+
+ public void setVariableScope(VariableScope variableScope) {
+ this.variableScope = variableScope;
+ variableScope.setInStaticContext(isStatic());
+ }
+
+ public boolean isDynamicReturnType() {
+ return dynamicReturnType;
+ }
+
+ public boolean isAbstract() {
+ return (modifiers & ACC_ABSTRACT) != 0;
+ }
+
+ public boolean isStatic() {
+ return (modifiers & ACC_STATIC) != 0;
+ }
+
+ public boolean isPublic() {
+ return (modifiers & ACC_PUBLIC) != 0;
+ }
+
+ public boolean isPrivate() {
+ return (modifiers & ACC_PRIVATE) != 0;
+ }
+
+ public boolean isFinal() {
+ return (modifiers & ACC_FINAL) != 0;
+ }
+
+ public boolean isProtected() {
+ return (modifiers & ACC_PROTECTED) != 0;
+ }
+
+ public boolean hasDefaultValue() {
+ return this.hasDefaultValue;
+ }
+
+ /**
+ * @return true if this method is the run method from a script
+ */
+ public boolean isScriptBody() {
+ return getNodeMetaData(SCRIPT_BODY_METHOD_KEY) != null;
+ }
+
+ /**
+ * Set the metadata flag for this method to indicate that it is a script body implementation.
+ * @see ModuleNode createStatementsClass().
+ */
+ public void setIsScriptBody() {
+ setNodeMetaData(SCRIPT_BODY_METHOD_KEY, true);
+ }
+
+ public String toString() {
+ return "MethodNode@" + hashCode() + "[" + getDeclaringClass().getName() + "#" + getTypeDescriptor() + "]";
+ }
+
+ public void setReturnType(ClassNode returnType) {
+ invalidateCachedData();
+ dynamicReturnType |= ClassHelper.DYNAMIC_TYPE == returnType;
+ this.returnType = returnType;
+ if (returnType == null) this.returnType = ClassHelper.OBJECT_TYPE;
+ }
+
+ public ClassNode[] getExceptions() {
+ return exceptions;
+ }
+
+ public Statement getFirstStatement() {
+ if (code == null) return null;
+ Statement first = code;
+ while (first instanceof BlockStatement) {
+ List<Statement> list = ((BlockStatement) first).getStatements();
+ if (list.isEmpty()) {
+ first = null;
+ } else {
+ first = list.get(0);
+ }
+ }
+ return first;
+ }
+
+ public GenericsType[] getGenericsTypes() {
+ return genericsTypes;
+ }
+
+ public void setGenericsTypes(GenericsType[] genericsTypes) {
+ invalidateCachedData();
+ this.genericsTypes = genericsTypes;
+ }
+
+ public void setAnnotationDefault(boolean b) {
+ this.hasDefault = b;
+ }
+
+ public boolean hasAnnotationDefault() {
+ return hasDefault;
+ }
+
+ public boolean isStaticConstructor() {
+ return staticConstructor;
+ }
+
+ /**
+ * Indicates that this method has been "promoted" to public by
+ * Groovy when in fact there was no public modifier explicitly
+ * in the source code. I.e. it remembers that it has applied
+ * Groovy's "public methods by default" rule. This property is
+ * typically only of interest to AST transform writers.
+ *
+ * @return true if this class is public but had no explicit public modifier
+ */
+ public boolean isSyntheticPublic() {
+ return syntheticPublic;
+ }
+
+ public void setSyntheticPublic(boolean syntheticPublic) {
+ this.syntheticPublic = syntheticPublic;
+ }
+
+ /**
+ * Provides a nicely formatted string of the method definition. For simplicity, generic types on some of the elements
+ * are not displayed.
+ * @return
+ * string form of node with some generic elements suppressed
+ */
+ @Override
+ public String getText() {
+ String retType = AstToTextHelper.getClassText(returnType);
+ String exceptionTypes = AstToTextHelper.getThrowsClauseText(exceptions);
+ String parms = AstToTextHelper.getParametersText(parameters);
+ return AstToTextHelper.getModifiersText(modifiers) + " " + retType + " " + name + "(" + parms + ") " + exceptionTypes + " { ... }";
+ }
+
+ @Override
+ public Groovydoc getGroovydoc() {
+ return this.<Groovydoc>getNodeMetaData(DOC_COMMENT);
+ }
+
+ @Override
+ public MethodNode getInstance() {
+ return this;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/ast/MixinASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/MixinASTTransformation.java b/src/main/java/org/codehaus/groovy/ast/MixinASTTransformation.java
new file mode 100644
index 0000000..21bccce
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/ast/MixinASTTransformation.java
@@ -0,0 +1,88 @@
+/*
+ * 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.ast;
+
+import groovy.lang.Mixin;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.ListExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.transform.AbstractASTTransformation;
+import org.codehaus.groovy.transform.GroovyASTTransformation;
+
+import static org.codehaus.groovy.ast.ClassHelper.make;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
+
+/**
+ * @deprecated static mixins have been deprecated in favour of traits (trait keyword).
+ */
+@Deprecated
+@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
+public class MixinASTTransformation extends AbstractASTTransformation {
+ private static final ClassNode MY_TYPE = make(Mixin.class);
+
+ public void visit(ASTNode nodes[], SourceUnit source) {
+ init(nodes, source);
+ AnnotationNode node = (AnnotationNode) nodes[0];
+ AnnotatedNode parent = (AnnotatedNode) nodes[1];
+ if (!MY_TYPE.equals(node.getClassNode()))
+ return;
+
+ final Expression expr = node.getMember("value");
+ if (expr == null) {
+ return;
+ }
+
+ Expression useClasses = null;
+ if (expr instanceof ClassExpression) {
+ useClasses = expr;
+ } else if (expr instanceof ListExpression) {
+ ListExpression listExpression = (ListExpression) expr;
+ for (Expression ex : listExpression.getExpressions()) {
+ if (!(ex instanceof ClassExpression))
+ return;
+ }
+ useClasses = expr;
+ }
+
+ if (useClasses == null)
+ return;
+
+ if (parent instanceof ClassNode) {
+ ClassNode annotatedClass = (ClassNode) parent;
+
+ final Parameter[] noparams = Parameter.EMPTY_ARRAY;
+ MethodNode clinit = annotatedClass.getDeclaredMethod("<clinit>", noparams);
+ if (clinit == null) {
+ clinit = annotatedClass.addMethod("<clinit>", ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC, ClassHelper.VOID_TYPE, noparams, null, new BlockStatement());
+ clinit.setSynthetic(true);
+ }
+
+ final BlockStatement code = (BlockStatement) clinit.getCode();
+ code.addStatement(
+ stmt(callX(propX(classX(annotatedClass), "metaClass"), "mixin", useClasses))
+ );
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/ast/MixinNode.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/MixinNode.java b/src/main/java/org/codehaus/groovy/ast/MixinNode.java
new file mode 100644
index 0000000..08dee7c
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/ast/MixinNode.java
@@ -0,0 +1,47 @@
+/*
+ * 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.ast;
+
+/**
+ * Represents a mixin which can be applied to any ClassNode to implement mixins
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class MixinNode extends ClassNode {
+
+ public static final MixinNode[] EMPTY_ARRAY = {};
+
+ /**
+ * @param name is the full name of the class
+ * @param modifiers the modifiers, @see org.objectweb.asm.Opcodes
+ * @param superType the base class name - use "java.lang.Object" if no direct base class
+ */
+ public MixinNode(String name, int modifiers, ClassNode superType) {
+ this(name, modifiers, superType, ClassHelper.EMPTY_TYPE_ARRAY);
+ }
+
+ /**
+ * @param name is the full name of the class
+ * @param modifiers the modifiers, @see org.objectweb.asm.Opcodes
+ * @param superType the base class name - use "java.lang.Object" if no direct base class
+ */
+ public MixinNode(String name, int modifiers, ClassNode superType, ClassNode[] interfaces) {
+ super(name, modifiers, superType, interfaces, MixinNode.EMPTY_ARRAY);
+ }
+}