@Override
 public boolean visit(Statement node) throws Exception {
   for (final PHPSourceElementRequestorExtension visitor : extensions) {
     visitor.visit(node);
   }
   if (node instanceof PHPFieldDeclaration) {
     return visit((PHPFieldDeclaration) node);
   }
   if (node instanceof FieldDeclaration) {
     return visit((FieldDeclaration) node);
   }
   if (node instanceof ConstantDeclaration) {
     return visit((ConstantDeclaration) node);
   }
   if (node instanceof CatchClause) {
     return visit((CatchClause) node);
   }
   if (node instanceof ForEachStatement) {
     return visit((ForEachStatement) node);
   }
   if (node instanceof GlobalStatement) {
     return visit((GlobalStatement) node);
   }
   if (node instanceof UseStatement) {
     return visit((UseStatement) node);
   }
   return true;
 }
 @Override
 public boolean visit(Expression node) throws Exception {
   for (final PHPSourceElementRequestorExtension visitor : extensions) {
     visitor.visit(node);
   }
   if (node instanceof Assignment) {
     return visit((Assignment) node);
   }
   if (node instanceof ListVariable) {
     return visit((ListVariable) node);
   }
   if (node instanceof TypeReference) {
     return visit((TypeReference) node);
   }
   if (node instanceof Include) {
     return visit((Include) node);
   }
   if (node instanceof PHPCallExpression) {
     return visit((PHPCallExpression) node);
   }
   if (node instanceof LambdaFunctionDeclaration) {
     return visit((LambdaFunctionDeclaration) node);
   }
   return true;
 }
  @Override
  protected void modifyMethodInfo(
      MethodDeclaration methodDeclaration, ISourceElementRequestor.MethodInfo mi) {
    Declaration parentDeclaration = null;

    // find declaration that was before this method:
    declarations.pop();
    if (!declarations.empty()) {
      parentDeclaration = declarations.peek();
    }
    declarations.push(methodDeclaration);

    mi.isConstructor =
        mi.name.equalsIgnoreCase(CONSTRUCTOR_NAME)
            || (parentDeclaration instanceof ClassDeclaration
                && mi.name.equalsIgnoreCase(((ClassDeclaration) parentDeclaration).getName()));

    if (fCurrentClass == null || fCurrentClass == fLastNamespace) {
      mi.modifiers |= Modifiers.AccGlobal;
    }

    mi.parameterTypes = processParamterTypes(methodDeclaration);
    mi.returnType = processReturnType(methodDeclaration);

    // modify method info if needed by extensions
    for (final PHPSourceElementRequestorExtension extension : extensions) {
      extension.modifyMethodInfo(methodDeclaration, mi);
    }
  }
  @Override
  public boolean endvisit(MethodDeclaration method) throws Exception {
    methodGlobalVars.pop();
    declarations.pop();

    for (final PHPSourceElementRequestorExtension visitor : extensions) {
      visitor.endvisit(method);
    }
    return super.endvisit(method);
  }
  @Override
  protected void modifyClassInfo(TypeDeclaration typeDeclaration, TypeInfo ti) {
    // check whether this is a namespace
    if (typeDeclaration instanceof NamespaceDeclaration) {
      ti.modifiers |= Modifiers.AccNameSpace;
    }

    // modify class info if needed by extensions
    for (final PHPSourceElementRequestorExtension extension : extensions) {
      extension.modifyClassInfo(typeDeclaration, ti);
    }
  }
  public boolean endvisit(LambdaFunctionDeclaration lambdaMethod) throws Exception {

    methodGlobalVars.pop();
    this.fInMethod = false;

    if (!fNodes.isEmpty() && fNodes.peek() == lambdaMethod) {
      fRequestor.exitMethod(lambdaMethod.sourceEnd() - 1);
      fNodes.pop();
    }
    for (final PHPSourceElementRequestorExtension visitor : extensions) {
      visitor.endvisit(lambdaMethod);
    }
    return true;
  }
  @Override
  public boolean visit(MethodDeclaration method) throws Exception {

    methodGlobalVars.add(new HashSet<String>());

    Declaration parentDeclaration = null;
    if (!declarations.empty()) {
      parentDeclaration = declarations.peek();
    }

    // In case we are entering a nested element - just add to the deferred
    // list
    // and get out of the nested element visiting process
    if (parentDeclaration instanceof MethodDeclaration) {
      deferredDeclarations.add(method);
      return false;
    }

    if (parentDeclaration instanceof InterfaceDeclaration) {
      method.setModifier(Modifiers.AccAbstract);
    }

    declarations.push(method);

    for (final PHPSourceElementRequestorExtension visitor : extensions) {
      visitor.visit(method);
    }

    final boolean visit = super.visit(method);

    if (visit) {
      // Process method argument (local variable) declarations:
      final List<Argument> arguments = method.getArguments();
      for (final Argument arg : arguments) {
        final ISourceElementRequestor.FieldInfo info = new ISourceElementRequestor.FieldInfo();
        info.name = arg.getName();
        info.modifiers = Modifiers.AccPublic;
        info.nameSourceStart = arg.getNameStart();
        info.nameSourceEnd = arg.getNameEnd() - 1;
        info.declarationStart = arg.sourceStart();
        fRequestor.enterField(info);
        fRequestor.exitField(arg.sourceEnd() - 1);
      }
    }
    return visit;
  }
  @Override
  public boolean endvisit(ModuleDeclaration declaration) throws Exception {
    for (final PHPSourceElementRequestorExtension visitor : extensions) {
      visitor.endvisit(declaration);
    }

    while (deferredDeclarations != null && !deferredDeclarations.isEmpty()) {
      final ASTNode[] declarations =
          deferredDeclarations.toArray(new ASTNode[deferredDeclarations.size()]);
      deferredDeclarations.clear();

      for (final ASTNode deferred : declarations) {
        deferred.traverse(this);
      }
    }
    fLastUseParts.clear();
    return super.endvisit(declaration);
  }
  @Override
  public boolean endvisit(TypeDeclaration type) throws Exception {
    if (type instanceof NamespaceDeclaration) {
      final NamespaceDeclaration namespaceDecl = (NamespaceDeclaration) type;
      fLastNamespace = null; // there are no nested namespaces
      if (namespaceDecl.isGlobal()) {
        return true;
      }
    }

    declarations.pop();

    // resolve more type member declarations
    resolveMagicMembers(type);

    for (final PHPSourceElementRequestorExtension visitor : extensions) {
      visitor.endvisit(type);
    }

    return super.endvisit(type);
  }
  public PHPSourceElementRequestor(ISourceElementRequestor requestor, IModuleSource sourceModule) {
    super(requestor);

    // Load PHP source element requester extensions
    final IConfigurationElement[] elements =
        Platform.getExtensionRegistry()
            .getConfigurationElementsFor(PHPCorePlugin.ID, "phpSourceElementRequestors");
    final List<PHPSourceElementRequestorExtension> requestors =
        new ArrayList<PHPSourceElementRequestorExtension>(elements.length);
    for (final IConfigurationElement element : elements) {
      try {
        final PHPSourceElementRequestorExtension extension =
            (PHPSourceElementRequestorExtension) element.createExecutableExtension("class");
        extension.setRequestor(fRequestor);
        extension.setSourceModule(sourceModule);
        requestors.add(extension);
      } catch (final CoreException e) {
        GeneratorBeautifierPlugin.log(e);
        // Logger.logException(e);
      }
    }
    extensions = requestors.toArray(new PHPSourceElementRequestorExtension[requestors.size()]);
  }
  @Override
  public boolean visit(TypeDeclaration type) throws Exception {
    if (type instanceof NamespaceDeclaration) {
      final NamespaceDeclaration namespaceDecl = (NamespaceDeclaration) type;
      fLastNamespace = namespaceDecl;
      fLastUseParts.clear();
      if (namespaceDecl.isGlobal()) {
        return true;
      }
    }

    // In case we are entering a nested element
    if (!declarations.empty() && declarations.peek() instanceof MethodDeclaration) {
      deferredDeclarations.add(type);
      return false;
    }

    declarations.push(type);

    for (final PHPSourceElementRequestorExtension visitor : extensions) {
      visitor.visit(type);
    }
    return super.visit(type);
  }
  public boolean visit(LambdaFunctionDeclaration lambdaMethod) throws Exception {

    fNodes.push(lambdaMethod);
    methodGlobalVars.add(new HashSet<String>());

    // Declaration parentDeclaration = null;
    // if (!declarations.empty()
    // && declarations.peek() instanceof MethodDeclaration) {
    // parentDeclaration = declarations.peek();
    // // In case we are entering a nested element - just add to the
    // // deferred list and get out of the nested element visiting process
    // deferredDeclarations.add(lambdaMethod);
    // return visitGeneral(lambdaMethod);
    // }

    final Collection<FormalParameter> arguments = lambdaMethod.getArguments();
    final StringBuilder metadata = new StringBuilder();
    final String[] parameters = new String[arguments.size()];
    // if (arguments != null) {
    final Iterator<FormalParameter> i = arguments.iterator();
    int indx = 0;
    while (i.hasNext()) {
      final Argument arg = i.next();
      metadata.append(arg.getName());
      parameters[indx] = arg.getName();
      indx++;
      if (i.hasNext()) {
        metadata.append(",");
      }
    }
    // }

    // Add method declaration:
    for (final PHPSourceElementRequestorExtension visitor : extensions) {
      visitor.visit(lambdaMethod);
    }

    final ISourceElementRequestor.MethodInfo mi = new ISourceElementRequestor.MethodInfo();
    mi.parameterNames = parameters;
    mi.name = PHPCoreConstants.ANONYMOUS;
    mi.modifiers = Modifiers.AccPublic;
    mi.nameSourceStart = lambdaMethod.sourceStart();
    mi.nameSourceEnd = lambdaMethod.sourceEnd();
    mi.declarationStart = mi.nameSourceStart;
    mi.isConstructor = false;

    this.fRequestor.enterMethod(mi);
    this.fInMethod = true;

    for (final Argument arg : arguments) {
      final ISourceElementRequestor.FieldInfo info = new ISourceElementRequestor.FieldInfo();
      info.name = arg.getName();
      info.modifiers = Modifiers.AccPublic;
      info.nameSourceStart = arg.getNameStart();
      info.nameSourceEnd = arg.getNameEnd() - 1;
      info.declarationStart = arg.sourceStart();
      fRequestor.enterField(info);
      fRequestor.exitField(arg.sourceEnd() - 1);
    }

    return true;
  }