@Override
  public void process() {
    EclipseMessager messager = (EclipseMessager) _env.getMessager();

    // obtain the declaration of the annotation we want to process
    AnnotationTypeDeclaration annoDecl =
        (AnnotationTypeDeclaration) _env.getTypeDeclaration(JSFillChildrenMethod.class.getName());

    // get the annotated types
    Collection<Declaration> annotatedTypes = _env.getDeclarationsAnnotatedWith(annoDecl);

    for (Declaration decl : annotatedTypes) {
      Collection<AnnotationMirror> mirrors = decl.getAnnotationMirrors();

      // for each annotation found, get a map of element name/value pairs
      for (AnnotationMirror mirror : mirrors) {
        if (!"JSFillChildrenMethod"
            .equals(mirror.getAnnotationType().getDeclaration().getSimpleName())) {
          continue;
        }
        MethodDeclaration methodDeclaration = (MethodDeclaration) decl;
        JSJPQLMethodProcessor.checkReturnType(mirror, methodDeclaration, messager);
        JSJPQLMethodProcessor.checkTransferInfo(mirror, methodDeclaration, true, messager);
        JSJPQLMethodProcessor.checkIfAnnotationValueAttributeIsEntity(mirror, "parent", messager);
      }
    }
  }
    public int compare(Declaration d1, Declaration d2) {
      if (equals(d1, d2)) return 0;

      SourcePosition p1 = d1.getPosition();
      SourcePosition p2 = d2.getPosition();

      if (p1 == null && p2 != null) return 1;
      else if (p1 != null && p2 == null) return -1;
      else if (p1 == null && p2 == null) return compareEqualPosition(d1, d2);
      else {
        assert p1 != null && p2 != null;
        int fileComp = p1.file().compareTo(p2.file());
        if (fileComp == 0) {
          long diff = (long) p1.line() - (long) p2.line();
          if (diff == 0) {
            diff = Long.signum((long) p1.column() - (long) p2.column());
            if (diff != 0) return (int) diff;
            else {
              // declarations may be two
              // compiler-generated members with the
              // same source position
              return compareEqualPosition(d1, d2);
            }
          } else return (diff < 0) ? -1 : 1;
        } else return fileComp;
      }
    }
  /**
   * Visits a class declaration.
   *
   * @param d the declaration to visit
   */
  public void visitClassDeclaration(ClassDeclaration d) {
    d.accept(pre);

    SortedSet<Declaration> decls = new TreeSet<Declaration>(SourceOrderDeclScanner.comparator);

    for (TypeParameterDeclaration tpDecl : d.getFormalTypeParameters()) {
      decls.add(tpDecl);
    }

    for (FieldDeclaration fieldDecl : d.getFields()) {
      decls.add(fieldDecl);
    }

    for (MethodDeclaration methodDecl : d.getMethods()) {
      decls.add(methodDecl);
    }

    for (TypeDeclaration typeDecl : d.getNestedTypes()) {
      decls.add(typeDecl);
    }

    for (ConstructorDeclaration ctorDecl : d.getConstructors()) {
      decls.add(ctorDecl);
    }

    for (Declaration decl : decls) decl.accept(this);

    d.accept(post);
  }
  public void visitExecutableDeclaration(ExecutableDeclaration d) {
    d.accept(pre);

    SortedSet<Declaration> decls = new TreeSet<Declaration>(SourceOrderDeclScanner.comparator);

    for (TypeParameterDeclaration tpDecl : d.getFormalTypeParameters()) decls.add(tpDecl);

    for (ParameterDeclaration pDecl : d.getParameters()) decls.add(pDecl);

    for (Declaration decl : decls) decl.accept(this);

    d.accept(post);
  }
  @Override
  public ValidationResult validateRootElement(RootElementDeclaration rootElementDeclaration) {
    ValidationResult result = super.validateRootElement(rootElementDeclaration);
    String namespace = rootElementDeclaration.getNamespace();
    if (namespace == null) {
      namespace = "";
    }

    if (namespace.isEmpty()) {
      result.addError(rootElementDeclaration, "Root element should not be in the empty namespace.");
    }

    if (rootElementDeclaration.getName().toLowerCase().startsWith("web")) {
      result.addWarning(
          rootElementDeclaration,
          "You probably don't want a root element that starts with the name 'web'. Consider renaming using the @XmlRootElement annotation.");
    }

    JsonElementWrapper elementWrapper =
        rootElementDeclaration.getAnnotation(JsonElementWrapper.class);
    if (namespace.startsWith(CommonModels.GEDCOMX_DOMAIN) && elementWrapper == null) {
      result.addWarning(
          rootElementDeclaration,
          "Root elements in the '"
              + CommonModels.GEDCOMX_DOMAIN
              + "' namespace should probably be annotated with @"
              + JsonElementWrapper.class.getSimpleName()
              + ".");
    }

    if (elementWrapper != null) {
      String jsonName = elementWrapper.namespace() + elementWrapper.name();
      Declaration previous = this.jsonNameDeclarations.put(jsonName, rootElementDeclaration);
      if (previous != null) {
        result.addError(
            rootElementDeclaration,
            "JSON name conflict with " + String.valueOf(previous.getPosition()));
      }
    }

    return result;
  }
 private Map<String, AnnotationValue> getAnnotationValues(
     Declaration declaration, String annotationName) {
   Collection<AnnotationMirror> annotationMirrors = declaration.getAnnotationMirrors();
   for (AnnotationMirror annotationMirror : annotationMirrors) {
     if (annotationName.equals(
         annotationMirror.getAnnotationType().getDeclaration().getQualifiedName())) {
       return getAnnotationValues(annotationMirror);
     }
   }
   return null;
 }
 @Override
 public ValidationResult validate(EnunciateFreemarkerModel model) {
   ValidationResult result = super.validate(model);
   for (SchemaInfo schemaInfo : model.getNamespacesToSchemas().values()) {
     for (Registry registry : schemaInfo.getRegistries()) {
       Collection<LocalElementDeclaration> localElements = registry.getLocalElementDeclarations();
       for (LocalElementDeclaration localElement : localElements) {
         JsonElementWrapper elementWrapper = localElement.getAnnotation(JsonElementWrapper.class);
         if (elementWrapper != null) {
           String jsonName = elementWrapper.namespace() + elementWrapper.name();
           Declaration previous = this.jsonNameDeclarations.put(jsonName, localElement);
           if (previous != null) {
             result.addError(
                 localElement,
                 "JSON name conflict with " + String.valueOf(previous.getPosition()));
           }
         }
       }
     }
   }
   return result;
 }
    @SuppressWarnings("cast")
    private int compareEqualPosition(Declaration d1, Declaration d2) {
      assert d1.getPosition() == d2.getPosition();

      DeclPartialOrder dpo1 = new DeclPartialOrder();
      DeclPartialOrder dpo2 = new DeclPartialOrder();

      d1.accept(dpo1);
      d2.accept(dpo2);

      int difference = dpo1.getValue() - dpo2.getValue();
      if (difference != 0) return difference;
      else {
        int result = d1.getSimpleName().compareTo(d2.getSimpleName());
        if (result != 0) return result;
        return (int)
            (Long.signum((long) System.identityHashCode(d1) - (long) System.identityHashCode(d2)));
      }
    }
 private static int staticAdjust(Declaration d) {
   return d.getModifiers().contains(Modifier.STATIC) ? 0 : 1;
 }
 static boolean equals(Declaration d1, Declaration d2) {
   return d1 == d2 || (d1 != null && d1.equals(d2));
 }
 private boolean suppressWarning(Declaration declaration, String warning) {
   SuppressWarnings suppressionInfo = declaration.getAnnotation(SuppressWarnings.class);
   return suppressionInfo != null && Arrays.asList(suppressionInfo.value()).contains(warning);
 }
 public void error(Declaration declaration, String message) {
   env_.getMessager().printError(declaration.getPosition(), message);
 }