Package org.aspectj.org.eclipse.jdt.internal.codeassist.complete

Source Code of org.aspectj.org.eclipse.jdt.internal.codeassist.complete.CompletionJavadocParser

/*******************************************************************************
* Copyright (c) 2000, 2007 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*******************************************************************************/
package org.aspectj.org.eclipse.jdt.internal.codeassist.complete;

import java.util.ArrayList;
import java.util.List;

import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.core.compiler.InvalidInputException;
import org.aspectj.org.eclipse.jdt.internal.codeassist.CompletionEngine;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.aspectj.org.eclipse.jdt.internal.compiler.parser.JavadocParser;
import org.aspectj.org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
import org.aspectj.org.eclipse.jdt.internal.compiler.parser.TerminalTokens;

/**
* Parser specialized for decoding javadoc comments which includes cursor location for code completion.
*/
public class CompletionJavadocParser extends JavadocParser {
 
  // Initialize lengthes for block and inline tags tables
  public final static int INLINE_ALL_TAGS_LENGTH;
  public final static int BLOCK_ALL_TAGS_LENGTH;
  static {
    int length = 0;
    for (int i=0; i<INLINE_TAGS_LENGTH; i++) {
      length += INLINE_TAGS[i].length;
    }
    INLINE_ALL_TAGS_LENGTH  = length;
    length = 0;
    for (int i=0; i<BLOCK_TAGS_LENGTH; i++) {
      length += BLOCK_TAGS[i].length;
    }
    BLOCK_ALL_TAGS_LENGTH = length;
  }
 
  // Level tags are array of inline/block tags depending on compilation source level
  char[][][] levelTags = new char[2][][];
  int[] levelTagsLength = new int[2];
 
  // Completion specific info
  int cursorLocation;
  CompletionOnJavadoc completionNode = null;
  boolean pushText = false;
  boolean allPossibleTags = false;

  public CompletionJavadocParser(CompletionParser sourceParser) {
    super(sourceParser);
    this.scanner = new CompletionScanner(ClassFileConstants.JDK1_3);
    this.kind = COMPLETION_PARSER | TEXT_PARSE;
    this.reportProblems = false;
    initLevelTags();
  }

  /*
   * Do not parse comment if completion location is not included.
   */
  public boolean checkDeprecation(int commentPtr) {
    this.cursorLocation = ((CompletionParser)sourceParser).cursorLocation;
    CompletionScanner completionScanner = (CompletionScanner)this.scanner;
    completionScanner.cursorLocation = this.cursorLocation;
    this.javadocStart = this.sourceParser.scanner.commentStarts[commentPtr];
    this.javadocEnd = this.sourceParser.scanner.commentStops[commentPtr];
    if (this.javadocStart <= this.cursorLocation && this.cursorLocation <= this.javadocEnd) {
      if (CompletionEngine.DEBUG) {
        System.out.println("COMPLETION in Javadoc:"); //$NON-NLS-1$
      }
      completionScanner.completionIdentifier = null;
      this.firstTagPosition = 1;
      super.checkDeprecation(commentPtr);
    } else {
      this.docComment = null;
    }
    return false;
  }

  /*
   * Replace stored Javadoc node with specific completion one.
   */
  protected boolean commentParse() {
    this.docComment = new CompletionJavadoc(this.javadocStart, this.javadocEnd);
    return super.commentParse();
  }

