private void addFolds(
      BaseDocument doc,
      List<? extends ASTElement> elements,
      Map<String, List<OffsetRange>> folds,
      List<OffsetRange> codeblocks)
      throws BadLocationException {
    for (ASTElement element : elements) {
      ElementKind kind = element.getKind();
      switch (kind) {
        case FIELD:
        case METHOD:
        case CONSTRUCTOR:
        case CLASS:
        case MODULE:
          ASTNode node = element.getNode();
          OffsetRange range = ASTUtils.getRangeFull(node, doc);

          // beware of synthetic elements
          if ((kind == ElementKind.METHOD && !((MethodNode) node).isSynthetic())
              || (kind == ElementKind.CONSTRUCTOR && !((ConstructorNode) node).isSynthetic())
              || (kind == ElementKind.FIELD
                  && ((FieldNode) node).getInitialExpression() instanceof ClosureExpression)
              // Only make nested classes/modules foldable, similar to what the java editor is doing
              || (range.getStart() > Utilities.getRowStart(doc, range.getStart()))
                  && kind != ElementKind.FIELD) {

            int start = range.getStart();
            // Start the fold at the END of the line behind last non-whitespace, remove curly brace,
            // if any
            start = Utilities.getRowLastNonWhite(doc, start);
            if (start >= 0 && doc.getChars(start, 1)[0] != '{') {
              start++;
            }
            int end = range.getEnd();
            if (start != (-1) && end != (-1) && start < end && end <= doc.getLength()) {
              range = new OffsetRange(start, end);
              codeblocks.add(range);
            }
          }
          break;
      }

      List<? extends ASTElement> children = element.getChildren();

      if (children != null && children.size() > 0) {
        addFolds(doc, children, folds, codeblocks);
      }
    }
  }
    @Override
    public String getHtml(HtmlFormatter formatter) {
      formatter.appendText(node.getName());

      if ((kind == ElementKind.METHOD) || (kind == ElementKind.CONSTRUCTOR)) {
        // Append parameters
        ASTMethod jn = (ASTMethod) node;

        Collection<String> parameters = jn.getParameters();

        if ((parameters != null) && (parameters.size() > 0)) {
          formatter.appendHtml("(");
          formatter.parameters(true);

          for (Iterator<String> it = parameters.iterator(); it.hasNext(); ) {
            String ve = it.next();
            // TODO - if I know types, list the type here instead. For now, just use the parameter
            // name instead
            formatter.appendText(ve);

            if (it.hasNext()) {
              formatter.appendHtml(", ");
            }
          }

          formatter.parameters(false);
          formatter.appendHtml(")");
        } else {
          formatter.appendHtml("()");
        }
      }

      return formatter.getText();
    }
 private GroovyStructureItem(ASTElement node, ParserResult info) {
   this.node = node;
   this.kind = node.getKind();
   this.info = info;
   // FIXME true or false ?
   this.doc = (BaseDocument) info.getSnapshot().getSource().getDocument(false);
 }
 @Override
 public long getEndPosition() {
   if (doc != null) {
     OffsetRange range = ASTUtils.getRangeFull(node.getNode(), doc);
     LOG.log(Level.FINEST, "getEndPosition(), end: {0}", range.getEnd());
     return (long) range.getEnd();
   }
   return 0;
 }
 private static boolean isVisible(ASTElement element) {
   // FIXME perhaps we should store synthetic atributte in AstElement
   if ((element.getKind() == ElementKind.METHOD)) {
     ASTMethod method = (ASTMethod) element;
     ASTNode node = method.getNode();
     return !(node instanceof MethodNode)
         || (!((MethodNode) node).isSynthetic() && ((MethodNode) node).getLineNumber() >= 0);
   }
   return true;
 }
    @Override
    public List<? extends StructureItem> getNestedItems() {
      List<ASTElement> nested = node.getChildren();

      if ((nested != null) && (nested.size() > 0)) {
        List<GroovyStructureItem> children = new ArrayList<GroovyStructureItem>(nested.size());

        // FIXME: the same old problem: AstElement != ElementHandle.

        for (ASTElement co : nested) {
          if (isVisible(co)) {
            children.add(new GroovyStructureItem(co, info));
          }
        }

        return children;
      } else {
        return Collections.emptyList();
      }
    }
 @Override
 public Set<Modifier> getModifiers() {
   return node.getModifiers();
 }
 @Override
 public String getName() {
   return node.getName();
 }
  private void scan(
      GroovyParserResult result,
      ASTNode node,
      AstPath path,
      String in,
      Set<String> includes,
      ASTElement parent) {
    if (node instanceof AnnotatedNode && !((AnnotatedNode) node).hasNoRealSourcePosition()) {

      if (node instanceof ClassNode) {
        ASTClass co = new ASTClass(result, node);
        co.setFqn(((ClassNode) node).getName());

        if (parent != null) {
          parent.addChild(co);
        } else {
          structure.add(co);
        }

        parent = co;
      } else if (node instanceof FieldNode) {
        if (parent instanceof ASTClass) {
          // We don't have unique declarations, only assignments (possibly many)
          // so stash these in a map and extract unique fields when we're done
          Set<FieldNode> assignments = fields.get(parent);

          if (assignments == null) {
            assignments = new HashSet<FieldNode>();
            fields.put((ASTClass) parent, assignments);
          }

          assignments.add((FieldNode) node);
        }
      } else if (node instanceof MethodNode) {
        ASTMethod co = new ASTMethod(result, node);
        methods.add(co);
        co.setIn(in);

        // TODO - don't add this to the top level! Make a nested list
        if (parent != null) {
          parent.addChild(co);
        } else {
          structure.add(co);
        }
      } else if (node instanceof PropertyNode) {
        Set<PropertyNode> declarations = properties.get(parent);

        if (declarations == null) {
          declarations = new HashSet<PropertyNode>();
          properties.put((ASTClass) parent, declarations);
        }

        declarations.add((PropertyNode) node);
      }
    }

    @SuppressWarnings("unchecked")
    List<ASTNode> list = ASTUtils.children(node);

    for (ASTNode child : list) {
      path.descend(child);
      scan(result, child, path, in, includes, parent);
      path.ascend();
    }
  }