private void assertMembersNamesAreUnique() {
    Map<String, FieldNode> allDslCollectionFieldNodesOfHierarchy = new HashMap<String, FieldNode>();

    for (ClassNode level : ASTHelper.getHierarchyOfDSLObjectAncestors(annotatedClass)) {
      for (FieldNode field : level.getFields()) {
        if (!ASTHelper.isListOrMap(field.getType())) continue;

        String memberName = getElementNameForCollectionField(field);

        FieldNode conflictingField = allDslCollectionFieldNodesOfHierarchy.get(memberName);

        if (conflictingField != null) {
          addCompileError(
              String.format(
                  "Member name %s is used more than once: %s:%s and %s:%s",
                  memberName,
                  field.getOwner().getName(),
                  field.getName(),
                  conflictingField.getOwner().getName(),
                  conflictingField.getName()),
              field);
          return;
        }

        allDslCollectionFieldNodesOfHierarchy.put(memberName, field);
      }
    }
  }
  /**
   * This method is used to add "bridge" methods for private methods of an inner/outer class, so
   * that the outer class is capable of calling them. It does basically the same job as access$000
   * like methods in Java.
   *
   * @param node an inner/outer class node for which to generate bridge methods
   */
  @SuppressWarnings("unchecked")
  private void addPrivateBridgeMethods(final ClassNode node) {
    Set<ASTNode> accessedMethods =
        (Set<ASTNode>) node.getNodeMetaData(StaticTypesMarker.PV_METHODS_ACCESS);
    if (accessedMethods == null) return;
    List<MethodNode> methods = new ArrayList<MethodNode>(node.getAllDeclaredMethods());
    Map<MethodNode, MethodNode> privateBridgeMethods =
        (Map<MethodNode, MethodNode>) node.getNodeMetaData(PRIVATE_BRIDGE_METHODS);
    if (privateBridgeMethods != null) {
      // private bridge methods already added
      return;
    }
    privateBridgeMethods = new HashMap<MethodNode, MethodNode>();
    int i = -1;
    final int access = Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC;
    for (MethodNode method : methods) {
      if (accessedMethods.contains(method)) {
        i++;
        Parameter[] methodParameters = method.getParameters();
        Parameter[] newParams = new Parameter[methodParameters.length + 1];
        System.arraycopy(methodParameters, 0, newParams, 1, methodParameters.length);
        newParams[0] = new Parameter(node.getPlainNodeReference(), "$that");
        Expression arguments;
        if (method.getParameters() == null || method.getParameters().length == 0) {
          arguments = ArgumentListExpression.EMPTY_ARGUMENTS;
        } else {
          List<Expression> args = new LinkedList<Expression>();
          for (Parameter parameter : methodParameters) {
            args.add(new VariableExpression(parameter));
          }
          arguments = new ArgumentListExpression(args);
        }
        Expression receiver =
            method.isStatic() ? new ClassExpression(node) : new VariableExpression(newParams[0]);
        MethodCallExpression mce = new MethodCallExpression(receiver, method.getName(), arguments);
        mce.setMethodTarget(method);

        ExpressionStatement returnStatement = new ExpressionStatement(mce);
        MethodNode bridge =
            node.addMethod(
                "access$" + i,
                access,
                method.getReturnType(),
                newParams,
                method.getExceptions(),
                returnStatement);
        privateBridgeMethods.put(method, bridge);
        bridge.addAnnotation(new AnnotationNode(COMPILESTATIC_CLASSNODE));
      }
    }
    if (!privateBridgeMethods.isEmpty()) {
      node.setNodeMetaData(PRIVATE_BRIDGE_METHODS, privateBridgeMethods);
    }
  }
  /** Adds special accessors for private constants so that inner classes can retrieve them. */
  @SuppressWarnings("unchecked")
  private void addPrivateFieldsAccessors(ClassNode node) {
    Set<ASTNode> accessedFields =
        (Set<ASTNode>) node.getNodeMetaData(StaticTypesMarker.PV_FIELDS_ACCESS);
    if (accessedFields == null) return;
    Map<String, MethodNode> privateConstantAccessors =
        (Map<String, MethodNode>) node.getNodeMetaData(PRIVATE_FIELDS_ACCESSORS);
    if (privateConstantAccessors != null) {
      // already added
      return;
    }
    int acc = -1;
    privateConstantAccessors = new HashMap<String, MethodNode>();
    final int access = Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC;
    for (FieldNode fieldNode : node.getFields()) {
      if (accessedFields.contains(fieldNode)) {

        acc++;
        Parameter param = new Parameter(node.getPlainNodeReference(), "$that");
        Expression receiver =
            fieldNode.isStatic() ? new ClassExpression(node) : new VariableExpression(param);
        Statement stmt =
            new ExpressionStatement(new PropertyExpression(receiver, fieldNode.getName()));
        MethodNode accessor =
            node.addMethod(
                "pfaccess$" + acc,
                access,
                fieldNode.getOriginType(),
                new Parameter[] {param},
                ClassNode.EMPTY_ARRAY,
                stmt);
        privateConstantAccessors.put(fieldNode.getName(), accessor);
      }
    }
    node.setNodeMetaData(PRIVATE_FIELDS_ACCESSORS, privateConstantAccessors);
  }
 /**
  * Dequeues any source units add through addSource and resets the compiler phase to
  * initialization.
  *
  * <p>Note: this does not mean a file is recompiled. If a SourceUnit has already passed a phase it
  * is skipped until a higher phase is reached.
  *
  * @return true if there was a queued source
  * @throws CompilationFailedException
  */
 protected boolean dequeued() throws CompilationFailedException {
   boolean dequeue = !queuedSources.isEmpty();
   while (!queuedSources.isEmpty()) {
     SourceUnit su = queuedSources.removeFirst();
     String name = su.getName();
     // GRECLIPSE: start
     if (iterating) {
       GroovyBugError gbe =
           new GroovyBugError(
               "Damaging 'names' whilst already iterating.  Name getting added is '"
                   + su.getName()
                   + "'");
       gbe.printStackTrace();
       throw gbe;
     }
     // end
     names.add(name);
     sources.put(name, su);
   }
   if (dequeue) {
     gotoPhase(Phases.INITIALIZATION);
   }
   return dequeue;
 }
  private static void doAddGlobalTransforms(
      ASTTransformationsContext context, boolean isFirstScan) {
    final CompilationUnit compilationUnit = context.getCompilationUnit();
    GroovyClassLoader transformLoader = compilationUnit.getTransformLoader();
    Map<String, URL> transformNames = new LinkedHashMap<String, URL>();
    try {
      Enumeration<URL> globalServices =
          transformLoader.getResources(
              "META-INF/services/org.codehaus.groovy.transform.ASTTransformation");
      while (globalServices.hasMoreElements()) {
        URL service = globalServices.nextElement();
        String className;
        BufferedReader svcIn = null;
        try {
          svcIn = new BufferedReader(new InputStreamReader(service.openStream()));
          try {
            className = svcIn.readLine();
          } catch (IOException ioe) {
            compilationUnit
                .getErrorCollector()
                .addError(
                    new SimpleMessage(
                        "IOException reading the service definition at "
                            + service.toExternalForm()
                            + " because of exception "
                            + ioe.toString(),
                        null));
            continue;
          }
          Set<String> disabledGlobalTransforms =
              compilationUnit.getConfiguration().getDisabledGlobalASTTransformations();
          if (disabledGlobalTransforms == null) disabledGlobalTransforms = Collections.emptySet();
          while (className != null) {
            if (!className.startsWith("#") && className.length() > 0) {
              if (!disabledGlobalTransforms.contains(className)) {
                if (transformNames.containsKey(className)) {
                  if (!service.equals(transformNames.get(className))) {
                    compilationUnit
                        .getErrorCollector()
                        .addWarning(
                            WarningMessage.POSSIBLE_ERRORS,
                            "The global transform for class "
                                + className
                                + " is defined in both "
                                + transformNames.get(className).toExternalForm()
                                + " and "
                                + service.toExternalForm()
                                + " - the former definition will be used and the latter ignored.",
                            null,
                            null);
                  }

                } else {
                  transformNames.put(className, service);
                }
              }
            }
            try {
              className = svcIn.readLine();
            } catch (IOException ioe) {
              compilationUnit
                  .getErrorCollector()
                  .addError(
                      new SimpleMessage(
                          "IOException reading the service definition at "
                              + service.toExternalForm()
                              + " because of exception "
                              + ioe.toString(),
                          null));
              //noinspection UnnecessaryContinue
              continue;
            }
          }
        } finally {
          if (svcIn != null) svcIn.close();
        }
      }
    } catch (IOException e) {
      // FIXME the warning message will NPE with what I have :(
      compilationUnit
          .getErrorCollector()
          .addError(
              new SimpleMessage(
                  "IO Exception attempting to load global transforms:" + e.getMessage(), null));
    }
    try {
      Class.forName("java.lang.annotation.Annotation"); // test for 1.5 JVM
    } catch (Exception e) {
      // we failed, notify the user
      StringBuffer sb = new StringBuffer();
      sb.append("Global ASTTransformations are not enabled in retro builds of groovy.\n");
      sb.append("The following transformations will be ignored:");
      for (Map.Entry<String, URL> entry : transformNames.entrySet()) {
        sb.append('\t');
        sb.append(entry.getKey());
        sb.append('\n');
      }
      compilationUnit
          .getErrorCollector()
          .addWarning(
              new WarningMessage(WarningMessage.POSSIBLE_ERRORS, sb.toString(), null, null));
      return;
    }

    // record the transforms found in the first scan, so that in the 2nd scan, phase operations
    // can be added for only for new transforms that have come in
    if (isFirstScan) {
      for (Map.Entry<String, URL> entry : transformNames.entrySet()) {
        context.getGlobalTransformNames().add(entry.getKey());
      }
      addPhaseOperationsForGlobalTransforms(
          context.getCompilationUnit(), transformNames, isFirstScan);
    } else {
      Iterator<Map.Entry<String, URL>> it = transformNames.entrySet().iterator();
      while (it.hasNext()) {
        Map.Entry<String, URL> entry = it.next();
        if (!context.getGlobalTransformNames().add(entry.getKey())) {
          // phase operations for this transform class have already been added before, so remove
          // from current scan cycle
          it.remove();
        }
      }
      addPhaseOperationsForGlobalTransforms(
          context.getCompilationUnit(), transformNames, isFirstScan);
    }
  }