  /*
   * Create argument expression. If it includes completion location, create and store completion node.
   */
  protected Object createArgumentReference(char[] name, int dim, boolean isVarargs, Object typeRef, long[] dimPositions, long argNamePos) throws InvalidInputException {
    // Create argument as we may need it after
    char[] argName = name==null ? CharOperation.NO_CHAR : name;
    Expression expression = (Expression) super.createArgumentReference(argName, dim, isVarargs, typeRef, dimPositions, argNamePos);
    // See if completion location is in argument
    int refStart = ((TypeReference)typeRef).sourceStart;
    int refEnd = ((TypeReference)typeRef).sourceEnd;
    boolean inCompletion = (refStart <= this.cursorLocation && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers
      || ((refStart == (refEnd+1) && refEnd == this.cursorLocation)); // or it's a completion on empty token
    if (this.completionNode == null && inCompletion) {
      JavadocArgumentExpression javadocArgument = (JavadocArgumentExpression) expression;
      TypeReference expressionType = javadocArgument.argument.type;
      if (expressionType instanceof JavadocSingleTypeReference) {
        this.completionNode = new CompletionOnJavadocSingleTypeReference((JavadocSingleTypeReference) expressionType);
      } else if (expressionType instanceof JavadocQualifiedTypeReference) {
        this.completionNode = new CompletionOnJavadocQualifiedTypeReference((JavadocQualifiedTypeReference) expressionType);
      }
      if (CompletionEngine.DEBUG) {
        System.out.println("  completion argument="+completionNode); //$NON-NLS-1$
      }
      return this.completionNode;
    }
    return expression;
  }

  /*
   * Create field reference. If it includes completion location, create and store completion node.
   */
  protected Object createFieldReference(Object receiver) throws InvalidInputException {
    int refStart = (int) (this.identifierPositionStack[0] >>> 32);
    int refEnd = (int) this.identifierPositionStack[0];
    boolean inCompletion = (refStart <= (this.cursorLocation+1) && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers
      || ((refStart == (refEnd+1) && refEnd == this.cursorLocation)) // or it's a completion on empty token
      || (this.memberStart == this.cursorLocation); // or it's a completion just after the member separator with an identifier after the cursor
    if (inCompletion) {
      JavadocFieldReference fieldRef = (JavadocFieldReference) super.createFieldReference(receiver);
      char[] name = this.sourceParser.compilationUnit.getMainTypeName();
      TypeDeclaration typeDecl = getParsedTypeDeclaration();
      if (typeDecl != null) {
        name = typeDecl.name;
      }
      this.completionNode = new CompletionOnJavadocFieldReference(fieldRef, this.memberStart, name);
      if (CompletionEngine.DEBUG) {
        System.out.println("  completion field="+completionNode); //$NON-NLS-1$
      }
      return this.completionNode;
    }
    return super.createFieldReference(receiver);
  }

  /*
   * Verify if method identifier positions include completion location.
   * If so, create method reference and store it.
   * Otherwise return null as we do not need this reference.
   */
  protected Object createMethodReference(Object receiver, List arguments) throws InvalidInputException {
    int memberPtr = this.identifierLengthStack[0] - 1; // may be > 0 for inner class constructor reference
    int refStart = (int) (this.identifierPositionStack[memberPtr] >>> 32);
    int refEnd = (int) this.identifierPositionStack[memberPtr];
    boolean inCompletion = (refStart <= (this.cursorLocation+1) && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers
      || ((refStart == (refEnd+1) && refEnd == this.cursorLocation)) // or it's a completion on empty token
      || (this.memberStart == this.cursorLocation); // or it's a completion just after the member separator with an identifier after the cursor
    if (inCompletion) {
      ASTNode node = (ASTNode) super.createMethodReference(receiver, arguments);
      if (node instanceof JavadocMessageSend) {
        JavadocMessageSend messageSend = (JavadocMessageSend) node;
        int nameStart = (int) (messageSend.nameSourcePosition >>> 32);
        int nameEnd = (int) messageSend.nameSourcePosition;
        if ((nameStart <= (this.cursorLocation+1) && this.cursorLocation <= nameEnd)) {
          this.completionNode = new CompletionOnJavadocFieldReference(messageSend, this.memberStart);
        } else {
          this.completionNode = new CompletionOnJavadocMessageSend(messageSend, this.memberStart);
        }
      } else if (node instanceof JavadocAllocationExpression) {
        this.completionNode = new CompletionOnJavadocAllocationExpression((JavadocAllocationExpression)node, this.memberStart);
      }
      if (CompletionEngine.DEBUG) {
        System.out.println("  completion method="+completionNode); //$NON-NLS-1$
      }
      return this.completionNode;
    }
    return super.createMethodReference(receiver, arguments);
  }

  /*
   * Create type reference. If it includes completion location, create and store completion node.
   */
  protected Object createTypeReference(int primitiveToken) {
    // Need to create type ref in case it was needed by members
    int nbIdentifiers = this.identifierLengthStack[this.identifierLengthPtr];
    int startPtr = this.identifierPtr - (nbIdentifiers-1);
    int refStart = (int) (this.identifierPositionStack[startPtr] >>> 32);
    int refEnd = (int) this.identifierPositionStack[this.identifierPtr];
    boolean inCompletion = (refStart <= (this.cursorLocation+1) && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers
      || ((refStart == (refEnd+1) && refEnd == this.cursorLocation)); // or it's a completion on empty token
    if (!inCompletion) {
      return super.createTypeReference(primitiveToken);
    }
    this.identifierLengthPtr--;
    if (nbIdentifiers == 1) { // Single Type ref
      this.completionNode = new CompletionOnJavadocSingleTypeReference(
            this.identifierStack[this.identifierPtr],
            this.identifierPositionStack[this.identifierPtr],
            this.tagSourceStart,
            this.tagSourceEnd);
    } else if (nbIdentifiers > 1) { // Qualified Type ref
      for (int i=startPtr; i<this.identifierPtr; i++) {
        int start = (int) (this.identifierPositionStack[i] >>> 32);
        int end = (int) this.identifierPositionStack[i];
        if (start <= this.cursorLocation && this.cursorLocation <= end) {
          if (i == startPtr) {
            this.completionNode = new CompletionOnJavadocSingleTypeReference(
                  this.identifierStack[startPtr],
                  this.identifierPositionStack[startPtr],
                  this.tagSourceStart,
                  this.tagSourceEnd);
          } else {
            char[][] tokens = new char[i][];
            System.arraycopy(this.identifierStack, startPtr, tokens, 0, i);
            long[] positions = new long[i+1];
            System.arraycopy(this.identifierPositionStack, startPtr, positions, 0, i+1);
            this.completionNode = new CompletionOnJavadocQualifiedTypeReference(tokens, this.identifierStack[i], positions, this.tagSourceStart, this.tagSourceEnd);
          }
          break;
        }
      }
      if (this.completionNode == null) {
        char[][] tokens = new char[nbIdentifiers-1][];
        System.arraycopy(this.identifierStack, startPtr, tokens, 0, nbIdentifiers-1);
        long[] positions = new long[nbIdentifiers];
        System.arraycopy(this.identifierPositionStack, startPtr, positions, 0, nbIdentifiers);
        this.completionNode = new CompletionOnJavadocQualifiedTypeReference(tokens, this.identifierStack[this.identifierPtr], positions, this.tagSourceStart, this.tagSourceEnd);
      }
    }

    if (CompletionEngine.DEBUG) {
      System.out.println("  completion partial qualified type="+completionNode); //$NON-NLS-1$
    }
    return this.completionNode;
  }

  /*
   * Get possible tags for a given prefix.
   */
  private char[][][] possibleTags(char[] prefix, boolean newLine) {
    char[][][] possibleTags = new char[2][][];
    if (newLine) {
      System.arraycopy(this.levelTags[BLOCK_IDX], 0, possibleTags[BLOCK_IDX] = new char[this.levelTagsLength[BLOCK_IDX]][], 0, this.levelTagsLength[BLOCK_IDX]);
    } else {
      possibleTags[BLOCK_IDX] = CharOperation.NO_CHAR_CHAR;
    }
    System.arraycopy(this.levelTags[INLINE_IDX], 0, possibleTags[INLINE_IDX] = new char[this.levelTagsLength[INLINE_IDX]][], 0, this.levelTagsLength[INLINE_IDX]);
    if (prefix == null || prefix.length == 0) return possibleTags;
    int kinds = levelTags.length;
    for (int k=0; k<kinds; k++) {
      int length = possibleTags[k].length, size = 0;
      int indexes[] = new int[length];
      for (int i=0; i<length; i++) {
        if (CharOperation.prefixEquals(prefix, possibleTags[k][i], false)) {
          indexes[size++] = i;
        }
      }
      char[][] tags = new char[size][];
      for (int i=0; i<size; i++) {
        tags[i] = possibleTags[k][indexes[i]];
      }
      possibleTags[k] = tags;
    }
    return possibleTags;
  }

  private CompletionJavadoc getCompletionJavadoc() {
    return (CompletionJavadoc)this.docComment;
  }

  private CompletionParser getCompletionParser() {
    return (CompletionParser)this.sourceParser;
  }

  /*
   * Init tags arrays for current source level.
   */
  private void initLevelTags() {
    int level = ((int)(this.complianceLevel >>> 16)) - ClassFileConstants.MAJOR_VERSION_1_1 + 1;
    // Init block tags
    this.levelTags[BLOCK_IDX] = new char[BLOCK_ALL_TAGS_LENGTH][];
    this.levelTagsLength[BLOCK_IDX] = 0;
    for (int i=0; i<=level; i++) {
      int length = BLOCK_TAGS[i].length;
      System.arraycopy(BLOCK_TAGS[i], 0, this.levelTags[BLOCK_IDX], this.levelTagsLength[BLOCK_IDX], length);
      this.levelTagsLength[BLOCK_IDX] += length;
    }
    if (this.levelTagsLength[BLOCK_IDX] < BLOCK_ALL_TAGS_LENGTH) {
      System.arraycopy(this.levelTags[BLOCK_IDX], 0, this.levelTags[BLOCK_IDX] = new char[this.levelTagsLength[BLOCK_IDX]][], 0, this.levelTagsLength[BLOCK_IDX]);
    }
    // Init inline tags
    this.levelTags[INLINE_IDX] = new char[INLINE_ALL_TAGS_LENGTH][];
    this.levelTagsLength[INLINE_IDX]= 0;
    for (int i=0; i<=level; i++) {
      int length = INLINE_TAGS[i].length;
      System.arraycopy(INLINE_TAGS[i], 0, this.levelTags[INLINE_IDX], this.levelTagsLength[INLINE_IDX], length);
      this.levelTagsLength[INLINE_IDX] += length;
    }
    if (this.levelTagsLength[INLINE_IDX] < INLINE_ALL_TAGS_LENGTH) {
      System.arraycopy(this.levelTags[INLINE_IDX], 0, this.levelTags[INLINE_IDX] = new char[this.levelTagsLength[INLINE_IDX]][], 0, this.levelTagsLength[INLINE_IDX]);
    }
  }
  /*
   * Parse argument in @see tag method reference
   */
  protected Object parseArguments(Object receiver) throws InvalidInputException {
   
    if (this.tagSourceStart>this.cursorLocation) {
      return super.parseArguments(receiver);
    }

    // Init
    int modulo = 0; // should be 2 for (Type,Type,...) or 3 for (Type arg,Type arg,...)
    int iToken = 0;
    char[] argName = null;
    List arguments = new ArrayList(10);
    Object typeRef = null;
    int dim = 0;
    boolean isVarargs = false;
    long[] dimPositions = new long[20]; // assume that there won't be more than 20 dimensions...
    char[] name = null;
    long argNamePos = -1;
   
    // Parse arguments declaration if method reference
    nextArg : while (this.index < this.scanner.eofPosition) {

      // Read argument type reference
      try {
        typeRef = parseQualifiedName(false);
        if (this.abort) return null; // May be aborted by specialized parser
      } catch (InvalidInputException e) {
        break nextArg;
      }
      boolean firstArg = modulo == 0;
      if (firstArg) { // verify position
        if (iToken != 0)
          break nextArg;
      } else if ((iToken % modulo) != 0) {
          break nextArg;
      }
      if (typeRef == null) {
        if (firstArg && getCurrentTokenType() == TerminalTokens.TokenNameRPAREN) {
          this.lineStarted = true;
          return createMethodReference(receiver, null);
        }
        Object methodRef = createMethodReference(receiver, arguments);
        return syntaxRecoverEmptyArgumentType(methodRef);
      }
      if (this.index >= this.scanner.eofPosition) {
        int argumentStart = ((ASTNode)typeRef).sourceStart;
        Object argument = createArgumentReference(this.scanner.getCurrentIdentifierSource(), 0, false, typeRef, null, (((long)argumentStart)<<32)+this.tokenPreviousPosition-1);
        return syntaxRecoverArgumentType(receiver, arguments, argument);
      }
      if (this.index >= this.cursorLocation) {
        if (this.completionNode instanceof CompletionOnJavadocSingleTypeReference) {
          CompletionOnJavadocSingleTypeReference singleTypeReference = (CompletionOnJavadocSingleTypeReference) this.completionNode;
          if (singleTypeReference.token == null || singleTypeReference.token.length == 0) {
            Object methodRef = createMethodReference(receiver, arguments);
            return syntaxRecoverEmptyArgumentType(methodRef);
          }
        }
        if (this.completionNode instanceof CompletionOnJavadocQualifiedTypeReference) {
          CompletionOnJavadocQualifiedTypeReference qualifiedTypeReference = (CompletionOnJavadocQualifiedTypeReference) this.completionNode;
          if (qualifiedTypeReference.tokens == null || qualifiedTypeReference.tokens.length < qualifiedTypeReference.sourcePositions.length) {
            Object methodRef = createMethodReference(receiver, arguments);
            return syntaxRecoverEmptyArgumentType(methodRef);
          }
        }
      }
      iToken++;

      // Read possible additional type info
      dim = 0;
      isVarargs = false;
      if (readToken() == TerminalTokens.TokenNameLBRACKET) {
        // array declaration
        int dimStart = this.scanner.getCurrentTokenStartPosition();
        while (readToken() == TerminalTokens.TokenNameLBRACKET) {
          consumeToken();
          if (readToken() != TerminalTokens.TokenNameRBRACKET) {
            break nextArg;
          }
          consumeToken();
          dimPositions[dim++] = (((long) dimStart) << 32) + this.scanner.getCurrentTokenEndPosition();
        }
      } else if (readToken() == TerminalTokens.TokenNameELLIPSIS) {
        // ellipsis declaration
        int dimStart = this.scanner.getCurrentTokenStartPosition();
        dimPositions[dim++] = (((long) dimStart) << 32) + this.scanner.getCurrentTokenEndPosition();
        consumeToken();
        isVarargs = true;
      }

      // Read argument name
      argNamePos = -1;
      if (readToken() == TerminalTokens.TokenNameIdentifier) {
        consumeToken();
        if (firstArg) { // verify position
          if (iToken != 1)
            break nextArg;
        } else if ((iToken % modulo) != 1) {
            break nextArg;
        }
        if (argName == null) { // verify that all arguments name are declared
          if (!firstArg) {
            break nextArg;
          }
        }
        argName = this.scanner.getCurrentIdentifierSource();
        argNamePos = (((long)this.scanner.getCurrentTokenStartPosition())<<32)+this.scanner.getCurrentTokenEndPosition();
        iToken++;
      } else if (argName != null) { // verify that no argument name is declared
        break nextArg;
      }
     
      // Verify token position
      if (firstArg) {
        modulo = iToken + 1;
      } else {
        if ((iToken % modulo) != (modulo - 1)) {
          break nextArg;
        }
      }

      // Read separator or end arguments declaration
      int token = readToken();
      name = argName == null ? CharOperation.NO_CHAR : argName;
      if (token == TerminalTokens.TokenNameCOMMA) {
        // Create new argument
        Object argument = createArgumentReference(name, dim, isVarargs, typeRef, dimPositions, argNamePos);
        if (this.abort) return null; // May be aborted by specialized parser
        arguments.add(argument);
        consumeToken();
        iToken++;
      } else if (token == TerminalTokens.TokenNameRPAREN) {
        // Create new argument
        Object argument = createArgumentReference(name, dim, isVarargs, typeRef, dimPositions, argNamePos);
        if (this.abort) return null; // May be aborted by specialized parser
        arguments.add(argument);
        consumeToken();
        return createMethodReference(receiver, arguments);
      } else {
        Object argument = createArgumentReference(name, dim, isVarargs, typeRef, dimPositions, argNamePos);
        return syntaxRecoverArgumentType(receiver, arguments, argument);
      }
    }

    // Something wrong happened => Invalid input
    throw new InvalidInputException();
  }

    protected boolean parseParam() throws InvalidInputException {
      int startPosition = this.index;
      int endPosition = this.index;
      long namePosition = (((long)startPosition)<<32) + endPosition;
      this.identifierPtr = -1;
      boolean valid = super.parseParam();
      if (this.identifierPtr > 2) return valid;
      // See if expression is concerned by completion
      char[] name = null;
      CompletionScanner completionScanner = (CompletionScanner) this.scanner;
      boolean isTypeParam = false;
      if (this.identifierPtr >= 0) {
        char[] identifier = null;
        switch (this.identifierPtr) {
          case 2:
            if (!valid && completionScanner.completionIdentifier != null && completionScanner.completionIdentifier.length == 0) {
              valid = pushParamName(true);
            }
          case 1:
            isTypeParam = this.identifierStack[0][0] == '<';
            identifier = this.identifierStack[1];
            namePosition = this.identifierPositionStack[1];
            break;
          case 0:
            identifier = this.identifierStack[0];
            namePosition = this.identifierPositionStack[0];
            isTypeParam = identifier.length > 0 && identifier[0] == '<';
            break;
        }
        if (identifier != null && identifier.length > 0 && ScannerHelper.isJavaIdentifierPart(identifier[0])) {
          name = identifier;
        }
        startPosition = (int)(this.identifierPositionStack[0]>>32);
        endPosition = (int)this.identifierPositionStack[this.identifierPtr];
      }
      boolean inCompletion = (startPosition <= (this.cursorLocation+1) && this.cursorLocation <= endPosition) // completion cursor is between first and last stacked identifiers
        || ((startPosition == (endPosition+1) && endPosition == this.cursorLocation)); // or it's a completion on empty token
      if (inCompletion) {
        if (this.completionNode == null) {
          if (isTypeParam) {
            this.completionNode = new CompletionOnJavadocTypeParamReference(name, namePosition, startPosition, endPosition);
          } else {
            this.completionNode = new CompletionOnJavadocParamNameReference(name, namePosition, startPosition, endPosition);
          }
          if (CompletionEngine.DEBUG) {
            System.out.println("  completion param="+completionNode); //$NON-NLS-1$
          }
        } else if (this.completionNode instanceof CompletionOnJavadocParamNameReference) {
          CompletionOnJavadocParamNameReference paramNameRef = (CompletionOnJavadocParamNameReference)this.completionNode;
          int nameStart = (int) (namePosition>>32);
          paramNameRef.sourceStart = nameStart;
          int nameEnd = (int) namePosition;
          if (nameStart<this.cursorLocation && this.cursorLocation<nameEnd) {
            paramNameRef.sourceEnd = this.cursorLocation + 1;
          } else {
            paramNameRef.sourceEnd = nameEnd;
          }
          paramNameRef.tagSourceStart = startPosition;
          paramNameRef.tagSourceEnd = endPosition;
        } else if (this.completionNode instanceof CompletionOnJavadocTypeParamReference) {
          CompletionOnJavadocTypeParamReference typeParamRef = (CompletionOnJavadocTypeParamReference)this.completionNode;
          int nameStart = (int) (namePosition>>32);
          typeParamRef.sourceStart = nameStart;
          int nameEnd = (int) namePosition;
          if (nameStart<this.cursorLocation && this.cursorLocation<nameEnd) {
            typeParamRef.sourceEnd = this.cursorLocation + 1;
          } else {
            typeParamRef.sourceEnd = nameEnd;
          }
          typeParamRef.tagSourceStart = startPosition;
          typeParamRef.tagSourceEnd = endPosition;
        }
      }
      return valid;
    }

  /* (non-Javadoc)
     * @see org.aspectj.org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#parseReference()
     */
    protected boolean parseReference() throws InvalidInputException {
      boolean completed = this.completionNode != null;
      boolean valid = super.parseReference();
      if (!completed && this.completionNode != null) {
        this.completionNode.addCompletionFlags(CompletionOnJavadoc.FORMAL_REFERENCE);
      }
      return valid;
    }

  /*(non-Javadoc)
   * @see org.aspectj.org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#parseTag(int)
   */
  protected boolean parseTag(int previousPosition) throws InvalidInputException {
    int startPosition = this.inlineTagStarted ? this.inlineTagStart : previousPosition;
    boolean newLine = !this.lineStarted;
    boolean valid = super.parseTag(previousPosition);
    boolean inCompletion = (this.tagSourceStart <= (this.cursorLocation+1) && this.cursorLocation <= this.tagSourceEnd) // completion cursor is between first and last stacked identifiers
      || ((this.tagSourceStart == (this.tagSourceEnd+1) && this.tagSourceEnd == this.cursorLocation)); // or it's a completion on empty token
    if (inCompletion) {
      int end = this.tagSourceEnd;
      if (this.inlineTagStarted && this.scanner.currentCharacter == '}') {
        end = this.scanner.currentPosition;
      }
      long position = (((long)startPosition)<<32) + end;
      int length = this.cursorLocation+1-tagSourceStart;
      char[] tag = new char[length];
      System.arraycopy(this.source, this.tagSourceStart, tag, 0, length);
      char[][][] tags = possibleTags(tag, newLine);
      if (tags != null) {
        this.completionNode = new CompletionOnJavadocTag(tag, position, startPosition, end, tags, this.allPossibleTags);
      }
    }
    return valid;
  }

  /* (non-Javadoc)
   * @see org.aspectj.org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#parseThrows()
   */
  protected boolean parseThrows() {
    try {
      Object typeRef = parseQualifiedName(true);
      if (this.completionNode != null) {
        this.completionNode.addCompletionFlags(CompletionOnJavadoc.EXCEPTION);
      }
      return pushThrowName(typeRef);
    } catch (InvalidInputException ex) {
      // ignore
    }
    return false;
  }

  /*
   * Push param name reference. If it includes completion location, create and store completion node.
   */
  protected boolean pushParamName(boolean isTypeParam) {
    if (super.pushParamName(isTypeParam)) {
      Expression expression = (Expression) astStack[astPtr];
      // See if expression is concerned by completion
      if (expression.sourceStart <= (this.cursorLocation+1) && this.cursorLocation <= expression.sourceEnd) {
        if (isTypeParam) {
          this.completionNode = new CompletionOnJavadocTypeParamReference((JavadocSingleTypeReference)expression);
        } else {
          this.completionNode = new CompletionOnJavadocParamNameReference((JavadocSingleNameReference)expression);
        }
        if (CompletionEngine.DEBUG) {
          System.out.println("  completion param="+completionNode); //$NON-NLS-1$
        }
      }
      return true;
    }
    return false;
  }

  /**
   * Push text. If it includes completion location, then rescan line to see if there's a possible
   * reference under the cursor location.
   *
   * @see org.aspectj.org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#pushText(int, int)
   */
  protected void pushText(int start, int end) {
    if (start <= this.cursorLocation && this.cursorLocation <= end) {
      this.scanner.resetTo(start, end);
      boolean tokenizeWhiteSpace = this.scanner.tokenizeWhiteSpace;
      this.scanner.tokenizeWhiteSpace = true;
      try {
        Object typeRef = null;
        this.pushText = true;

        // Get reference tokens
        int previousToken = TerminalTokens.TokenNameWHITESPACE;
        while (!this.scanner.atEnd() && this.completionNode == null && !this.abort) {
          int token = readTokenSafely();
          switch (token) {
            case TerminalTokens.TokenNameStringLiteral :
              int strStart = 0, strEnd = 0;
              if ((strStart=this.scanner.getCurrentTokenStartPosition()+1) <= this.cursorLocation &&
                this.cursorLocation <= (strEnd=this.scanner.getCurrentTokenEndPosition()-1))
              {
                this.scanner.resetTo(strStart, strEnd);
              }
              consumeToken();
              break;
            case TerminalTokens.TokenNameERROR :
              consumeToken();
              if (this.scanner.currentCharacter == '#') { // @see ...#member
                Object member = null;
                try {
                  this.scanner.tokenizeWhiteSpace = false;
                  member = parseMember(typeRef);
                } catch (InvalidInputException e) {
                  consumeToken();
                }
                this.scanner.tokenizeWhiteSpace = true;
                if (this.completionNode != null) {
                  int flags = this.inlineTagStarted ? 0 : CompletionOnJavadoc.TEXT|CompletionOnJavadoc.ONLY_INLINE_TAG;
                  if (member instanceof JavadocMessageSend) {
                    JavadocMessageSend msgSend = (JavadocMessageSend) member;
                    this.completionNode = new CompletionOnJavadocMessageSend(msgSend, this.memberStart, flags);
                    if (CompletionEngine.DEBUG) {
                      System.out.println("  new completion method="+completionNode); //$NON-NLS-1$
                    }
                  } else if (member instanceof JavadocAllocationExpression) {
                    JavadocAllocationExpression alloc = (JavadocAllocationExpression) member;
                    this.completionNode = new CompletionOnJavadocAllocationExpression(alloc, this.memberStart, flags);
                    if (CompletionEngine.DEBUG) {
                      System.out.println("  new completion method="+completionNode); //$NON-NLS-1$
                    }
                  } else {
                    this.completionNode.addCompletionFlags(flags);
                  }
                }
              }
              break;
            case TerminalTokens.TokenNameIdentifier :
              try {
                this.scanner.tokenizeWhiteSpace = false;
                typeRef = parseQualifiedName(true);
                if (this.completionNode == null) {
                  consumeToken();
                  this.scanner.resetTo(this.tokenPreviousPosition, end);
                  this.index = this.tokenPreviousPosition;
                }
              }
              catch (InvalidInputException e) {
                consumeToken();
              }
              finally {
                this.scanner.tokenizeWhiteSpace = true;
              }
              if (previousToken != TerminalTokens.TokenNameWHITESPACE) {
                typeRef = null;
                this.completionNode = null;
              }
              break;
            case TerminalTokens.TokenNameAT:
              consumeToken();
              try {
                this.scanner.tokenizeWhiteSpace = false;
                int startPosition = this.scanner.getCurrentTokenStartPosition();
                parseTag(startPosition);
                if (this.completionNode != null) {
                  if (this.inlineTagStarted) {
                    /* May be to replace invalid @value tag inside text?
                    if (this.completionNode instanceof CompletionOnJavadocSingleTypeReference) {
                      CompletionOnJavadocSingleTypeReference singleTypeReference = (CompletionOnJavadocSingleTypeReference) this.completionNode;
                      singleTypeReference.tagSourceStart = startPosition;
                      switch (this.tagValue) {
                        case TAG_VALUE_VALUE:
//                          singleTypeReference.completionFlags |= ONLY_INLINE_TAG;
                          if (this.sourceLevel < ClassFileConstants.JDK1_5) singleTypeReference.completionFlags |= REPLACE_TAG;
                          break;
                      }
                    } else if (this.completionNode instanceof CompletionOnJavadocQualifiedTypeReference) {
                      CompletionOnJavadocQualifiedTypeReference qualifiedTypeRef = (CompletionOnJavadocQualifiedTypeReference) this.completionNode;
                      qualifiedTypeRef.tagSourceStart = startPosition;
                      switch (this.tagValue) {
                        case TAG_VALUE_VALUE:
                          singleTypeReference.completionFlags |= ONLY_INLINE_TAG;
                          if (this.sourceLevel < ClassFileConstants.JDK1_5) qualifiedTypeRef.completionFlags |= REPLACE_TAG;
                          break;
                      }
                    }
//                    */
                  } else {
                    /* May be to replace non-inline tag inside text?
                    if (this.completionNode instanceof CompletionOnJavadocSingleTypeReference) {
                      CompletionOnJavadocSingleTypeReference singleTypeReference = (CompletionOnJavadocSingleTypeReference) this.completionNode;
                      singleTypeReference.tagSourceStart = startPosition;
                      switch (this.tagValue) {
                        case TAG_LINK_VALUE:
                        case TAG_LINKPLAIN_VALUE:
                          singleTypeReference.completionFlags |= ONLY_INLINE_TAG;
                        case TAG_SEE_VALUE:
                          singleTypeReference.completionFlags |= REPLACE_TAG;
                          break;
                      }
                    } else if (this.completionNode instanceof CompletionOnJavadocQualifiedTypeReference) {
                      CompletionOnJavadocQualifiedTypeReference qualifiedTypeRef = (CompletionOnJavadocQualifiedTypeReference) this.completionNode;
                      qualifiedTypeRef.tagSourceStart = startPosition;
                      switch (this.tagValue) {
                        case TAG_LINK_VALUE:
                        case TAG_LINKPLAIN_VALUE:
                          qualifiedTypeRef.completionFlags |= ONLY_INLINE_TAG;
                        case TAG_SEE_VALUE:
                          qualifiedTypeRef.completionFlags |= REPLACE_TAG;
                          break;
                      }
                    }
//                    */
                  }
                }
              } catch (InvalidInputException e) {
                consumeToken();
              }
              this.scanner.tokenizeWhiteSpace = true;
              break;
            default :
              consumeToken();
              typeRef = null;
              break;
          }
          previousToken = token;
        }
      }
      finally {
        this.scanner.tokenizeWhiteSpace = tokenizeWhiteSpace;
        this.pushText = false;
      }

      // Reset position to avoid missing tokens when new line was encountered
      this.index = end;
      this.scanner.currentPosition = end;
      consumeToken();

      if (this.completionNode != null) {
        if (this.inlineTagStarted) {
          this.completionNode.addCompletionFlags(CompletionOnJavadoc.FORMAL_REFERENCE);
        } else {
          this.completionNode.addCompletionFlags(CompletionOnJavadoc.TEXT);
        }
      }
    }
  }

  /* (non-Javadoc)
   * @see org.aspectj.org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#readToken()
   */
  protected int readToken() throws InvalidInputException {
    int token = super.readToken();
    if (token == TerminalTokens.TokenNameIdentifier && this.scanner.currentPosition == this.scanner.startPosition) {
      // Scanner is looping on empty token => read it...
      this.scanner.getCurrentIdentifierSource();
    }
    return token;
  }

  /*
   * Recover syntax on invalid qualified name.
   */
  protected Object syntaxRecoverQualifiedName(int primitiveToken) throws InvalidInputException {
    if (this.cursorLocation == ((int)this.identifierPositionStack[this.identifierPtr])) {
      // special case of completion just before the dot.
      return createTypeReference(primitiveToken);
    }
    int idLength = this.identifierLengthStack[this.identifierLengthPtr];
    char[][] tokens = new char[idLength][];
    int startPtr = this.identifierPtr-idLength+1;
    System.arraycopy(this.identifierStack, startPtr, tokens, 0, idLength);
    long[] positions = new long[idLength+1];
    System.arraycopy(this.identifierPositionStack, startPtr, positions, 0, idLength);
    positions[idLength] = (((long)this.tokenPreviousPosition)<<32) + this.tokenPreviousPosition;
    this.completionNode = new CompletionOnJavadocQualifiedTypeReference(tokens, CharOperation.NO_CHAR, positions, this.tagSourceStart, this.tagSourceEnd);

    if (CompletionEngine.DEBUG) {
      System.out.println("  completion partial qualified type="+completionNode); //$NON-NLS-1$
    }
    return this.completionNode;
  }

  /*
   * Recover syntax on type argument in invalid method/constructor reference
   */
  protected Object syntaxRecoverArgumentType(Object receiver, List arguments, Object argument) throws InvalidInputException {
    if (this.completionNode != null && !this.pushText) {
      this.completionNode.addCompletionFlags(CompletionOnJavadoc.BASE_TYPES);
      if (this.completionNode instanceof CompletionOnJavadocSingleTypeReference) {
        char[] token = ((CompletionOnJavadocSingleTypeReference)this.completionNode).token;
        if (token != null && token.length > 0) {
          return this.completionNode;
        }
      } else {
        return this.completionNode;
      }
    }
    // Filter empty token
    if (this.completionNode instanceof CompletionOnJavadocSingleTypeReference) {
      CompletionOnJavadocSingleTypeReference singleTypeReference = (CompletionOnJavadocSingleTypeReference) this.completionNode;
      if (singleTypeReference.token != null && singleTypeReference.token.length > 0) {
        arguments.add(argument);
      }
    } else if (this.completionNode instanceof CompletionOnJavadocQualifiedTypeReference) {
      CompletionOnJavadocQualifiedTypeReference qualifiedTypeReference = (CompletionOnJavadocQualifiedTypeReference) this.completionNode;
      if (qualifiedTypeReference.tokens != null && qualifiedTypeReference.tokens.length == qualifiedTypeReference.sourcePositions.length) {
        arguments.add(argument);
      }
    } else {
      arguments.add(argument);
    }
    Object methodRef = super.createMethodReference(receiver, arguments);
    if (methodRef instanceof JavadocMessageSend) {
      JavadocMessageSend msgSend = (JavadocMessageSend) methodRef;
      if (this.index > this.cursorLocation) {
        msgSend.sourceEnd = this.tokenPreviousPosition-1;
      }
      int nameStart = (int) (msgSend.nameSourcePosition >>> 32);
      int nameEnd = (int) msgSend.nameSourcePosition;
      if ((nameStart <= (this.cursorLocation+1) && this.cursorLocation <= nameEnd)) {
        this.completionNode = new CompletionOnJavadocFieldReference(msgSend, this.memberStart);
      } else {
        this.completionNode = new CompletionOnJavadocMessageSend(msgSend, this.memberStart);
      }
    } else if (methodRef instanceof JavadocAllocationExpression) {
      JavadocAllocationExpression allocExp = (JavadocAllocationExpression) methodRef;
      if (this.index > this.cursorLocation) {
        allocExp.sourceEnd = this.tokenPreviousPosition-1;
      }
      this.completionNode = new CompletionOnJavadocAllocationExpression(allocExp, this.memberStart);
    }
    if (CompletionEngine.DEBUG) {
      System.out.println("  completion method="+completionNode); //$NON-NLS-1$
    }
    return this.completionNode;
  }

  /*
   * Recover syntax on empty type argument in invalid method/constructor reference
   */
  protected Object syntaxRecoverEmptyArgumentType(Object methodRef) throws InvalidInputException {
    if (methodRef instanceof JavadocMessageSend) {
      JavadocMessageSend msgSend = (JavadocMessageSend) methodRef;
      if (this.index > this.cursorLocation) {
        msgSend.sourceEnd = this.tokenPreviousPosition-1;
      }
      this.completionNode = new CompletionOnJavadocMessageSend(msgSend, this.memberStart);
    } else if (methodRef instanceof JavadocAllocationExpression) {
      JavadocAllocationExpression allocExp = (JavadocAllocationExpression) methodRef;
      if (this.index > this.cursorLocation) {
        allocExp.sourceEnd = this.tokenPreviousPosition-1;
      }
      this.completionNode = new CompletionOnJavadocAllocationExpression(allocExp, this.memberStart);
    }
    if (CompletionEngine.DEBUG) {
      System.out.println("  completion method="+completionNode); //$NON-NLS-1$
    }
    return this.completionNode;
  }

  /*
   * Store completion node into doc comment.
   */
  protected void updateDocComment() {
    super.updateDocComment();
    if (completionNode instanceof Expression) {
      getCompletionParser().assistNodeParent = this.docComment;
      getCompletionParser().assistNode = (ASTNode) this.completionNode;
      getCompletionJavadoc().completionNode = (Expression) completionNode;
    }
  }

  /* (non-Javadoc)
   * @see org.aspectj.org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#verifySpaceOrEndComment()
   */
  protected boolean verifySpaceOrEndComment() {
    CompletionScanner completionScanner = (CompletionScanner) this.scanner;
    if (completionScanner.completionIdentifier != null && completionScanner.completedIdentifierStart <= this.cursorLocation && this.cursorLocation <= completionScanner.completedIdentifierEnd) {
      // if we're on completion location do not verify end...
      return true;
    }
    return super.verifySpaceOrEndComment();
  }
 
}
TOP

Related Classes of org.aspectj.org.eclipse.jdt.internal.codeassist.complete.CompletionJavadocParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.