Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * Copyright 2003-2007 the original author or authors.
    *
    * 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.codehaus.groovy.control;
  
  import  org.objectweb.asm.Opcodes;
  
  import java.io.File;
  import java.net.URL;
  import java.util.*;

Visitor to resolve Types and convert VariableExpression to ClassExpressions if needed. The ResolveVisitor will try to find the Class for a ClassExpression and prints an error if it fails to do so. Constructions like C[], foo as C, (C) foo will force creation of a ClassExpression for C

Note: the method to start the resolving is startResolving(ClassNode, SourceUnit).

Author(s):
Jochen Theodorou
  
  public class ResolveVisitor extends ClassCodeExpressionTransformer {
      private ClassNode currentClass;
      // note: BigInteger and BigDecimal are also imported by default
      public static final String[] DEFAULT_IMPORTS = {"java.lang.""java.io.""java.net.""java.util.""groovy.lang.""groovy.util."};
      private CompilationUnit compilationUnit;
      private Map cachedClasses = new HashMap();
      private static final Object NO_CLASS = new Object();
      private static final Object SCRIPT = new Object();
      private SourceUnit source;
      private VariableScope currentScope;
  
      private boolean isTopLevelProperty = true;
      private boolean inPropertyExpression = false;
      private boolean inClosure = false;
      private boolean isSpecialConstructorCall = false;
  
      private Map genericParameterNames = new HashMap();

    
we use ConstructedClassWithPackage to limit the resolving the compiler does when combining package names and class names. The idea that if we use a package, then we do not want to replace the '.' with a '$' for the package part, only for the class name part. There is also the case of a imported class, so this logic can't be done in these cases...
  
      private static class ConstructedClassWithPackage extends ClassNode {
          String prefix;
          String className;
          public ConstructedClassWithPackage(String pkgString name) {
              super(pkg+name, Opcodes.ACC_PUBLIC,.);
               = false;
              this. = pkg;
              this. = name;
          }
          public String getName() {
              if (redirect()!=thisreturn super.getName();
              return +;
          }
          public boolean hasPackageName() {
              if (redirect()!=thisreturn super.hasPackageName();
              return .indexOf('.')!=-1;
          }
          public String setName(String name) {
              if (redirect()!=this) {
                  return super.setName(name);
              } else {
                  throw new GroovyBugError("ConstructedClassWithPackage#setName should not be called");
              }
          }
      }

     
we use LowerCaseClass to limit the resolving the compiler does for vanilla names starting with a lower case letter. The idea that if we use a vanilla name with a lower case letter, that this is in most cases no class. If it is a class the class needs to be imported explicitly. The efffect is that in an expression like "def foo = bar" we do not have to use a loadClass call to check the name foo and bar for being classes. Instead we will ask the module for an alias for this name which is much faster.
 
     private static class LowerCaseClass extends ClassNode {
         String className;
         public LowerCaseClass(String name) {
             super(name, Opcodes.ACC_PUBLIC,.);
              = false;
             this. = name;
         }
         public String getName() {
             if (redirect()!=thisreturn super.getName();
             return ;
         }
         public boolean hasPackageName() {
             if (redirect()!=thisreturn super.hasPackageName();
             return false;
         }
         public String setName(String name) {
             if (redirect()!=this) {
                 return super.setName(name);
             } else {
                 throw new GroovyBugError("ConstructedClassWithPackage#setName should not be called");
             }
         }
     }
 
     public ResolveVisitor(CompilationUnit cu) {
          = cu;
     }
 
     public void startResolving(ClassNode nodeSourceUnit source) {
         this. = source;
         visitClass(node);
     }
 
     protected void visitConstructorOrMethod(MethodNode nodeboolean isConstructor) {
         VariableScope oldScope = ;
          = node.getVariableScope();
         Map oldPNames = ;
 
         resolveGenericsHeader(node.getGenericsTypes());
 
         Parameter[] paras = node.getParameters();
         for (int i = 0; i < paras.lengthi++) {
             Parameter p = paras[i];
             p.setInitialExpression(transform(p.getInitialExpression()));
             resolveOrFail(p.getType(), p.getType());
             visitAnnotations(p);
         }
         ClassNode[] exceptions = node.getExceptions();
         for (int i = 0; i < exceptions.lengthi++) {
             ClassNode t = exceptions[i];
             resolveOrFail(tnode);
         }
         resolveOrFail(node.getReturnType(), node);
 
         super.visitConstructorOrMethod(nodeisConstructor);
 
          = oldPNames;
          = oldScope;
     }
 
     public void visitField(FieldNode node) {
         ClassNode t = node.getType();
         resolveOrFail(tnode);
         super.visitField(node);
     }
 
     public void visitProperty(PropertyNode node) {
         ClassNode t = node.getType();
         resolveOrFail(tnode);
         super.visitProperty(node);
     }
 
     private boolean resolveToInner (ClassNode type) {
         // we do not do our name mangling to find an inner class
         // if the type is a ConstructedClassWithPackage, because in this case we
         // are resolving the name at a different place already
         if (type instanceof ConstructedClassWithPackagereturn false;
         String name = type.getName();
         String saved = name;
         while (true) {
             int len = name.lastIndexOf('.');
             if (len == -1) break;
             name = name.substring(0,len) + "$" + name.substring(len+1);
             type.setName(name);
             if (resolve(type)) return true;
         }
         if(resolveToInnerEnum (type)) return true;
         
         type.setName(saved);
         return false;
     }
     
     private boolean resolveToInnerEnum (ClassNode type) {
         // GROOVY-3483: It may be an inner enum defined by this class itself, in which case it does not need to be
         // explicitly qualified by the currentClass name
     	String name = type.getName();
         if( != type && !name.contains(".") && type.getClass().equals(ClassNode.class)) {
             type.setName(.getName() + "$" + name);
             if (resolve(type)) return true;
         }
         return false;
     }
 
     private void resolveOrFail(ClassNode typeString msgASTNode node) {
         if (resolve(type)) return;
         if (resolveToInner(type)) return;
         addError("unable to resolve class " + type.getName() + " " + msgnode);
     }
 
     private void resolveOrFail(ClassNode typeASTNode nodeboolean prefereImports) {
         resolveGenericsTypes(type.getGenericsTypes());
         if (prefereImports && resolveAliasFromModule(type)) return;
         resolveOrFail(typenode);
     }
 
     private void resolveOrFail(ClassNode typeASTNode node) {
         resolveOrFail(type""node);
     }
 
     private boolean resolve(ClassNode type) {
         return resolve(typetruetruetrue);
     }
 
     private boolean resolve(ClassNode typeboolean testModuleImportsboolean testDefaultImportsboolean testStaticInnerClasses) {
         if (type.isResolved() || type.isPrimaryClassNode()) return true;
         resolveGenericsTypes(type.getGenericsTypes());
         if (type.isArray()) {
             ClassNode element = type.getComponentType();
             boolean resolved = resolve(elementtestModuleImportstestDefaultImportstestStaticInnerClasses);
             if (resolved) {
                 ClassNode cn = element.makeArray();
                 type.setRedirect(cn);
             }
             return resolved;
         }
 
         // test if vanilla name is current class name
         if ( == typereturn true;
 
         if (.get(type.getName()) != null) {
             GenericsType gt = (GenericsType.get(type.getName());
             type.setRedirect(gt.getType());
             type.setGenericsTypes(new GenericsType[]{gt});
             type.setGenericsPlaceHolder(true);
             return true;
         }
 
         if (.getNameWithoutPackage().equals(type.getName())) {
             type.setRedirect();
             return true;
         }
 
         return  resolveFromModule(typetestModuleImports) ||
                 resolveFromCompileUnit(type) ||
                 resolveFromDefaultImports(typetestDefaultImports) ||
                 resolveFromStaticInnerClasses(typetestStaticInnerClasses) ||
                 resolveFromClassCache(type) ||
                 resolveToClass(type) ||
                 resolveToScript(type);
 
     }
 
     private boolean resolveFromClassCache(ClassNode type) {
         String name = type.getName();
         Object val = .get(name);
         if (val == null || val == ) {
             return false;
         } else {
             setClass(type, (Classval);
             return true;
         }
     }
 
     // NOTE: copied from GroovyClassLoader
     private long getTimeStamp(Class cls) {
         return Verifier.getTimestamp(cls);
     }
 
     // NOTE: copied from GroovyClassLoader
     private boolean isSourceNewer(URL sourceClass cls) {
         try {
             long lastMod;
 
             // Special handling for file:// protocol, as getLastModified() often reports
             // incorrect results (-1)
             if (source.getProtocol().equals("file")) {
                 // Coerce the file URL to a File
                 String path = source.getPath().replace('/'.).replace('|'':');
                 File file = new File(path);
                 lastMod = file.lastModified();
             } else {
                 URLConnection conn = source.openConnection();
                 lastMod = conn.getLastModified();
                 conn.getInputStream().close();
             }
             return lastMod > getTimeStamp(cls);
         } catch (IOException e) {
             // if the stream can't be opened, let's keep the old reference
             return false;
         }
     }
 
 
     private boolean resolveToScript(ClassNode type) {
         String name = type.getName();
 
         // We do not need to check instances of LowerCaseClass
         // to be a script, because unless there was an import for
         // for this we  do not lookup these cases. This was a decision
         // made on the mailing list. To ensure we will not visit this
         // method again we set a NO_CLASS for this name
         if (type instanceof LowerCaseClass) {
             .put(name);
         }
         
         if (.get(name) == return false;
         if (.get(name) == .put(name);
         if (name.startsWith("java.")) return type.isResolved();
         //TODO: don't ignore inner static classes completely
         if (name.indexOf('$') != -1) return type.isResolved();
         ModuleNode module = .getModule();
         if (module.hasPackageName() && name.indexOf('.') == -1) return type.isResolved();
         // try to find a script from classpath
         URL url = null;
         try {
             url = gcl.getResourceLoader().loadGroovySource(name);
         } catch (MalformedURLException e) {
             // fall through and let the URL be null
         }
         if (url != null) {
             if (type.isResolved()) {
                 Class cls = type.getTypeClass();
                 // if the file is not newer we don't want to recompile
                 if (!isSourceNewer(urlcls)) return true;
                 // since we came to this, we want to recompile
                 .remove(type.getName());
                 type.setRedirect(null);
             }
             SourceUnit su = .addSource(url);
             .getCompileUnit().addClassNodeToCompile(typesu);
             return true;
         }
         // type may be resolved through the classloader before
         return type.isResolved();
     }
 
     private String replaceLastPoint(String name) {
         int lastPoint = name.lastIndexOf('.');
         name = new StringBuffer()
                 .append(name.substring(0, lastPoint))
                 .append("$")
                 .append(name.substring(lastPoint + 1))
                 .toString();
         return name;
     }
 
     private boolean resolveFromStaticInnerClasses(ClassNode typeboolean testStaticInnerClasses) {
         // a class consisting of a vanilla name can never be
         // a static inner class, because at least one dot is
         // required for this. Example: foo.bar -> foo$bar
         if (type instanceof LowerCaseClassreturn false;
 
         // try to resolve a public static inner class' name
         testStaticInnerClasses &= type.hasPackageName();
         if (testStaticInnerClasses) {
             if (type instanceof ConstructedClassWithPackage) {
                 // we replace '.' only in the className part
                 // with '$' to find an inner class. The case that
                 // the packageis really a class is handled else where
                 ConstructedClassWithPackage tmp = (ConstructedClassWithPackagetype;
                 String name = ((ConstructedClassWithPackagetype).;
                 tmp.className = replaceLastPoint(name);
                 if (resolve(tmpfalsetruetrue)) {
                     type.setRedirect(tmp.redirect());
                     return true;
                 }
                 tmp.className = name;
             }   else {
                 String name = type.getName();
                 String replacedPointType = replaceLastPoint(name);
                 type.setName(replacedPointType);
                 if (resolve(typefalsetruetrue)) return true;
                 type.setName(name);
             }
         }
         return false;
     }
 
     private boolean resolveFromDefaultImports(ClassNode typeboolean testDefaultImports) {
         // test default imports
         testDefaultImports &= !type.hasPackageName();
         // we do not resolve a vanilla name starting with a lower case letter
         // try to resolve against adefault import, because we know that the
         // default packages do not contain classes like these
         testDefaultImports &= !(type instanceof LowerCaseClass);
         if (testDefaultImports) {
             for (int i = 0, size = .i < sizei++) {
                 String packagePrefix = [i];
                 String name = type.getName();
                 // We limit the inner class lookups here by using ConstructedClassWithPackage.
                 // This way only the name will change, the packagePrefix will
                 // not be included in the lookup. The case where the
                 // packagePrefix is really a class is handled else where.
                 // WARNING: This code does not expect a class that has an static
                 //          inner class in DEFAULT_IMPORTS
                 ConstructedClassWithPackage tmp =  new ConstructedClassWithPackage(packagePrefix,name);
                 if (resolve(tmpfalsefalsefalse)) {
                     type.setRedirect(tmp.redirect());
                     return true;
                 }
             }
             String name = type.getName();
             if (name.equals("BigInteger")) {
                 type.setRedirect(.);
                 return true;
             } else if (name.equals("BigDecimal")) {
                 type.setRedirect(.);
                 return true;
             }
         }
         return false;
     }
 
     private boolean resolveFromCompileUnit(ClassNode type) {
         // look into the compile unit if there is a class with that name
         CompileUnit compileUnit = .getCompileUnit();
         if (compileUnit == nullreturn false;
         ClassNode cuClass = compileUnit.getClass(type.getName());
         if (cuClass != null) {
             if (type != cuClasstype.setRedirect(cuClass);
             return true;
         }
         return false;
     }
 
     private void setClass(ClassNode nClass cls) {
         ClassNode cn = ClassHelper.make(cls);
         n.setRedirect(cn);
     }
 
     private void ambiguousClass(ClassNode typeClassNode iTypeString name) {
         if (type.getName().equals(iType.getName())) {
             addError("reference to " + name + " is ambiguous, both class " + type.getName() + " and " + iType.getName() + " match"type);
         } else {
             type.setRedirect(iType);
         }
     }
 
     private boolean resolveAliasFromModule(ClassNode type) {
         // In case of getting a ConstructedClassWithPackage here we do not do checks for partial
         // matches with imported classes. The ConstructedClassWithPackage is already a constructed
         // node and any subclass resolving will then take elsewhere place
         if (type instanceof ConstructedClassWithPackagereturn false;
 
         ModuleNode module = .getModule();
         if (module == nullreturn false;
         String name = type.getName();
 
         // check module node imports aliases
         // the while loop enables a check for inner classes which are not fully imported,
         // but visible as the surrounding class is imported and the inner class is public/protected static
         String pname = name;
         int index = name.length();
         /*
          * we have a name foo.bar and an import foo.foo. This means foo.bar is possibly
          * foo.foo.bar rather than foo.bar. This means to cut at the dot in foo.bar and
          * foo for import
          */
         while (true) {
             pname = name.substring(0, index);
             ClassNode aliasedNode = module.getImport(pname);
 
             if (aliasedNode != null) {
                 if (pname.length() == name.length()) {
                     // full match
 
                     // We can compare here by length, because pname is always
                     // a sbustring of name, so same length means they are equal.
                     type.setRedirect(aliasedNode);
                     return true;
                 } else {
                     //partial match
 
                     // At this point we know that we have a match for pname. This may
                     // mean, that name[pname.length()..<-1] is a static inner class.
                     // For this the rest of the name does not need any dots in its name.
                     // It is either completely a inner static class or it is not.
                     // Since we do not want to have useless lookups we create the name
                     // completely and use a ConstructedClassWithPackage to prevent lookups against the package.
                     String className = aliasedNode.getNameWithoutPackage() + '$' +
                                        name.substring(pname.length()+1).replace('.''$');
                     ConstructedClassWithPackage tmp = new ConstructedClassWithPackage(aliasedNode.getPackageName()+"."className);
                     if (resolve(tmptruetruefalse)) {
                         type.setRedirect(tmp.redirect());
                         return true;
                     }
                 }
             }
             index = pname.lastIndexOf('.');
             if (index == -1) break;
         }
         return false;
     }
 
     private boolean resolveFromModule(ClassNode typeboolean testModuleImports) {
         // we decided if we have a vanilla name starting with a lower case
         // letter that we will not try to resolve this name against .*
         // imports. Instead a full import is needed for these.
         // resolveAliasFromModule will do this check for us. This method
         // does also check the module contains a class in the same package
         // of this name. This check is not done for vanilla names starting
         // with a lower case letter anymore
         if (type instanceof LowerCaseClass) {
             return resolveAliasFromModule(type);
         }
 
         String name = type.getName();
         ModuleNode module = .getModule();
         if (module == nullreturn false;
 
         boolean newNameUsed = false;
         // we add a package if there is none yet and the module has one. But we
         // do not add that if the type is a ConstructedClassWithPackage. The code in ConstructedClassWithPackage
         // hasPackageName() will return true if ConstructedClassWithPackage#className has no dots.
         // but since the prefix may have them and the code there does ignore that
         // fact. We check here for ConstructedClassWithPackage.
         if (!type.hasPackageName() && module.hasPackageName() && !(type instanceof ConstructedClassWithPackage)) {
             type.setName(module.getPackageName() + name);
             newNameUsed = true;
         }
         // look into the module node if there is a class with that name
         List moduleClasses = module.getClasses();
         for (Iterator iter = moduleClasses.iterator(); iter.hasNext();) {
             ClassNode mClass = (ClassNodeiter.next();
             if (mClass.getName().equals(type.getName())) {
                 if (mClass != typetype.setRedirect(mClass);
                 return true;
             }
         }
         if (newNameUsedtype.setName(name);
 
         if (testModuleImports) {
             if (resolveAliasFromModule(type)) return true;
 
             if (module.hasPackageName()) {
                 // check package this class is defined in. The usage of ConstructedClassWithPackage here
                 // means, that the module package will not be involved when the
                 // compiler tries to find an inner class.
                 ConstructedClassWithPackage tmp =  new ConstructedClassWithPackage(module.getPackageName(),name);
                 if (resolve(tmpfalsefalsefalse)) {
                     type.setRedirect(tmp.redirect());
                     return true;
                 }
             }
 
             // check module node imports packages
             List packages = module.getImportPackages();
             for (Iterator iter = packages.iterator(); iter.hasNext();) {
                 String packagePrefix = (Stringiter.next();
                 // We limit the inner class lookups here by using ConstructedClassWithPackage.
                 // This way only the name will change, the packagePrefix will
                 // not be included in the lookup. The case where the
                 // packagePrefix is really a class is handled else where.
                 ConstructedClassWithPackage tmp =  new ConstructedClassWithPackage(packagePrefix,name);
                 if (resolve(tmpfalsefalsetrue)) {
                     ambiguousClass(typetmpname);
                     type.setRedirect(tmp.redirect());
                     return true;
                 }
             }
         }
         return false;
     }
 
     private boolean resolveToClass(ClassNode type) {
         String name = type.getName();
 
         // We do not need to check instances of LowerCaseClass
         // to be a Class, because unless there was an import for
         // for this we  do not lookup these cases. This was a decision
         // made on the mailing list. To ensure we will not visit this
         // method again we set a NO_CLASS for this name
         if (type instanceof LowerCaseClass) {
             .put(name,);
         }
 
         // We use here the class cahce cachedClasses to prevent
         // calls to ClassLoader#loadClass. disabling this cache will
         // cause a major performance hit. Unlike at the end of this
         // method we do not return true or false depending on if we
         // want to recompile or not. If the class was cached, then
         // we do not want to recompile, recompilation is already
         // scheduled then
         Object cached = .get(name);
         if (cached == return false;
         // cached == SCRIPT should not happen here!
         if (cached == throw new GroovyBugError("name "+name+" was marked as script, but was not resolved as such");
         if (cached != nullreturn true;
 
         if (.getModule().hasPackageName() && name.indexOf('.') == -1) return false;
         GroovyClassLoader loader = .getClassLoader();
         Class cls;
         try {
             // NOTE: it's important to do no lookup against script files
             // here since the GroovyClassLoader would create a new CompilationUnit
             cls = loader.loadClass(namefalsetrue);
         } catch (ClassNotFoundException cnfe) {
             .put(name);
             return false;
         } catch (CompilationFailedException cfe) {
             .getErrorCollector().addErrorAndContinue(new ExceptionMessage(cfetrue));
             return false;
         }
         //TODO: the case of a NoClassDefFoundError needs a bit more research
         // a simple recompilation is not possible it seems. The current class
         // we are searching for is there, so we should mark that somehow.
         // Basically the missing class needs to be completly compiled before
         // we can again search for the current name.
         /*catch (NoClassDefFoundError ncdfe) {
             cachedClasses.put(name,SCRIPT);
             return false;
         }*/
         if (cls == nullreturn false;
         .put(namecls);
         setClass(typecls);
         //NOTE: we might return false here even if we found a class,
         //      because  we want to give a possible script a chance to
         //      recompile. This can only be done if the loader was not
         //      the instance defining the class.
         return cls.getClassLoader() == loader;
     }
 
 
     public Expression transform(Expression exp) {
         if (exp == nullreturn null;
         Expression ret = null;
         if (exp instanceof VariableExpression) {
             ret = transformVariableExpression((VariableExpressionexp);
         } else if (exp.getClass() == PropertyExpression.class) {
             ret = transformPropertyExpression((PropertyExpressionexp);
         } else if (exp instanceof DeclarationExpression) {
             ret = transformDeclarationExpression((DeclarationExpressionexp);
         } else if (exp instanceof BinaryExpression) {
             ret = transformBinaryExpression((BinaryExpressionexp);
         } else if (exp instanceof MethodCallExpression) {
             ret = transformMethodCallExpression((MethodCallExpressionexp);
         } else if (exp instanceof ClosureExpression) {
             ret = transformClosureExpression((ClosureExpressionexp);
         } else if (exp instanceof ConstructorCallExpression) {
             ret = transformConstructorCallExpression((ConstructorCallExpressionexp);
         } else if (exp instanceof AnnotationConstantExpression) {
         } else {
             resolveOrFail(exp.getType(), exp);
             ret = exp.transformExpression(this);
         }
         if (ret!=null && ret!=expret.setSourcePosition(exp);
         return ret;
     }
 
     private String lookupClassName(PropertyExpression pe) {
         boolean doInitialClassTest=true;
         String name = "";
         // this loop builds a name from right to left each name part
         // separated by "."
         for (Expression it = peit != nullit = ((PropertyExpressionit).getObjectExpression()) {
             if (it instanceof VariableExpression) {
                 VariableExpression ve = (VariableExpressionit;
                 // stop at super and this
                 if (ve.isSuperExpression() || ve.isThisExpression()) {
                     return null;
                 }
                 String varName = ve.getName();
                 if (doInitialClassTest) {
                     // we are at the first name part. This is the right most part.
                     // If this part is in lower case, then we do not need a class
                     // check. other parts of the property expression will be tested
                     // by a different method call to this method, so foo.Bar.bar
                     // can still be resolved to the class foo.Bar and the static
                     // field bar.
                     if (!testVanillaNameForClass(varName)) return null;
                     doInitialClassTestfalse;
                     name = varName;
                 } else {
                     name = varName + "." + name;
                 }
                 break;
             }
             // anything other than PropertyExpressions, ClassExpression or
             // VariableExpressions will stop resolving
             else if (!(it.getClass() == PropertyExpression.class)) {
                 return null;
             } else {
                 PropertyExpression current = (PropertyExpressionit;
                 String propertyPart = current.getPropertyAsString();
                 // the class property stops resolving, dynamic property names too
                 if (propertyPart == null || propertyPart.equals("class")) {
                     return null;
                 }
                 if (doInitialClassTest) {
                     // we are at the first name part. This is the right most part.
                     // If this part is in lower case, then we do not need a class
                     // check. other parts of the property expression will be tested
                     // by a different method call to this method, so foo.Bar.bar
                     // can still be resolved to the class foo.Bar and the static
                     // field bar.
                     if (!testVanillaNameForClass(propertyPart)) return null;
                     doInitialClassTestfalse;
                     name = propertyPart;
                 } else {
                     name = propertyPart + "." + name;
                 }
             }
         }
         if (name.length() == 0) return null;
         return name;
     }
 
     // iterate from the inner most to the outer and check for classes
     // this check will ignore a .class property, for Example Integer.class will be
     // a PropertyExpression with the ClassExpression of Integer as objectExpression
     // and class as property
         LinkedList stack = new LinkedList();
         ClassExpression found = null;
         for (Expression it = peit != nullit = ((PropertyExpressionit).getObjectExpression()) {
             if (it instanceof ClassExpression) {
                 found = (ClassExpressionit;
                 break;
             } else if (!(it.getClass() == PropertyExpression.class)) {
                 return pe;
             }
             stack.addFirst(it);
         }
         if (found == nullreturn pe;
 
         if (stack.isEmpty()) return pe;
         Object stackElement = stack.removeFirst();
         if (!(stackElement.getClass() == PropertyExpression.class)) return pe;
         PropertyExpression classPropertyExpression = (PropertyExpressionstackElement;
         String propertyNamePart = classPropertyExpression.getPropertyAsString();
         if (propertyNamePart == null || !propertyNamePart.equals("class")) return pe;
 
         found.setSourcePosition(classPropertyExpression);
         if (stack.isEmpty()) return found;
         stackElement = stack.removeFirst();
         if (!(stackElement.getClass() == PropertyExpression.class)) return pe;
         PropertyExpression classPropertyExpressionContainer = (PropertyExpressionstackElement;
 
         classPropertyExpressionContainer.setObjectExpression(found);
         return pe;
     }
 
         boolean itlp = ;
         boolean ipe = ;
 
         Expression objectExpression = pe.getObjectExpression();
          = true;
          = !(objectExpression.getClass() == PropertyExpression.class);
         objectExpression = transform(objectExpression);
         // we handle the property part as if it were not part of the property
          = false;
         Expression property = transform(pe.getProperty());
          = itlp;
          = ipe;
 
         boolean spreadSafe = pe.isSpreadSafe();
         PropertyExpression old = pe;
         pe = new PropertyExpression(objectExpressionpropertype.isSafe());
         pe.setSpreadSafe(spreadSafe);
         pe.setSourcePosition(old);
 
         String className = lookupClassName(pe);
         if (className != null) {
             ClassNode type = ClassHelper.make(className);
             if (resolve(type)) {
                 Expression ret =  new ClassExpression(type);
                 ret.setSourcePosition(pe);
                 return ret;
             }
         }
         if (objectExpression instanceof ClassExpression && pe.getPropertyAsString() != null) {
             // possibly an inner class
             ClassExpression ce = (ClassExpressionobjectExpression;
             ClassNode type = ClassHelper.make(ce.getType().getName() + "$" + pe.getPropertyAsString());
             if (resolve(typefalsefalsefalse)) {
                 Expression ret = new ClassExpression(type);
                 ret.setSourcePosition(ce);
                 return ret;
             }
         }
         Expression ret = pe;
         if (ret = correctClassClassChain(pe);
         return ret;
     }
 
         Variable v = ve.getAccessedVariable();
         if (v instanceof DynamicVariable){
             String name = ve.getName();
             ClassNode t = ClassHelper.make(name);
             // asking isResolved here allows to check if a primitive
             // type name like "int" was used to make t. In such a case
             // we have nothing left to do.
             boolean isClass = t.isResolved();
             if (!isClass) {
                 // It was no primitive type, so next we see if the name,
                 // which is a vanilla name, starts with a lower case letter.
                 // In that case we change t to a LowerCaseClass to let the
                 // compiler skip the resolving at several places in this class.
                 if (Character.isLowerCase(name.charAt(0))) {
                   t = new LowerCaseClass(name);
                 }
                 isClass = resolve(t);
                 if(!isClassisClass = resolveToInnerEnum(t);
             }
             if (isClass) {
                 // the name is a type so remove it from the scoping
                 // as it is only a classvariable, it is only in
                 // referencedClassVariables, but must be removed
                 // for each parentscope too
                 for (VariableScope scope = scope != null && !scope.isRoot(); scope = scope.getParent()) {
                     if (scope.isRoot()) break;
                     if (scope.removeReferencedClassVariable(ve.getName()) == nullbreak;
                 }
                 ClassExpression ce = new ClassExpression(t);
                 ce.setSourcePosition(ve);
                 return ce;
             }
         }
         resolveOrFail(ve.getType(), ve);
         return ve;
     }
 
     private boolean testVanillaNameForClass(String name) {
         if (name==null || name.length()==0) return false;
         return !Character.isLowerCase(name.charAt(0));
     }
 
         Expression left = transform(be.getLeftExpression());
         int type = be.getOperation().getType();
         if ((type == . || type == .) &&
                 left instanceof ClassExpression) {
             ClassExpression ce = (ClassExpressionleft;
             String error = "you tried to assign a value to the class '" + ce.getType().getName() + "'";
             if (ce.getType().isScript()) {
                 error += ". Do you have a script with this name?";
             }
             addError(errorbe.getLeftExpression());
             return be;
         }
         if (left instanceof ClassExpression && be.getRightExpression() instanceof ListExpression) {
             // we have C[] if the list is empty -> should be an array then!
             ListExpression list = (ListExpressionbe.getRightExpression();
             if (list.getExpressions().isEmpty()) {
                 return new ClassExpression(left.getType().makeArray());
             }
         }
         Expression right = transform(be.getRightExpression());
         be.setLeftExpression(left);
         be.setRightExpression(right);
         return be;
     }
 
         boolean oldInClosure = ;
          = true;
         Parameter[] paras = ce.getParameters();
         if (paras != null) {
             for (int i = 0; i < paras.lengthi++) {
                 ClassNode t = paras[i].getType();
                 resolveOrFail(tce);
                 if(paras[i].hasInitialExpression()) {
                 	Object initialVal = paras[i].getInitialExpression();
                 	if(initialVal instanceof Expression) {
                 		transform((Expression)initialVal);
                 	}
                 }
             }
         }
         Statement code = ce.getCode();
         if (code != nullcode.visit(this);
          = oldInClosure;
         return ce;
     }
 
         ClassNode type = cce.getType();
         resolveOrFail(typecce);
          = cce.isSpecialCall();
         Expression ret = cce.transformExpression(this);
          = false;
         return ret;
     }
 
         Expression args = transform(mce.getArguments());
         Expression method = transform(mce.getMethod());
         Expression object = transform(mce.getObjectExpression());
 
         MethodCallExpression result = new MethodCallExpression(objectmethodargs);
         result.setSafe(mce.isSafe());
         result.setImplicitThis(mce.isImplicitThis());
         result.setSpreadSafe(mce.isSpreadSafe());
         result.setSourcePosition(mce);
         return result;
     }
 
         Expression oldLeft = de.getLeftExpression();
         Expression left = transform(oldLeft);
         if (left instanceof ClassExpression) {
             ClassExpression ce = (ClassExpressionleft;
             addError("you tried to assign a value to the class " + ce.getType().getName(), oldLeft);
             return de;
         }
         Expression right = transform(de.getRightExpression());
         if (right == de.getRightExpression()) return de;
         DeclarationExpression newDeclExpr = new DeclarationExpression(leftde.getOperation(), right);
         newDeclExpr.setSourcePosition(de);
         return newDeclExpr;
     }
 
         AnnotationNode an = (AnnotationNodeace.getValue();
         ClassNode type = an.getClassNode();
         resolveOrFail(type", unable to find class for annotation"an);
         for (Iterator iter = an.getMembers().entrySet().iterator(); iter.hasNext();) {
             Map.Entry member = (Map.Entryiter.next();
             Expression memberValue = (Expressionmember.getValue();
             member.setValue(transform(memberValue));
         }
 
         return ace;
     }
 
     public void visitAnnotations(AnnotatedNode node) {
         List annotions = node.getAnnotations();
         if (annotions.isEmpty()) return;
         Iterator it = annotions.iterator();
         while (it.hasNext()) {
             AnnotationNode an = (AnnotationNodeit.next();
             //skip builtin properties
             if (an.isBuiltIn()) continue;
             ClassNode type = an.getClassNode();
             resolveOrFail(type",  unable to find class for annotation"an);
             for (Iterator iter = an.getMembers().entrySet().iterator(); iter.hasNext();) {
                 Map.Entry member = (Map.Entryiter.next();
                 Expression memberValue = (Expressionmember.getValue();
                 Expression newValue = transform(memberValue);
                 member.setValue(newValue);
                 if (newValue instanceof PropertyExpression) {
                     PropertyExpression pe = (PropertyExpressionnewValue;
                     if (!(pe.getObjectExpression() instanceof ClassExpression)) {
                         addError("unable to find class for enum",pe.getObjectExpression());
                     }
                 }
             }
         }
     }
 
     public void visitClass(ClassNode node) {
         ClassNode oldNode = ;
          = node;
 
         resolveGenericsHeader(node.getGenericsTypes());
 
         ModuleNode module = node.getModule();
         if (!module.hasImportsResolved()) {
             List l = module.getImports();
             for (Iterator iter = l.iterator(); iter.hasNext();) {
                 ImportNode element = (ImportNodeiter.next();
                 ClassNode type = element.getType();
                 if (resolve(typefalsefalsetrue)) continue;
                 addError("unable to resolve class " + type.getName(), type);
             }
             Map importPackages = module.getStaticImportClasses();
             for (Iterator iter = importPackages.values().iterator(); iter.hasNext();) {
                 ClassNode type = (ClassNodeiter.next();
                 if (resolve(typefalsefalsetrue)) continue;
                 // May be this type belongs in the same package as the node that is doing the
                 // static import. In that case, the package may not have been explicitly specified.
                 // Try with the node's package too. If still not found, revert to original type name.
                 if(type.getPackageName() == null && node.getPackageName() != null) {
                 	String oldTypeName = type.getName();
                	type.setName(node.getPackageName() + "." + oldTypeName);
                	if (resolve(typefalsefalsetrue)) continue;