@SuppressWarnings("unchecked")
  public boolean visit(MethodDeclaration method) throws Exception {
    fNodes.push(method);
    methodGlobalVars.add(new HashSet<String>());
    int modifiers = method.getModifiers();
    PHPDocBlock doc = null;
    if (method instanceof IPHPDocAwareDeclaration) {
      IPHPDocAwareDeclaration declaration = (IPHPDocAwareDeclaration) method;
      doc = declaration.getPHPDoc();
    }
    Declaration parentDeclaration = null;
    if (!declarations.empty()) {
      parentDeclaration = declarations.peek();
    }
    declarations.push(method);

    // 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) {
      if (fCurrentNamespace == null) {
        deferredDeclarations.add(method);
      } else {
        deferredNamespacedDeclarations.add(method);
      }
      return visitGeneral(method);
    }

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

    String methodName = method.getName();

    // Determine whether this method represents constructor:
    if (methodName.equalsIgnoreCase(CONSTRUCTOR_NAME)
        || (parentDeclaration instanceof ClassDeclaration
            && methodName.equalsIgnoreCase(((ClassDeclaration) parentDeclaration).getName()))) {
      modifiers |= IPHPModifiers.Constructor;
    }

    if (parentDeclaration == null
        || (parentDeclaration instanceof TypeDeclaration
            && parentDeclaration == fCurrentNamespace)) {
      modifiers |= Modifiers.AccGlobal;
    }
    if (!Flags.isPrivate(modifiers)
        && !Flags.isProtected(modifiers)
        && !Flags.isPublic(modifiers)) {
      modifiers |= Modifiers.AccPublic;
    }

    modifiers = markAsDeprecated(modifiers, method);

    StringBuilder metadata = new StringBuilder();
    if (fCurrentQualifier != null) {
      metadata.append(fCurrentQualifierCounts.get(fCurrentQualifier));
      metadata.append(";"); // $NON-NLS-1$
    }
    List<Argument> arguments = method.getArguments();
    if (arguments != null) {
      Iterator<Argument> i = arguments.iterator();
      while (i.hasNext()) {
        Argument arg = (Argument) i.next();

        String type = NULL_VALUE;
        if (arg instanceof FormalParameter) {
          FormalParameter fp = (FormalParameter) arg;
          if (fp.getParameterType() != null) {
            if (fp.getParameterType().getName() != null) {
              type = fp.getParameterType().getName();
            }
          }
        }
        if (type == NULL_VALUE && doc != null) {
          type = getParamType(doc, arg.getName(), type);
        }

        metadata.append(type);
        metadata.append(PARAMETER_SEPERATOR);
        metadata.append(arg.getName());
        metadata.append(PARAMETER_SEPERATOR);
        String defaultValue = NULL_VALUE;
        if (arg.getInitialization() != null) {
          if (arg.getInitialization() instanceof Literal) {
            Literal scalar = (Literal) arg.getInitialization();
            defaultValue = scalar.getValue();
          } else {
            defaultValue = DEFAULT_VALUE;
          }
        }
        metadata.append(defaultValue);
        if (i.hasNext()) {
          metadata.append(","); // $NON-NLS-1$
        }
      }
    }

    // Add method declaration:
    modifyDeclaration(
        method,
        new DeclarationInfo(
            IModelElement.METHOD,
            modifiers,
            method.sourceStart(),
            method.sourceEnd() - method.sourceStart(),
            method.getNameStart(),
            method.getNameEnd() - method.getNameStart(),
            methodName,
            metadata.length() == 0 ? null : metadata.toString(),
            encodeDocInfo(method),
            fCurrentQualifier,
            fCurrentParent));

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

    return visitGeneral(method);
  }
  /**
   * Resolve class members that were defined using the @property tag
   *
   * @param type declaration for wich we add the magic variables
   */
  private void resolveMagicMembers(TypeDeclaration type) {
    if (type instanceof IPHPDocAwareDeclaration) {
      IPHPDocAwareDeclaration declaration = (IPHPDocAwareDeclaration) type;
      final PHPDocBlock doc = declaration.getPHPDoc();
      if (doc != null) {
        for (PHPDocTag docTag : doc.getTags()) {
          final int tagKind = docTag.getTagKind();
          if (tagKind == PHPDocTag.PROPERTY
              || tagKind == PHPDocTag.PROPERTY_READ
              || tagKind == PHPDocTag.PROPERTY_WRITE) {
            // http://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_tags.property.pkg.html
            final String[] split = WHITESPACE_SEPERATOR.split(docTag.getValue().trim());
            if (split.length < 2) {
              break;
            }

            String name = removeParenthesis(split);
            int offset = docTag.sourceStart();
            int length = docTag.sourceStart() + 9;

            Map<String, String> info = new HashMap<String, String>();
            info.put("v", split[0]); // $NON-NLS-1$

            StringBuilder metadata = new StringBuilder();
            if (fCurrentQualifier != null) {
              metadata.append(fCurrentQualifierCounts.get(fCurrentQualifier));
              metadata.append(";"); // $NON-NLS-1$
            }

            modifyDeclaration(
                null,
                new DeclarationInfo(
                    IModelElement.FIELD,
                    Modifiers.AccPublic,
                    offset,
                    length,
                    offset,
                    length,
                    name,
                    metadata.length() == 0 ? null : metadata.toString(),
                    encodeDocInfo(info),
                    fCurrentQualifier,
                    fCurrentParent));

          } else if (tagKind == PHPDocTag.METHOD) {
            // http://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_tags.method.pkg.html
            String[] split = WHITESPACE_SEPERATOR.split(docTag.getValue().trim());
            if (split.length < 2) {
              break;
            }
            int methodModifiers = Modifiers.AccPublic;
            if (Constants.STATIC.equals(split[0].trim())) {
              if (split.length < 3) {
                break;
              }
              methodModifiers |= Modifiers.AccStatic;
              split = Arrays.copyOfRange(split, 1, split.length);
            }

            String name = removeParenthesis(split);
            int index = name.indexOf('(');
            if (index > 0) {
              name = name.substring(0, index);
            }
            int offset = docTag.sourceStart();
            int length = docTag.sourceStart() + 6;
            Map<String, String> info = new HashMap<String, String>();
            info.put("r", split[0]); // $NON-NLS-1$

            StringBuilder metadata = new StringBuilder();
            if (fCurrentQualifier != null) {
              metadata.append(fCurrentQualifierCounts.get(fCurrentQualifier));
              metadata.append(";"); // $NON-NLS-1$
            }

            modifyDeclaration(
                null,
                new DeclarationInfo(
                    IModelElement.METHOD,
                    methodModifiers,
                    offset,
                    length,
                    offset,
                    length,
                    name,
                    metadata.length() == 0 ? null : metadata.toString(),
                    encodeDocInfo(info),
                    fCurrentQualifier,
                    fCurrentParent));
          }
        }
      }
    }
  }
  /**
   * Resolve class members that were defined using the @property tag
   *
   * @param type declaration for wich we add the magic variables
   */
  private void resolveMagicMembers(TypeDeclaration type) {
    if (type instanceof IPHPDocAwareDeclaration) {
      IPHPDocAwareDeclaration declaration = (IPHPDocAwareDeclaration) type;
      final PHPDocBlock doc = declaration.getPHPDoc();
      if (doc != null) {
        Pattern WHITESPACE_SEPERATOR = MagicMemberUtil.WHITESPACE_SEPERATOR;
        final PHPDocTag[] tags = doc.getTags();
        for (PHPDocTag docTag : tags) {
          final int tagKind = docTag.getTagKind();
          if (tagKind == PHPDocTag.PROPERTY
              || tagKind == PHPDocTag.PROPERTY_READ
              || tagKind == PHPDocTag.PROPERTY_WRITE) {
            // http://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_tags.property.pkg.html
            final String[] split = WHITESPACE_SEPERATOR.split(docTag.getValue().trim());
            if (split.length < 2) {
              continue;
            }
            ISourceElementRequestor.FieldInfo info = new ISourceElementRequestor.FieldInfo();
            info.modifiers = Modifiers.AccPublic | IPHPModifiers.AccMagicProperty;
            info.name = split[1];
            info.type = split[0];

            SimpleReference var =
                new SimpleReference(
                    docTag.sourceStart(), docTag.sourceStart() + 9, removeParenthesis(split));
            info.nameSourceStart = var.sourceStart();
            info.nameSourceEnd = var.sourceEnd();
            info.declarationStart = info.nameSourceStart;

            fRequestor.enterField(info);
            fRequestor.exitField(info.nameSourceEnd);

          } else if (tagKind == PHPDocTag.METHOD) {
            // http://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_tags.method.pkg.html

            // workaround for lack of method return type
            int methodModifiers = Modifiers.AccPublic;
            String docTagValue = docTag.getValue().trim();
            int index = docTagValue.indexOf('(');
            if (index != -1) {
              String[] split = WHITESPACE_SEPERATOR.split(docTagValue.substring(0, index).trim());
              if (split.length == 1) {
                docTagValue =
                    new StringBuilder(VOID_RETURN_TYPE)
                        .append(Constants.SPACE)
                        .append(docTagValue)
                        .toString();
              } else if (split.length == 2 && Constants.STATIC.equals(split[0])) {
                StringBuilder sb = new StringBuilder(Constants.STATIC);
                sb.append(Constants.SPACE).append(VOID_RETURN_TYPE);
                sb.append(docTagValue.substring(6));
                docTagValue = sb.toString();
              }
            }
            String[] split = WHITESPACE_SEPERATOR.split(docTagValue);
            if (split.length < 2) {
              continue;
            }
            if (Constants.STATIC.equals(split[0])) {
              methodModifiers |= Modifiers.AccStatic;
              split = Arrays.copyOfRange(split, 1, split.length);
              docTagValue = docTagValue.substring(7).trim();
              if (split.length < 2) {
                continue;
              }
            }

            ISourceElementRequestor.MethodInfo mi = new ISourceElementRequestor.MethodInfo();
            mi.parameterNames = null;
            mi.name = removeParenthesis(split);
            SimpleReference var =
                new SimpleReference(
                    docTag.sourceStart(), docTag.sourceStart() + 6, removeParenthesis(split));
            mi.modifiers = methodModifiers;
            mi.nameSourceStart = var.sourceStart();
            mi.nameSourceEnd = var.sourceEnd();
            mi.declarationStart = mi.nameSourceStart;
            mi.isConstructor = false;
            mi.returnType = split[0];

            MagicMethod magicMethod;
            if (mi.name != null && mi.name.indexOf('(') > 0) {
              magicMethod = MagicMemberUtil.getMagicMethod2(docTagValue);
              mi.name = magicMethod.name;
            } else {
              magicMethod = MagicMemberUtil.getMagicMethod(docTagValue);
            }
            if (magicMethod != null) {
              mi.parameterNames = magicMethod.parameterNames;
              mi.parameterTypes = magicMethod.parameterTypes;
              mi.parameterInitializers = magicMethod.parameterInitializers;
            }

            this.fRequestor.enterMethod(mi);
            this.fRequestor.exitMethod(mi.nameSourceEnd);
          }
        }
      }
    }
  }