gt:             for (final ClassType type : extend.getTypes())
context: /*
   * Copyright (C) 2012 Klaus Reimer <k@ailis.de>
   * See LICENSE.md for licensing information.
   */
  
  package de.ailis.jasdoc.tree;
  
  import org.mozilla.javascript.Token;
  import org.mozilla.javascript.ast.Assignment;
  import org.mozilla.javascript.ast.AstNode;
  import org.mozilla.javascript.ast.ExpressionStatement;
  import org.mozilla.javascript.ast.FunctionNode;
  import org.mozilla.javascript.ast.Name;
  import org.mozilla.javascript.ast.NodeVisitor;
  import org.mozilla.javascript.ast.ObjectLiteral;
  import org.mozilla.javascript.ast.ObjectProperty;
  import org.mozilla.javascript.ast.PropertyGet;
  import org.mozilla.javascript.ast.VariableDeclaration;
  import org.mozilla.javascript.ast.VariableInitializer;
  
  import de.ailis.jasdoc.doc.Configuration;
  import de.ailis.jasdoc.tags.ConstructorTag;
  import de.ailis.jasdoc.tags.ExtendsTag;
  import de.ailis.jasdoc.tags.ImplementsTag;
  import de.ailis.jasdoc.tags.InterfaceTag;
  import de.ailis.jasdoc.tags.TagParser;
  import de.ailis.jasdoc.tags.Tags;
  import de.ailis.jasdoc.types.ClassType;
  import de.ailis.jasdoc.types.TypeFactory;
  
  /**
   * Builds a symbol tree. Let it visit all the parsed JavaScript root nodes.
   *
   * @author Klaus Reimer (k@ailis.de)
   */
  public final class TreeBuilder implements NodeVisitor
  {
      /** The type factory. */
      private final TypeFactory typeFactory;
  
      /** The tag parser. */
      private final TagParser annoParser;
  
      /** The root node of the tree. */
      private final RootNode root = new RootNode();
  
      /**
       * Constructor.
       *
       * @param config
       *            The configuration.
       */
      public TreeBuilder(final Configuration config)
      {
          this.typeFactory = new TypeFactory();
          this.annoParser = new TagParser(config, this.typeFactory);
      }
  
      /**
       * @see NodeVisitor#visit(AstNode)
       */
      @Override
      public boolean visit(final AstNode node)
      {
          switch (node.getType())
          {
              case Token.SCRIPT:
                  return true;
  
              case Token.FUNCTION:
                  return visitFunctionNode((FunctionNode) node, null, null,
                      this.root);
  
              case Token.EXPR_RESULT:
                  return visitExpressionStatement((ExpressionStatement) node,
                      this.root);
  
              case Token.VAR:
                  return visitVariableDeclaration((VariableDeclaration) node,
                      this.root);
  
              default:
                  return false;
          }
      }
  
      /**
       * Visits a variable declaration.
       *
       * @param node
       *            The variable declaration node.
       * @param scope
       *            The current scope.
       * @return True if child nodes must be visited, false if not.
       */
      private boolean visitVariableDeclaration(final VariableDeclaration node,
          final Node scope)
      {
          String varJsDoc = node.getJsDoc();
          for (final VariableInitializer variable : node.getVariables())
          {
              // Get variable name. Ignore variable if it can't be parsed.
              final AstNode target = variable.getTarget();
              if (target.getType() != Token.NAME) continue;
              final String name = ((Name) target).getIdentifier();
  
              // Get JsDoc
              final String jsDoc = getJsDoc(variable, varJsDoc);
  
              // Process the variable value
              final AstNode initializer = variable.getInitializer();
              if (initializer == null)
                  visitPropertyName((Name) target, jsDoc, scope);
              else if (initializer.getType() == Token.FUNCTION)
                  visitFunctionNode((FunctionNode) initializer, name, jsDoc,
                      scope);
              else if (initializer.getType() == Token.OBJECTLIT)
                  visitObjectLiteral((ObjectLiteral) initializer, name, jsDoc,
                      scope);
              else
                  visitPropertyValue(initializer, name, jsDoc, scope);
  
              // Resetting variable declaration JsDoc because it is only usable
              // for first declared variable.
              varJsDoc = null;
          }
          return false;
      }
  
      /**
       * Visits an expression statement.
       *
       * @param node
       *            The expression statement node.
       * @param scope
       *            The current scope.
       * @return True if child nodes must be visited, false if not.
       */
      private boolean visitExpressionStatement(final ExpressionStatement node,
          final Node scope)
      {
          final AstNode expression = node.getExpression();
          if (expression.getType() == Token.ASSIGN)
              return visitAssignment((Assignment) expression, scope);
          else if (expression.getType() == Token.NAME)
              return visitPropertyName((Name) expression, null, scope);
          return false;
      }
  
      /**
       * Returns the JavaScript documentation string from the specified node. If
       * it doesn't have one then the specified fallback is returned instead
       *
       * @param node
       *            The node to read the documentation string from.
       * @param fallback
       *            The fallback documentation string.
       * @return The documentation string to use.
       */
      private String getJsDoc(final AstNode node, final String fallback)
      {
          final String jsDoc = node.getJsDoc();
          if (jsDoc != null) return jsDoc;
          return fallback;
      }
  
      /**
       * Visits an assignment.
       *
       * @param node
       *            The assignment node.
       * @param scope
       *            The current scope.
       * @return True if child nodes must be visited, false if not.
       */
      private boolean visitAssignment(final Assignment node,
          final Node scope)
      {
          final AstNode left = node.getLeft();
          String jsDoc = node.getJsDoc();
          String name;
          Node parentScope;
  
          // If left is a NAME then it is a direct assignment
          if (left.getType() == Token.NAME)
          {
              name = ((Name) left).getIdentifier();
              parentScope = scope;
              jsDoc = getJsDoc(left, jsDoc);
          }
  
          // If left is a GEPROP then it is a scoped assignment
          else if (left.getType() == Token.GETPROP)
          {
              final PropertyGet propertyGet = (PropertyGet) left;
              name = propertyGet.getProperty().getIdentifier();
              jsDoc = getJsDoc(left, jsDoc);
              parentScope = getScope(propertyGet.getTarget(), scope);
          }
  
          // Otherwise we don't know what to do with it.
          else
              return false;
  
          // Process the assignment value
          final AstNode right = node.getRight();
          if (right.getType() == Token.FUNCTION)
          {
              return visitFunctionNode((FunctionNode) right, name, jsDoc,
                  parentScope);
          }
          else if (right.getType() == Token.OBJECTLIT)
          {
              return visitObjectLiteral((ObjectLiteral) right, name, jsDoc,
                  parentScope);
          }
          else
          {
              return visitPropertyValue(right, name, jsDoc, parentScope);
          }
      }
  
      /**
       * Visits a function node.
       *
       * @param node
       *            The function node to visit.
       * @param name
       *            The function name. If null then name is read from function
       *            itself (Which only works when function is not anonymous)
       * @param jsDoc
       *            JavaScript documentation string from a possible parent node
       *            location. Null if none.
       * @param scope
       *            The parent scope of the function.
       * @return True if child nodes must be visited, false if not.
       */
      private boolean visitFunctionNode(final FunctionNode node,
          final String name, final String jsDoc, final Node scope)
      {
          // Get the function name. Ignore function node if not found.
          final String funcName = name == null ? node.getName() : name;
          if (funcName == null) return false;
  
          // Get the function tags
          final Tags tags =
              this.annoParser.parse(getJsDoc(node, jsDoc), node);
  
          // Build the function documentation.
          final FuncNode func = new FuncNode(funcName, tags, scope);
  
          // Add parameters to function documentation.
          for (final AstNode paramNode : node.getParams())
              func.addParameter(processParam(paramNode, func));
  
          scope.addChildNode(func);
  
          // Create interface nodes for implemented interfaces
          final ImplementsTag implement =
              tags.getTag(ImplementsTag.class);
          if (implement != null)
          {
              for (final ClassType type : implement.getTypes())
                  createInterfaceNode(type.getName());
          }
  
          // Create interface nodes for implemented interfaces
          final ExtendsTag extend =
              tags.getTag(ExtendsTag.class);
          if (extend != null)
          {
  