/**
   * A loop driver for applying operations to all primary ClassNodes in our AST. Automatically skips
   * units that have already been processed through the current phase.
   */
  public void applyToPrimaryClassNodes(PrimaryClassNodeOperation body)
      throws CompilationFailedException {
    // GRECLIPSE: start
    /*old{
    Iterator classNodes = getPrimaryClassNodes(body.needSortedInput()).iterator();
    }*/
    // newcode
    List primaryClassNodes = getPrimaryClassNodes(body.needSortedInput());
    Iterator classNodes = primaryClassNodes.iterator();
    // end
    while (classNodes.hasNext()) {
      SourceUnit context = null;
      try {
        ClassNode classNode = (ClassNode) classNodes.next();
        context = classNode.getModule().getContext();
        // GRECLIPSE get to the bottom of this - why are operations running multiple times that
        // should only run once?
        if (context == null
            || context.phase < phase
            || (context.phase == phase && !context.phaseComplete)) {

          int offset = 1;
          Iterator<InnerClassNode> iterator = classNode.getInnerClasses();
          while (iterator.hasNext()) {
            iterator.next();
            offset++;
          }
          body.call(context, new GeneratorContext(this.ast, offset), classNode);
        }
      } catch (CompilationFailedException e) {
        // fall through, getErrorReporter().failIfErrors() will trigger
      } catch (NullPointerException npe) {
        throw npe;
      } catch (GroovyBugError e) {
        changeBugText(e, context);
        throw e;
      } catch (Exception e) {
        // check the exception for a nested compilation exception
        ErrorCollector nestedCollector = null;
        for (Throwable next = e.getCause(); next != e && next != null; next = next.getCause()) {
          if (!(next instanceof MultipleCompilationErrorsException)) continue;
          MultipleCompilationErrorsException mcee = (MultipleCompilationErrorsException) next;
          nestedCollector = mcee.collector;
          break;
        }

        if (nestedCollector != null) {
          getErrorCollector().addCollectorContents(nestedCollector);
        } else {
          getErrorCollector().addError(new ExceptionMessage(e, configuration.getDebug(), this));
        }
      }
    }

    getErrorCollector().failIfErrors();
  }
        public void call(SourceUnit source) throws CompilationFailedException {
          List<ClassNode> classes = source.ast.getClasses();
          for (ClassNode node : classes) {
            CompileUnit cu = node.getCompileUnit();
            for (Iterator iter = cu.iterateClassNodeToCompile(); iter.hasNext(); ) {
              String name = (String) iter.next();
              SourceUnit su = ast.getScriptSourceLocation(name);
              List<ClassNode> classesInSourceUnit = su.ast.getClasses();
              StringBuilder message = new StringBuilder();
              message
                  .append("Compilation incomplete: expected to find the class ")
                  .append(name)
                  .append(" in ")
                  .append(su.getName());
              if (classesInSourceUnit.isEmpty()) {
                message.append(", but the file seems not to contain any classes");
              } else {
                message.append(", but the file contains the classes: ");
                boolean first = true;
                for (ClassNode cn : classesInSourceUnit) {
                  if (!first) {
                    message.append(", ");
                  } else {
                    first = false;
                  }
                  message.append(cn.getName());
                }
              }

              getErrorCollector()
                  .addErrorAndContinue(new SimpleMessage(message.toString(), CompilationUnit.this));
              iter.remove();
            }
          }
        }
 private void addPrivateFieldAndMethodAccessors(ClassNode node) {
   addPrivateBridgeMethods(node);
   addPrivateFieldsAccessors(node);
   Iterator<InnerClassNode> it = node.getInnerClasses();
   while (it.hasNext()) {
     addPrivateFieldAndMethodAccessors(it.next());
   }
 }
  /**
   * Returns whether a classNode has the specified property or not
   *
   * @param classNode The ClassNode
   * @param propertyName The name of the property
   * @return True if the property exists in the ClassNode
   */
  public static boolean hasProperty(ClassNode classNode, String propertyName) {
    if (classNode == null || propertyName == null || "".equals(propertyName.trim())) return false;

    List properties = classNode.getProperties();
    for (Iterator i = properties.iterator(); i.hasNext(); ) {
      PropertyNode pn = (PropertyNode) i.next();
      if (pn.getName().equals(propertyName)) return true;
    }
    return false;
  }
 /**
  * Tests whether the ClassNode implements the specified method name
  *
  * @param classNode The ClassNode
  * @param methodName The method name
  * @param argTypes
  * @return True if it implements the method
  */
 private static boolean implementsMethod(
     ClassNode classNode, String methodName, Class[] argTypes) {
   List methods = classNode.getMethods();
   for (Iterator i = methods.iterator(); i.hasNext(); ) {
     MethodNode mn = (MethodNode) i.next();
     final boolean isZeroArg = (argTypes == null || argTypes.length == 0);
     boolean methodMatch = mn.getName().equals(methodName) && isZeroArg;
     if (methodMatch) return true;
     // TODO Implement further parameter analysis
   }
   return false;
 }
 private void injectAssociationProperties(ClassNode classNode, List propertiesToAdd) {
   for (Iterator i = propertiesToAdd.iterator(); i.hasNext(); ) {
     PropertyNode pn = (PropertyNode) i.next();
     if (! /*GrailsASTUtils.*/hasProperty(classNode, pn.getName())) {
       // if(LOG.isDebugEnabled()) {
       //    LOG.debug("[GrailsDomainInjector] Adding property [" + pn.getName() + "] to class [" +
       // classNode.getName() + "]");
       // }
       classNode.addProperty(pn);
     }
   }
 }
 private List createPropertiesForHasManyExpression(Expression e, ClassNode classNode) {
   List properties = new ArrayList();
   if (e instanceof MapExpression) {
     MapExpression me = (MapExpression) e;
     List mapEntries = me.getMapEntryExpressions();
     for (Iterator j = mapEntries.iterator(); j.hasNext(); ) {
       MapEntryExpression mee = (MapEntryExpression) j.next();
       Expression keyExpression = mee.getKeyExpression();
       String key = keyExpression.getText();
       addAssociationForKey(key, properties, classNode);
     }
   }
   return properties;
 }
  private Collection createPropertiesForBelongsToExpression(Expression e, ClassNode classNode) {
    List properties = new ArrayList();
    if (e instanceof MapExpression) {
      MapExpression me = (MapExpression) e;
      List mapEntries = me.getMapEntryExpressions();
      for (Iterator i = mapEntries.iterator(); i.hasNext(); ) {
        MapEntryExpression mme = (MapEntryExpression) i.next();
        String key = mme.getKeyExpression().getText();

        String type = mme.getValueExpression().getText();

        properties.add(
            new PropertyNode(
                key, Modifier.PUBLIC, ClassHelper.make(type), classNode, null, null, null));
      }
    }

    return properties;
  }
  private void injectAssociations(ClassNode classNode) {

    List properties = classNode.getProperties();
    List propertiesToAdd = new ArrayList();
    for (Iterator p = properties.iterator(); p.hasNext(); ) {
      PropertyNode pn = (PropertyNode) p.next();
      final boolean isHasManyProperty =
          pn.getName().equals(/*GrailsDomainClassProperty.*/ RELATES_TO_MANY)
              || pn.getName().equals(/*GrailsDomainClassProperty.*/ HAS_MANY);
      if (isHasManyProperty) {
        Expression e = pn.getInitialExpression();
        propertiesToAdd.addAll(createPropertiesForHasManyExpression(e, classNode));
      }
      final boolean isBelongsTo = pn.getName().equals(/*GrailsDomainClassProperty.*/ BELONGS_TO);
      if (isBelongsTo) {
        Expression e = pn.getInitialExpression();
        propertiesToAdd.addAll(createPropertiesForBelongsToExpression(e, classNode));
      }
    }
    injectAssociationProperties(classNode, propertiesToAdd);
  }
 @Override
 public void visitClass(final ClassNode node) {
   boolean skip = shouldSkipClassNode(node);
   if (!skip && !anyMethodSkip(node)) {
     node.putNodeMetaData(MopWriter.Factory.class, StaticCompilationMopWriter.FACTORY);
   }
   ClassNode oldCN = classNode;
   classNode = node;
   Iterator<InnerClassNode> innerClasses = classNode.getInnerClasses();
   while (innerClasses.hasNext()) {
     InnerClassNode innerClassNode = innerClasses.next();
     boolean innerStaticCompile = !(skip || isSkippedInnerClass(innerClassNode));
     innerClassNode.putNodeMetaData(STATIC_COMPILE_NODE, innerStaticCompile);
     innerClassNode.putNodeMetaData(
         WriterControllerFactory.class, node.getNodeMetaData(WriterControllerFactory.class));
     if (innerStaticCompile && !anyMethodSkip(innerClassNode)) {
       innerClassNode.putNodeMetaData(MopWriter.Factory.class, StaticCompilationMopWriter.FACTORY);
     }
   }
   super.visitClass(node);
   addPrivateFieldAndMethodAccessors(node);
   classNode = oldCN;
 }
  private List getPrimaryClassNodes(boolean sort) {
    if (sort == true) {
      List<ModuleNode> sortedModules = this.ast.getSortedModules();
      if (sortedModules != null) {
        return sortedModules;
      }
    }
    // FIXASC (groovychange) rewritten
    /*old{
    List unsorted = new ArrayList();
    Iterator modules = this.ast.getModules().iterator();
    while (modules.hasNext()) {
        ModuleNode module = (ModuleNode) modules.next();

        Iterator classNodes = module.getClasses().iterator();
        while (classNodes.hasNext()) {
            ClassNode classNode = (ClassNode) classNodes.next();
            unsorted.add(classNode);
        }
    }
    */
    // new
    List<ClassNode> unsorted = new ArrayList<ClassNode>();
    for (ModuleNode module : this.ast.getModules()) {
      unsorted.addAll(module.getClasses());
    }
    // FIXASC (groovychange) end

    if (!sort) return unsorted;

    // GRECLIPSE: start: rewritten sort algorithm
    /*old{
            int[] indexClass = new int[unsorted.size()];
            int[] indexInterface = new int[unsorted.size()];
            {
                int i = 0;
                for (Iterator<ClassNode> iter = unsorted.iterator(); iter.hasNext(); i++) {
                    ClassNode element = iter.next();
                    if (element.isInterface()) {
                        indexInterface[i] = getSuperInterfaceCount(element);
                        indexClass[i] = -1;
                    } else {
                        indexClass[i] = getSuperClassCount(element);
                        indexInterface[i] = -1;
                    }
                }
            }

            List<ClassNode> sorted = getSorted(indexInterface, unsorted);
            sorted.addAll(getSorted(indexClass, unsorted));
    */
    // newcode:
    // Sort them by how many types are in their hierarchy, but all interfaces first.
    // Algorithm:
    // Create a list of integers.  Each integer captures the index into the unsorted
    // list (bottom 16bits) and the count of how many types are in that types
    // hierarchy (top 16bits).  For classes the count is augmented by 2000 so that
    // when sorting the classes will come out after the interfaces.
    // This list of integers is sorted.  We then just go through it and for the
    // lower 16bits of each entry (0xffff) that is the index of the next value to
    // pull from the unsorted list and put into the sorted list.
    // Will break down if more than 2000 interfaces in the type hierarchy for an
    // individual type, or a project contains > 65535 files... but if you've got
    // that kind of setup, you have other problems...
    List<Integer> countIndexPairs = new ArrayList<Integer>();
    {
      int i = 0;
      for (Iterator iter = unsorted.iterator(); iter.hasNext(); i++) {
        ClassNode node = (ClassNode) iter.next();
        if (node.isInterface()) {
          countIndexPairs.add((getSuperInterfaceCount(node) << 16) + i);
        } else {
          countIndexPairs.add(((getSuperClassCount(node) + 2000) << 16) + i);
        }
      }
    }
    Collections.sort(countIndexPairs);
    List sorted = new ArrayList();
    for (int i : countIndexPairs) {
      sorted.add(unsorted.get(i & 0xffff));
    }
    this.ast.setSortedModules(sorted);
    // end
    return sorted;
  }