0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * If you wish your version of this file to be governed by only the CDDL
0025: * or only the GPL Version 2, indicate your decision by adding
0026: * "[Contributor] elects to include this software in this distribution
0027: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0028: * single choice of license, a recipient has the option to distribute
0029: * your version of this file under either the CDDL, the GPL Version 2 or
0030: * to extend the choice of license to its licensees as provided above.
0031: * However, if you add GPL Version 2 code and therefore, elected the GPL
0032: * Version 2 license, then the option applies only if the new code is
0033: * made subject to such option by the copyright holder.
0034: *
0035: * Contributor(s):
0036: *
0037: * Portions Copyrighted 2007 Sun Microsystems, Inc.
0038: */
0039:
0040: package org.netbeans.modules.java.editor.javadoc;
0041:
0042: import com.sun.javadoc.ClassDoc;
0043: import com.sun.javadoc.Doc;
0044: import com.sun.javadoc.ExecutableMemberDoc;
0045: import com.sun.javadoc.MemberDoc;
0046: import com.sun.javadoc.Parameter;
0047: import com.sun.javadoc.ProgramElementDoc;
0048: import com.sun.javadoc.Tag;
0049: import com.sun.javadoc.Type;
0050: import com.sun.source.tree.Scope;
0051: import com.sun.source.util.TreePath;
0052: import com.sun.source.util.Trees;
0053: import java.io.IOException;
0054: import java.util.ArrayList;
0055: import java.util.Collections;
0056: import java.util.EnumSet;
0057: import java.util.HashMap;
0058: import java.util.HashSet;
0059: import java.util.Iterator;
0060: import java.util.LinkedList;
0061: import java.util.List;
0062: import java.util.Set;
0063: import java.util.concurrent.ExecutionException;
0064: import java.util.concurrent.Future;
0065: import java.util.logging.Level;
0066: import java.util.logging.Logger;
0067: import javax.lang.model.element.Element;
0068: import javax.lang.model.element.ElementKind;
0069: import static javax.lang.model.element.ElementKind.*;
0070: import javax.lang.model.element.ExecutableElement;
0071: import javax.lang.model.element.Modifier;
0072: import javax.lang.model.element.PackageElement;
0073: import javax.lang.model.element.TypeElement;
0074: import javax.lang.model.element.TypeParameterElement;
0075: import javax.lang.model.element.VariableElement;
0076: import javax.lang.model.type.DeclaredType;
0077: import javax.lang.model.type.ExecutableType;
0078: import javax.lang.model.type.TypeKind;
0079: import javax.lang.model.type.TypeMirror;
0080: import javax.lang.model.type.TypeVariable;
0081: import javax.lang.model.util.Elements;
0082: import javax.lang.model.util.Types;
0083: import javax.swing.text.Document;
0084: import org.netbeans.api.java.lexer.JavadocTokenId;
0085: import org.netbeans.api.java.source.ClassIndex;
0086: import org.netbeans.api.java.source.ClasspathInfo;
0087: import org.netbeans.api.java.source.CompilationController;
0088: import org.netbeans.api.java.source.CompilationInfo;
0089: import org.netbeans.api.java.source.ElementHandle;
0090: import org.netbeans.api.java.source.ElementUtilities;
0091: import org.netbeans.api.java.source.JavaSource;
0092: import org.netbeans.api.java.source.Task;
0093: import org.netbeans.api.java.source.TreeUtilities;
0094: import org.netbeans.api.lexer.Token;
0095: import org.netbeans.api.lexer.TokenSequence;
0096: import org.netbeans.modules.editor.java.JavaCompletionItem;
0097: import org.netbeans.modules.editor.java.LazyTypeCompletionItem;
0098: import org.netbeans.modules.editor.java.Utilities;
0099: import org.netbeans.spi.editor.completion.CompletionItem;
0100: import org.netbeans.spi.editor.completion.CompletionProvider;
0101: import org.netbeans.spi.editor.completion.CompletionResultSet;
0102: import org.netbeans.spi.editor.completion.support.AsyncCompletionQuery;
0103: import org.openide.util.Exceptions;
0104: import org.openide.util.NbBundle;
0105:
0106: /**
0107: *
0108: * @author Jan Pokorsky
0109: */
0110: final class JavadocCompletionQuery extends AsyncCompletionQuery {
0111:
0112: private static final String CLASS_KEYWORD = "class"; //NOI18N
0113: private final int queryType;
0114:
0115: private int caretOffset;
0116: private List<CompletionItem> items;
0117: private boolean hasAdditionalItems;
0118:
0119: public JavadocCompletionQuery(int queryType) {
0120: this .queryType = queryType;
0121: }
0122:
0123: @Override
0124: protected void query(CompletionResultSet resultSet, Document doc,
0125: int caretOffset) {
0126: try {
0127: queryImpl(resultSet, doc, caretOffset);
0128: } catch (InterruptedException ex) {
0129: Exceptions.printStackTrace(ex);
0130: } catch (ExecutionException ex) {
0131: Exceptions.printStackTrace(ex);
0132: } finally {
0133: resultSet.finish();
0134: }
0135: }
0136:
0137: private void queryImpl(CompletionResultSet resultSet, Document doc,
0138: int caretOffset) throws InterruptedException,
0139: ExecutionException {
0140: if (!JavadocCompletionUtils.isJavadocContext(doc, caretOffset)) {
0141: return;
0142: }
0143:
0144: JavadocContext jdctx = new JavadocContext();
0145: this .caretOffset = caretOffset;
0146: Future<Void> f = runInJavac(JavaSource.forDocument(doc), jdctx);
0147: if (f != null && !f.isDone()) {
0148: resultSet.setWaitText(NbBundle.getMessage(
0149: JavadocCompletionProvider.class,
0150: "scanning-in-progress"));
0151: f.get();
0152: }
0153:
0154: if (isTaskCancelled()) {
0155: return;
0156: }
0157:
0158: if ((queryType & CompletionProvider.COMPLETION_QUERY_TYPE) != 0) {
0159: if (!items.isEmpty()) {
0160: resultSet.addAllItems(items);
0161: }
0162: resultSet.setHasAdditionalItems(hasAdditionalItems);
0163: }
0164:
0165: if (jdctx.anchorOffset >= 0) {
0166: resultSet.setAnchorOffset(jdctx.anchorOffset);
0167: }
0168: }
0169:
0170: private Future<Void> runInJavac(JavaSource js,
0171: final JavadocContext jdctx) {
0172: try {
0173: if (js == null) {
0174: return null;
0175: }
0176:
0177: return js.runWhenScanFinished(
0178: new Task<CompilationController>() {
0179:
0180: public void run(CompilationController javac)
0181: throws Exception {
0182: javac
0183: .toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
0184: if (isTaskCancelled()) {
0185: return;
0186: }
0187: try {
0188: jdctx.javac = javac;
0189: resolveContext(javac, jdctx);
0190: analyzeContext(jdctx);
0191: } finally {
0192: jdctx.javac = null;
0193: }
0194: }
0195: }, true);
0196: } catch (IOException ex) {
0197: Exceptions.printStackTrace(ex);
0198: return null;
0199: }
0200: }
0201:
0202: private void resolveContext(CompilationInfo javac,
0203: JavadocContext jdctx) throws IOException {
0204: jdctx.doc = javac.getDocument();
0205: // find class context: class, method, ...
0206: Doc javadoc = JavadocCompletionUtils.findJavadoc(javac,
0207: jdctx.doc, this .caretOffset);
0208: if (javadoc == null) {
0209: return;
0210: }
0211: jdctx.jdoc = javadoc;
0212: Element elm = javac.getElementUtilities().elementFor(javadoc);
0213: jdctx.handle = ElementHandle.create(elm);
0214: jdctx.jdts = JavadocCompletionUtils.findJavadocTokenSequence(
0215: jdctx.doc, this .caretOffset);
0216: jdctx.positions = DocPositions.get(javac, javadoc, jdctx.jdts);
0217: items = new ArrayList<CompletionItem>();
0218: }
0219:
0220: private void analyzeContext(JavadocContext jdctx) {
0221: TokenSequence<JavadocTokenId> jdts = jdctx.jdts;
0222: if (jdts == null) {
0223: return;
0224: }
0225:
0226: jdts.move(this .caretOffset);
0227: if (!jdts.moveNext() && !jdts.movePrevious()) {
0228: // XXX solve /***/
0229: // provide block tags, inline tags, html
0230: return;
0231: }
0232:
0233: if (this .caretOffset - jdts.offset() == 0) {
0234: // if position in token == 0 resolve CC according to previous token
0235: jdts.movePrevious();
0236: }
0237:
0238: switch (jdts.token().id()) {
0239: case TAG:
0240: resolveTagToken(jdctx);
0241: break;
0242: case IDENT:
0243: resolveIdent(jdctx);
0244: break;
0245: case DOT:
0246: resolveDotToken(jdctx);
0247: break;
0248: case HASH:
0249: resolveHashToken(jdctx);
0250: break;
0251: case OTHER_TEXT:
0252: resolveOtherText(jdctx, jdts);
0253: break;
0254: case HTML_TAG:
0255: resolveHTMLToken(jdctx);
0256: break;
0257: }
0258: }
0259:
0260: void resolveTagToken(JavadocContext jdctx) {
0261: assert jdctx.jdts.token() != null;
0262: assert jdctx.jdts.token().id() == JavadocTokenId.TAG;
0263:
0264: Tag tag = jdctx.positions.getTag(caretOffset);
0265: if (JavadocCompletionUtils.isBlockTag(tag)) {
0266: resolveBlockTag(tag, jdctx);
0267: } else {
0268: resolveInlineTag(tag, jdctx);
0269: }
0270: }
0271:
0272: void resolveBlockTag(Tag tag, JavadocContext jdctx) {
0273: int pos;
0274: String prefix;
0275: if (tag != null) {
0276: int[] tagSpan = jdctx.positions.getTagSpan(tag);
0277: prefix = JavadocCompletionUtils.getCharSequence(jdctx.doc,
0278: tagSpan[0], caretOffset).toString();
0279: pos = tagSpan[0];
0280: } else {
0281: prefix = ""; // NOI18N
0282: pos = caretOffset;
0283: }
0284:
0285: items.addAll(JavadocCompletionItem.addBlockTagItems(jdctx.jdoc,
0286: jdctx.handle.getKind(), prefix, pos));
0287: jdctx.anchorOffset = pos;
0288: }
0289:
0290: void resolveInlineTag(Tag tag, JavadocContext jdctx) {
0291: int pos;
0292: String prefix;
0293: if (tag != null) {
0294: int[] tagSpan = jdctx.positions.getTagSpan(tag);
0295: pos = tagSpan[0] + 1;
0296: prefix = JavadocCompletionUtils.getCharSequence(jdctx.doc,
0297: pos, caretOffset).toString();
0298: jdctx.anchorOffset = pos;
0299: } else {
0300: pos = caretOffset;
0301: prefix = ""; // NOI18N
0302: jdctx.anchorOffset = pos;
0303: }
0304: items.addAll(JavadocCompletionItem.addInlineTagItems(
0305: jdctx.jdoc, jdctx.handle.getKind(), prefix, pos));
0306: }
0307:
0308: static final class Reference {
0309: CharSequence fqn;
0310: CharSequence member;
0311: CharSequence tag;
0312: int begin = -1; // inclusive
0313: int end = -1; // exclusive
0314:
0315: boolean isReference() {
0316: return begin > 0;
0317: }
0318:
0319: private static void insideMember(
0320: TokenSequence<JavadocTokenId> jdts, Reference ref) {
0321: StringBuilder sb = new StringBuilder();
0322: STOP: while (jdts.moveNext()) {
0323: Token<JavadocTokenId> token = jdts.token();
0324: switch (token.id()) {
0325: case IDENT:
0326: sb.append(token.text());
0327: ref.end = jdts.offset() + token.length();
0328: break;
0329: case OTHER_TEXT:
0330: // XXX handle also () part
0331: default:
0332: break STOP;
0333: }
0334: }
0335:
0336: ref.member = sb;
0337: }
0338:
0339: private static void insideFQN(
0340: TokenSequence<JavadocTokenId> jdts, Reference ref) {
0341: StringBuilder sb = new StringBuilder();
0342: STOP: while (jdts.moveNext()) {
0343: Token<JavadocTokenId> token = jdts.token();
0344: switch (token.id()) {
0345: case IDENT:
0346: sb.append(token.text());
0347: if (ref.begin < 0) {
0348: ref.begin = jdts.offset();
0349: }
0350: ref.end = jdts.offset() + token.length();
0351: break;
0352: case HASH:
0353: // sb.append(token.text());
0354: if (ref.begin < 0) {
0355: ref.begin = jdts.offset();
0356: }
0357: ref.end = jdts.offset() + token.length();
0358: insideMember(jdts, ref);
0359: break STOP;
0360: case DOT:
0361: if (sb.length() == 0
0362: || '.' == sb.charAt(sb.length() - 1)) {
0363: break STOP;
0364: }
0365: sb.append('.');
0366: ref.end = jdts.offset() + token.length();
0367: break;
0368: default:
0369: break STOP;
0370: }
0371: }
0372:
0373: if (sb.length() > 0) {
0374: ref.fqn = sb;
0375: }
0376: }
0377:
0378: /**
0379: *
0380: * @param jdctx
0381: * @param offset offset of the first token to resolve
0382: * @return reference
0383: */
0384: public static Reference resolve(JavadocContext jdctx, int offset) {
0385: TokenSequence<JavadocTokenId> jdts = jdctx.jdts;
0386: Reference ref = new Reference();
0387: jdts.move(offset);
0388: insideFQN(jdts, ref);
0389:
0390: return ref;
0391: }
0392: }
0393:
0394: void resolveIdent(JavadocContext jdctx) {
0395: TokenSequence<JavadocTokenId> jdts = jdctx.jdts;
0396: assert jdts.token() != null;
0397: assert jdts.token().id() == JavadocTokenId.IDENT;
0398: // @see package.Class[.NestedClass]#member[()]
0399: // START -> TAG OT(WS+) MEMBER_SELECT|CLASS_SELECT
0400: // CLASS_SELECT -> IDENT | IDENT DOT CLASS_SELECT | IDENT MEMBER_SELECT
0401: // MEMBER_SELECT -> HASH IDENT OT('(')
0402:
0403: // @see org.Clazz#meth(int p, int q)
0404: // TAG OT(' ') IDENT DOT IDENT HASH IDENT OT('(') IDENT OT(' ') IDENT OT(', ') IDENT OT(' ') IDENT OT(')\n...')
0405: // @see Clazz#meth(int p, int q)
0406: // @see #meth(int p, int q)
0407: // @see Clazz.NestedClazz
0408:
0409: // XXX ignore parenthesis content for now
0410: // Parenthesis content:
0411: // param types not neccessary to be imported or fqn!!!
0412: // param types may be fqn
0413: // param types never contains generics
0414: // params may contain name but they not necessary match the real names
0415: // param list may contain spaces
0416: // no space allowed between member and parenthesis
0417:
0418: Tag tag = jdctx.positions.getTag(caretOffset);
0419: if (tag != null) {
0420: insideTag(tag, jdctx);
0421: }
0422: }
0423:
0424: void resolveDotToken(JavadocContext jdctx) {
0425: assert jdctx.jdts.token() != null;
0426: assert jdctx.jdts.token().id() == JavadocTokenId.DOT;
0427:
0428: Tag tag = jdctx.positions.getTag(caretOffset);
0429: if (tag != null) {
0430: insideTag(tag, jdctx);
0431: }
0432: }
0433:
0434: void resolveHashToken(JavadocContext jdctx) {
0435: assert jdctx.jdts.token() != null;
0436: assert jdctx.jdts.token().id() == JavadocTokenId.HASH;
0437:
0438: Tag tag = jdctx.positions.getTag(caretOffset);
0439: if (tag != null) {
0440: insideTag(tag, jdctx);
0441: }
0442: }
0443:
0444: void resolveHTMLToken(JavadocContext jdctx) {
0445: assert jdctx.jdts.token() != null;
0446: assert jdctx.jdts.token().id() == JavadocTokenId.HTML_TAG;
0447:
0448: Tag tag = jdctx.positions.getTag(caretOffset);
0449: if (tag != null && "@param".equals(tag.name())) {
0450: // type param
0451: insideParamTag(tag, jdctx);
0452: }
0453: }
0454:
0455: private void insideTag(Tag tag, JavadocContext jdctx) {
0456: String kind = tag.kind();
0457: if ("@param".equals(kind)) { // NOI18N
0458: insideParamTag(tag, jdctx);
0459: } else if ("@see".equals(kind)
0460: || "@throws".equals(kind)
0461: || "@value".equals(kind) // NOI18N
0462: || (DocPositions.UNCLOSED_INLINE_TAG == kind && ("@link"
0463: .equals(tag.name())
0464: || "@linkplain".equals(tag.name()) || "@value"
0465: .equals(tag.name())))) { // NOI18N
0466: insideSeeTag(tag, jdctx);
0467: }
0468: }
0469:
0470: private void insideSeeTag(Tag tag, JavadocContext jdctx) {
0471: TokenSequence<JavadocTokenId> jdts = jdctx.jdts;
0472: assert jdts.token() != null;
0473: int[] span = jdctx.positions.getTagSpan(tag);
0474:
0475: boolean isThrowsKind = "@throws".equals(tag.kind()); // NOI18N
0476: if (isThrowsKind
0477: && !(jdctx.jdoc.isMethod() || jdctx.jdoc
0478: .isConstructor())) {
0479: // illegal tag in this context
0480: return;
0481: }
0482:
0483: jdts.move(span[0]
0484: + (JavadocCompletionUtils.isBlockTag(tag) ? 0 : 1));
0485: jdts.moveNext(); // @see|@link|@throws
0486: jdts.moveNext(); // white space
0487:
0488: boolean noPrefix = false;
0489:
0490: if (caretOffset <= jdts.offset() + jdts.token().length()) {
0491: int pos = caretOffset - jdts.offset();
0492: CharSequence cs = jdts.token().text();
0493: cs = pos < cs.length() ? cs.subSequence(0, pos) : cs;
0494:
0495: if (JavadocCompletionUtils.isWhiteSpace(cs)
0496: || JavadocCompletionUtils.isLineBreak(jdts.token(),
0497: pos)) {
0498: noPrefix = true;
0499: } else {
0500: // broken syntax
0501: return;
0502: }
0503: }
0504:
0505: if (noPrefix) {
0506: // complete all types + members
0507:
0508: if (isThrowsKind) {
0509: completeThrowsOrPkg(null, "", caretOffset, jdctx); // NOI18N
0510: } else {
0511: completeClassOrPkg(null, "", caretOffset, jdctx); // NOI18N
0512: }
0513: jdctx.anchorOffset = caretOffset;
0514: return;
0515: }
0516:
0517: jdts.moveNext(); // reference
0518: Reference ref = Reference.resolve(jdctx, jdts.offset());
0519: if (ref.isReference() && caretOffset <= ref.end) {
0520: // complete type
0521: CharSequence cs = JavadocCompletionUtils.getCharSequence(
0522: jdctx.doc, ref.begin, caretOffset);
0523: StringBuilder sb = new StringBuilder();
0524: jdctx.anchorOffset = ref.begin;
0525: for (int i = cs.length() - 1; i >= 0; i--) {
0526: char c = cs.charAt(i);
0527: if (c == '#') {
0528: // complete class member
0529: String prefix = sb.toString();
0530: String fqn = ref.fqn == null ? null : ref.fqn
0531: .toString();
0532: int substitutionOffset = caretOffset - sb.length();
0533: completeClassMember(fqn, prefix,
0534: substitutionOffset, jdctx);
0535: return;
0536: } else if (c == '.') {
0537: // complete class or package
0538: String prefix = sb.toString();
0539: String fqn = cs.subSequence(0, i).toString();
0540: int substitutionOffset = caretOffset - sb.length();
0541: if (isThrowsKind) {
0542: completeThrowsOrPkg(fqn, prefix,
0543: substitutionOffset, jdctx);
0544: } else {
0545: completeClassOrPkg(fqn, prefix,
0546: substitutionOffset, jdctx);
0547: }
0548: return;
0549: } else {
0550: sb.insert(0, c);
0551: }
0552: }
0553: // complete class or package
0554: String prefix = sb.toString();
0555: String fqn = null;
0556: int substitutionOffset = caretOffset - sb.length();
0557: if (isThrowsKind) {
0558: completeThrowsOrPkg(fqn, prefix, substitutionOffset,
0559: jdctx);
0560: } else {
0561: completeClassOrPkg(fqn, prefix, substitutionOffset,
0562: jdctx);
0563: }
0564: return;
0565: }
0566: }
0567:
0568: private void insideParamTag(Tag tag, JavadocContext jdctx) {
0569: TokenSequence<JavadocTokenId> jdts = jdctx.jdts;
0570: assert jdts.token() != null;
0571: int[] span = jdctx.positions.getTagSpan(tag);
0572:
0573: jdts.move(span[0]);
0574: jdts.moveNext(); // @param
0575: jdts.moveNext(); // white space
0576:
0577: if (caretOffset <= jdts.offset() + jdts.token().length()) {
0578: int pos = caretOffset - jdts.offset();
0579: CharSequence cs = jdts.token().text();
0580: cs = pos < cs.length() ? cs.subSequence(0, pos) : cs;
0581:
0582: if (JavadocCompletionUtils.isWhiteSpace(cs)
0583: || JavadocCompletionUtils.isLineBreak(jdts.token(),
0584: pos)) {
0585: // none prefix
0586: jdctx.anchorOffset = caretOffset;
0587: completeParamName(tag, "", caretOffset, jdctx); // NOI18N
0588: return;
0589: } else {
0590: // broken syntax
0591: return;
0592: }
0593: }
0594:
0595: jdts.moveNext(); // param name
0596: if (!(jdts.token().id() == JavadocTokenId.IDENT || jdts.token()
0597: .id() == JavadocTokenId.HTML_TAG)) {
0598: // broken syntax
0599: return;
0600: }
0601: if (caretOffset <= jdts.offset() + jdts.token().length()) {
0602: CharSequence prefix = jdts.token().text().subSequence(0,
0603: caretOffset - jdts.offset());
0604: jdctx.anchorOffset = jdts.offset();
0605: completeParamName(tag, prefix.toString(), jdts.offset(),
0606: jdctx);
0607: return;
0608: }
0609: }
0610:
0611: private void completeParamName(Tag tag, String prefix,
0612: int substitutionOffset, JavadocContext jdctx) {
0613: if (jdctx.jdoc.isMethod() || jdctx.jdoc.isConstructor()) {
0614: ExecutableMemberDoc emd = (ExecutableMemberDoc) jdctx.jdoc;
0615: Parameter[] params = emd.parameters();
0616: for (int i = 0; i < params.length; i++) {
0617: Parameter param = params[i];
0618: if (param.name().startsWith(prefix)) {
0619: items.add(JavadocCompletionItem.createNameItem(
0620: param.name(), substitutionOffset));
0621: }
0622: }
0623:
0624: completeTypeVarName(emd, prefix, substitutionOffset);
0625: } else if (jdctx.jdoc.isClass()) {
0626: completeTypeVarName(jdctx.jdoc, prefix, substitutionOffset);
0627: }
0628: }
0629:
0630: private void completeTypeVarName(Doc holder, String prefix,
0631: int substitutionOffset) {
0632: if (prefix.length() > 0) {
0633: if (prefix.charAt(0) == '<') {
0634: prefix = prefix.substring(1, prefix.length());
0635: } else {
0636: // not type param
0637: return;
0638: }
0639: }
0640:
0641: com.sun.javadoc.TypeVariable[] tparams = holder.isClass() ? ((ClassDoc) holder)
0642: .typeParameters()
0643: : ((ExecutableMemberDoc) holder).typeParameters();
0644:
0645: for (com.sun.javadoc.TypeVariable typeVariable : tparams) {
0646: if (typeVariable.simpleTypeName().startsWith(prefix)) {
0647: items.add(JavadocCompletionItem.createNameItem(
0648: '<' + typeVariable.simpleTypeName() + '>',
0649: substitutionOffset));
0650: }
0651: }
0652: }
0653:
0654: private void completeClassOrPkg(String fqn, String prefix,
0655: int substitutionOffset, JavadocContext jdctx) {
0656: String pkgPrefix;
0657: if (fqn == null) {
0658: pkgPrefix = prefix;
0659: addTypes(EnumSet.<ElementKind> of(CLASS, INTERFACE, ENUM,
0660: ANNOTATION_TYPE), null, null, prefix,
0661: substitutionOffset, jdctx);
0662:
0663: } else {
0664: pkgPrefix = fqn + '.' + prefix;
0665: PackageElement pkgElm = jdctx.javac.getElements()
0666: .getPackageElement(fqn);
0667: if (pkgElm != null) {
0668: addPackageContent(pkgElm, EnumSet.<ElementKind> of(
0669: CLASS, INTERFACE, ENUM, ANNOTATION_TYPE), null,
0670: prefix, substitutionOffset, jdctx);
0671: }
0672:
0673: TypeElement typeElm = jdctx.javac.getElements()
0674: .getTypeElement(fqn);
0675: if (typeElm != null) {
0676: // inner classes
0677: addInnerClasses(typeElm, EnumSet.<ElementKind> of(
0678: CLASS, INTERFACE, ENUM, ANNOTATION_TYPE), null,
0679: prefix, substitutionOffset, jdctx);
0680: }
0681: }
0682:
0683: for (String pkgName : jdctx.javac.getClasspathInfo()
0684: .getClassIndex().getPackageNames(pkgPrefix, true,
0685: EnumSet.allOf(ClassIndex.SearchScope.class)))
0686: if (pkgName.length() > 0)
0687: items.add(JavaCompletionItem.createPackageItem(pkgName,
0688: substitutionOffset, false));
0689: }
0690:
0691: private void completeThrowsOrPkg(String fqn, String prefix,
0692: int substitutionOffset, JavadocContext jdctx) {
0693: final Elements elements = jdctx.javac.getElements();
0694: String pkgPrefix;
0695:
0696: // add RuntimeExceptions
0697:
0698: if (fqn == null) {
0699: pkgPrefix = prefix;
0700: addTypes(EnumSet.<ElementKind> of(CLASS), findDeclaredType(
0701: "java.lang.RuntimeException", elements), // NOI18N
0702: null, prefix, substitutionOffset, jdctx);
0703:
0704: } else {
0705: pkgPrefix = fqn + '.' + prefix;
0706:
0707: PackageElement pkgElm = elements.getPackageElement(fqn);
0708: if (pkgElm != null) {
0709: addPackageContent(pkgElm, EnumSet
0710: .<ElementKind> of(CLASS), findDeclaredType(
0711: "java.lang.RuntimeException", elements), // NOI18N
0712: prefix, substitutionOffset, jdctx);
0713: }
0714:
0715: TypeElement typeElm = elements.getTypeElement(fqn);
0716: if (typeElm != null) {
0717: // inner classes
0718: addInnerClasses(typeElm, EnumSet
0719: .<ElementKind> of(CLASS), findDeclaredType(
0720: "java.lang.RuntimeException", elements), // NOI18N
0721: prefix, substitutionOffset, jdctx);
0722: }
0723: }
0724:
0725: // add declared Throwables
0726:
0727: ExecutableMemberDoc edoc = (ExecutableMemberDoc) jdctx.jdoc;
0728: for (Type type : edoc.thrownExceptionTypes()) {
0729: String typeName = type.typeName();
0730: if (typeName.startsWith(prefix)) {
0731: String qualTypeName = type.qualifiedTypeName();
0732: TypeElement typeElement = elements
0733: .getTypeElement(qualTypeName);
0734: if (typeElement == null) {
0735: continue;
0736: }
0737: items.add(JavaCompletionItem
0738: .createTypeItem(typeElement,
0739: (DeclaredType) typeElement.asType(),
0740: substitutionOffset,
0741: typeName != qualTypeName, elements
0742: .isDeprecated(typeElement),
0743: false, true));
0744: }
0745: }
0746:
0747: // add packages
0748:
0749: for (String pkgName : jdctx.javac.getClasspathInfo()
0750: .getClassIndex().getPackageNames(pkgPrefix, true,
0751: EnumSet.allOf(ClassIndex.SearchScope.class)))
0752: if (pkgName.length() > 0)
0753: items.add(JavaCompletionItem.createPackageItem(pkgName,
0754: substitutionOffset, false));
0755: }
0756:
0757: private static DeclaredType findDeclaredType(CharSequence fqn,
0758: Elements elements) {
0759: TypeElement re = elements.getTypeElement(fqn);
0760: if (re != null) {
0761: TypeMirror asType = re.asType();
0762: if (asType.getKind() == TypeKind.DECLARED) {
0763: return (DeclaredType) asType;
0764: }
0765: }
0766: return null;
0767: }
0768:
0769: private void completeClassMember(String fqn, String prefix,
0770: int substitutionOffset, JavadocContext jdctx) {
0771: Element elm;
0772: if (fqn == null) {
0773: // XXX local members
0774: elm = null;
0775:
0776: addLocalMembersAndVars(jdctx, prefix, substitutionOffset);
0777: } else {
0778: elm = lookAroundForClass(jdctx.jdoc, fqn, jdctx.javac
0779: .getElements());
0780: // elm = jdctx.javac.getElements().getTypeElement(fqn);
0781: // if (elm == null) {
0782: // Element scope = jdctx.handle.resolve(jdctx.javac);
0783: // while (scope != null && !(scope.getKind().isClass() || scope.getKind().isInterface())) {
0784: // scope = scope.getEnclosingElement();
0785: // }
0786: // if (scope == null) {
0787: // return;
0788: // }
0789: // TypeMirror parsedType = jdctx.javac.getTreeUtilities().parseType(fqn, (TypeElement) scope);
0790: // if (parsedType != null && parsedType.getKind() == TypeKind.DECLARED) {
0791: // elm = ((DeclaredType) parsedType).asElement();
0792: // }
0793: // }
0794: }
0795:
0796: if (elm != null) {
0797: addMembers(jdctx, prefix, substitutionOffset, elm.asType(),
0798: elm, EnumSet.<ElementKind> of(ENUM_CONSTANT, FIELD,
0799: METHOD, CONSTRUCTOR), null);
0800: }
0801: }
0802:
0803: private static TypeElement lookAroundForClass(Doc holder,
0804: String fqn, Elements util) {
0805: // borrowed from SeeTagImpl
0806: ClassDoc container = null;
0807: if (holder instanceof MemberDoc) {
0808: container = ((ProgramElementDoc) holder).containingClass();
0809: } else if (holder instanceof ClassDoc) {
0810: container = (ClassDoc) holder;
0811: }
0812:
0813: if (container != null) {
0814: ClassDoc foundClass = container.findClass(fqn);
0815: if (foundClass != null) {
0816: return util.getTypeElement(foundClass.qualifiedName());
0817: }
0818: }
0819: return null;
0820: }
0821:
0822: private void addMembers(final JavadocContext env,
0823: final String prefix, final int substitutionOffset,
0824: final TypeMirror type, final Element elem,
0825: final EnumSet<ElementKind> kinds,
0826: final DeclaredType baseType) {
0827: Set<? extends TypeMirror> smartTypes = /*queryType == COMPLETION_QUERY_TYPE ? env.getSmartTypes() :*/null;
0828: final CompilationInfo controller = env.javac;
0829: final Trees trees = controller.getTrees();
0830: final Elements elements = controller.getElements();
0831: final Types types = controller.getTypes();
0832: final TreeUtilities tu = controller.getTreeUtilities();
0833: TypeElement typeElem = type.getKind() == TypeKind.DECLARED ? (TypeElement) ((DeclaredType) type)
0834: .asElement()
0835: : null;
0836: // final boolean isStatic = elem != null && (elem.getKind().isClass() || elem.getKind().isInterface() || elem.getKind() == TYPE_PARAMETER);
0837: // final boolean isSuperCall = elem != null && elem.getKind().isField() && elem.getSimpleName().contentEquals(SUPER_KEYWORD);
0838: Element docelm = env.handle.resolve(controller);
0839: TreePath docpath = trees.getPath(docelm);
0840: final Scope scope = trees.getScope(docpath);
0841: final boolean[] ctorSeen = { false };
0842: TypeElement enclClass = scope.getEnclosingClass();
0843: final TypeMirror enclType = enclClass != null ? enclClass
0844: .asType() : null;
0845: ElementUtilities.ElementAcceptor acceptor = new ElementUtilities.ElementAcceptor() {
0846: public boolean accept(Element e, TypeMirror t) {
0847: switch (e.getKind()) {
0848: case FIELD:
0849: String name = e.getSimpleName().toString();
0850: return Utilities.startsWith(name, prefix)
0851: && !CLASS_KEYWORD.equals(name)
0852: && (Utilities.isShowDeprecatedMembers() || !elements
0853: .isDeprecated(e));
0854: // &&
0855: // isOfKindAndType(asMemberOf(e, t, types), e, kinds, baseType, scope, trees, types) &&
0856: // tu.isAccessible(scope, e, isSuperCall && enclType != null ? enclType : t) &&
0857: // (!isStatic || e.getModifiers().contains(Modifier.STATIC)) &&
0858: // (isStatic || !e.getSimpleName().contentEquals(THIS_KEYWORD)) &&
0859: // ((isStatic && !inImport) /*|| !e.getSimpleName().contentEquals(CLASS_KEYWORD)*/);
0860: case ENUM_CONSTANT:
0861: return Utilities.startsWith(e.getSimpleName()
0862: .toString(), prefix)
0863: && (Utilities.isShowDeprecatedMembers() || !elements
0864: .isDeprecated(e)) &&
0865: // isOfKindAndType(asMemberOf(e, t, types), e, kinds, baseType, scope, trees, types) &&
0866: tu.isAccessible(scope, e, t);
0867: case METHOD:
0868: return Utilities.startsWith(e.getSimpleName()
0869: .toString(), prefix)
0870: && (Utilities.isShowDeprecatedMembers() || !elements
0871: .isDeprecated(e));
0872: // &&
0873: // isOfKindAndType(((ExecutableType)asMemberOf(e, t, types)).getReturnType(), e, kinds, baseType, scope, trees, types) &&
0874: // (isSuperCall && e.getModifiers().contains(PROTECTED) || tu.isAccessible(scope, e, isSuperCall && enclType != null ? enclType : t)) &&
0875: // (!isStatic || e.getModifiers().contains(Modifier.STATIC));
0876: // case CLASS:
0877: // case ENUM:
0878: // case INTERFACE:
0879: // case ANNOTATION_TYPE:
0880: // return Utilities.startsWith(e.getSimpleName().toString(), prefix) &&
0881: // (Utilities.isShowDeprecatedMembers() || !elements.isDeprecated(e)) &&
0882: // isOfKindAndType(e.asType(), e, kinds, baseType, scope, trees, types) &&
0883: // tu.isAccessible(scope, e, t) && isStatic;
0884: case CONSTRUCTOR:
0885: ctorSeen[0] = true;
0886: return (Utilities.isShowDeprecatedMembers() || !elements
0887: .isDeprecated(e))
0888: &&
0889: // isOfKindAndType(e.getEnclosingElement().asType(), e, kinds, baseType, scope, trees, types) &&
0890: (tu.isAccessible(scope, e, t) || (elem
0891: .getModifiers().contains(
0892: Modifier.ABSTRACT) && !e
0893: .getModifiers().contains(
0894: Modifier.PRIVATE)));
0895: // &&
0896: // isStatic;
0897: }
0898: return false;
0899: }
0900: };
0901: for (Element e : controller.getElementUtilities().getMembers(
0902: type, acceptor)) {
0903: switch (e.getKind()) {
0904: case ENUM_CONSTANT:
0905: case FIELD:
0906: TypeMirror tm = type.getKind() == TypeKind.DECLARED ? types
0907: .asMemberOf((DeclaredType) type, e)
0908: : e.asType();
0909: items.add(JavaCompletionItem.createVariableItem(
0910: (VariableElement) e, tm, substitutionOffset,
0911: typeElem != e.getEnclosingElement(), elements
0912: .isDeprecated(e), /*isOfSmartType(env, tm, smartTypes)*/
0913: false));
0914: break;
0915: case CONSTRUCTOR:
0916: case METHOD:
0917: ExecutableType et = (ExecutableType) (type.getKind() == TypeKind.DECLARED ? types
0918: .asMemberOf((DeclaredType) type, e)
0919: : e.asType());
0920: // items.add(JavaCompletionItem.createExecutableItem((ExecutableElement) e, et, substitutionOffset, typeElem != e.getEnclosingElement(), elements.isDeprecated(e), inImport, /*isOfSmartType(env, et.getReturnType(), smartTypes)*/false));
0921: items.add(JavadocCompletionItem.createExecutableItem(
0922: (ExecutableElement) e, et, substitutionOffset,
0923: typeElem != e.getEnclosingElement(), elements
0924: .isDeprecated(e)));
0925: break;
0926: // case CLASS:
0927: // case ENUM:
0928: // case INTERFACE:
0929: // case ANNOTATION_TYPE:
0930: // DeclaredType dt = (DeclaredType)(type.getKind() == TypeKind.DECLARED ? types.asMemberOf((DeclaredType)type, e) : e.asType());
0931: // results.add(JavaCompletionItem.createTypeItem((TypeElement)e, dt, anchorOffset, false, elements.isDeprecated(e), insideNew, false));
0932: // break;
0933: }
0934: }
0935: if (!ctorSeen[0] && kinds.contains(CONSTRUCTOR)
0936: && elem.getKind().isInterface()) {
0937: items.add(JavaCompletionItem.createDefaultConstructorItem(
0938: (TypeElement) elem, substitutionOffset, /*isOfSmartType(env, type, smartTypes)*/
0939: false));
0940: }
0941: }
0942:
0943: private void addLocalMembersAndVars(final JavadocContext env,
0944: final String prefix, final int substitutionOffset) {
0945: final CompilationInfo controller = env.javac;
0946: final Elements elements = controller.getElements();
0947: final Types types = controller.getTypes();
0948: final TreeUtilities tu = controller.getTreeUtilities();
0949: final Trees trees = controller.getTrees();
0950: Element docelm = env.handle.resolve(controller);
0951: TreePath docpath = trees.getPath(docelm);
0952: final Scope scope = trees.getScope(docpath);
0953: Set<? extends TypeMirror> smartTypes = null;
0954: // if (queryType == COMPLETION_QUERY_TYPE) {
0955: // smartTypes = env.getSmartTypes();
0956: // if (smartTypes != null) {
0957: // for (TypeMirror st : smartTypes) {
0958: // if (st.getKind().isPrimitive())
0959: // st = types.boxedClass((PrimitiveType)st).asType();
0960: // if (st.getKind() == TypeKind.DECLARED) {
0961: // final DeclaredType type = (DeclaredType)st;
0962: // final TypeElement element = (TypeElement)type.asElement();
0963: // if (withinScope(env, element))
0964: // continue;
0965: // final boolean isStatic = element.getKind().isClass() || element.getKind().isInterface();
0966: // final Set<? extends TypeMirror> finalSmartTypes = smartTypes;
0967: // ElementUtilities.ElementAcceptor acceptor = new ElementUtilities.ElementAcceptor() {
0968: // public boolean accept(Element e, TypeMirror t) {
0969: // return (!isStatic || e.getModifiers().contains(STATIC)) &&
0970: // Utilities.startsWith(e.getSimpleName().toString(), prefix) &&
0971: // tu.isAccessible(scope, e, t) &&
0972: // (e.getKind().isField() && isOfSmartType(env, ((VariableElement)e).asType(), finalSmartTypes) || e.getKind() == METHOD && isOfSmartType(env, ((ExecutableElement)e).getReturnType(), finalSmartTypes));
0973: // }
0974: // };
0975: // for (Element ee : controller.getElementUtilities().getMembers(type, acceptor)) {
0976: // if (Utilities.isShowDeprecatedMembers() || !elements.isDeprecated(ee))
0977: // results.add(JavaCompletionItem.createStaticMemberItem(type, ee, types.asMemberOf(type, ee), anchorOffset, elements.isDeprecated(ee)));
0978: // }
0979: // }
0980: // }
0981: // }
0982: // }
0983: final TypeElement enclClass = scope.getEnclosingClass();
0984: // final boolean isStatic = enclClass == null ? false :
0985: // (tu.isStaticContext(scope) || (env.getPath().getLeaf().getKind() == Tree.Kind.BLOCK && ((BlockTree)env.getPath().getLeaf()).isStatic()));
0986: // final Collection<? extends Element> illegalForwardRefs = env.getForwardReferences();
0987: final ExecutableElement method = scope.getEnclosingMethod();
0988: ElementUtilities.ElementAcceptor acceptor = new ElementUtilities.ElementAcceptor() {
0989: public boolean accept(Element e, TypeMirror t) {
0990: switch (e.getKind()) {
0991: case CONSTRUCTOR:
0992: return false;
0993: // case LOCAL_VARIABLE:
0994: // case EXCEPTION_PARAMETER:
0995: // case PARAMETER:
0996: // return Utilities.startsWith(e.getSimpleName().toString(), prefix) &&
0997: // (method == e.getEnclosingElement() || e.getModifiers().contains(FINAL) ||
0998: // (method == null && (e.getEnclosingElement().getKind() == INSTANCE_INIT ||
0999: // e.getEnclosingElement().getKind() == STATIC_INIT))) &&
1000: // !illegalForwardRefs.contains(e);
1001: case FIELD:
1002: // if (e.getSimpleName().contentEquals(THIS_KEYWORD) || e.getSimpleName().contentEquals(SUPER_KEYWORD))
1003: // return Utilities.startsWith(e.getSimpleName().toString(), prefix) && !isStatic;
1004: String name = e.getSimpleName().toString();
1005: return Utilities.startsWith(name, prefix)
1006: && !CLASS_KEYWORD.equals(name)
1007: && (Utilities.isShowDeprecatedMembers() || !elements
1008: .isDeprecated(e));
1009: // String name = e.getSimpleName().toString();
1010: // return !name.equals(THIS_KEYWORD) && !name.equals(SUPER_KEYWORD)
1011: // && Utilities.startsWith(name, prefix);
1012: case ENUM_CONSTANT:
1013: return Utilities.startsWith(e.getSimpleName()
1014: .toString(), prefix)
1015: &&
1016: // !illegalForwardRefs.contains(e) &&
1017: // (!isStatic || e.getModifiers().contains(STATIC)) &&
1018: (Utilities.isShowDeprecatedMembers() || !elements
1019: .isDeprecated(e))
1020: && tu.isAccessible(scope, e, t);
1021: case METHOD:
1022: return Utilities.startsWith(e.getSimpleName()
1023: .toString(), prefix)
1024: && (Utilities.isShowDeprecatedMembers() || !elements
1025: .isDeprecated(e)) &&
1026: // (!isStatic || e.getModifiers().contains(STATIC)) &&
1027: tu.isAccessible(scope, e, t);
1028: }
1029: return false;
1030: }
1031: };
1032: for (Element e : controller.getElementUtilities()
1033: .getLocalMembersAndVars(scope, acceptor)) {
1034: switch (e.getKind()) {
1035: case ENUM_CONSTANT:
1036: items
1037: .add(JavaCompletionItem
1038: .createVariableItem(
1039: (VariableElement) e,
1040: e.asType(), substitutionOffset,
1041: scope.getEnclosingClass() != e
1042: .getEnclosingElement(),
1043: elements.isDeprecated(e), false/*isOfSmartType(env, e.asType(), smartTypes)*/));
1044: break;
1045: case FIELD:
1046: String name = e.getSimpleName().toString();
1047: TypeMirror tm = asMemberOf(e,
1048: enclClass != null ? enclClass.asType() : null,
1049: types);
1050: items
1051: .add(JavaCompletionItem
1052: .createVariableItem(
1053: (VariableElement) e, tm,
1054: substitutionOffset,
1055: scope.getEnclosingClass() != e
1056: .getEnclosingElement(),
1057: elements.isDeprecated(e), false/*isOfSmartType(env, tm, smartTypes)*/));
1058: break;
1059: case METHOD:
1060: ExecutableType et = (ExecutableType) asMemberOf(e,
1061: enclClass != null ? enclClass.asType() : null,
1062: types);
1063: // items.add(JavaCompletionItem.createExecutableItem((ExecutableElement)e, et, substitutionOffset, scope.getEnclosingClass() != e.getEnclosingElement(), elements.isDeprecated(e), false, false/*isOfSmartType(env, et.getReturnType(), smartTypes)*/));
1064: items.add(JavadocCompletionItem.createExecutableItem(
1065: (ExecutableElement) e, et, substitutionOffset,
1066: scope.getEnclosingClass() != e
1067: .getEnclosingElement(), elements
1068: .isDeprecated(e)));
1069: break;
1070: }
1071: }
1072: }
1073:
1074: private static TypeMirror asMemberOf(Element element,
1075: TypeMirror type, Types types) {
1076: TypeMirror ret = element.asType();
1077: TypeMirror enclType = element.getEnclosingElement().asType();
1078: if (enclType.getKind() == TypeKind.DECLARED)
1079: enclType = types.erasure(enclType);
1080: while (type != null && type.getKind() == TypeKind.DECLARED) {
1081: if (types.isSubtype(type, enclType)) {
1082: ret = types.asMemberOf((DeclaredType) type, element);
1083: break;
1084: }
1085: type = ((DeclaredType) type).getEnclosingType();
1086: }
1087: return ret;
1088: }
1089:
1090: private void addTypes(EnumSet<ElementKind> kinds,
1091: DeclaredType baseType, Set<? extends Element> toExclude,
1092: String prefix, int substitutionOffset, JavadocContext jdctx) {
1093:
1094: if (queryType == CompletionProvider.COMPLETION_ALL_QUERY_TYPE) {
1095: if (baseType == null) {
1096: addAllTypes(jdctx, kinds, prefix, substitutionOffset);
1097: } else {
1098: Elements elements = jdctx.javac.getElements();
1099: for (DeclaredType subtype : getSubtypesOf(baseType,
1100: prefix, jdctx)) {
1101: TypeElement elem = (TypeElement) subtype
1102: .asElement();
1103: if (Utilities.isShowDeprecatedMembers()
1104: || !elements.isDeprecated(elem))
1105: items.add(JavaCompletionItem.createTypeItem(
1106: elem, subtype, substitutionOffset,
1107: true, elements.isDeprecated(elem),
1108: false, false));
1109: }
1110: }
1111: } else {
1112: addLocalAndImportedTypes(jdctx, kinds, baseType, toExclude,
1113: prefix, substitutionOffset);
1114: hasAdditionalItems = true;
1115: }
1116: }
1117:
1118: private void addLocalAndImportedTypes(final JavadocContext env,
1119: final EnumSet<ElementKind> kinds,
1120: final DeclaredType baseType,
1121: final Set<? extends Element> toExclude,
1122: final String prefix, int substitutionOffset) {
1123: final CompilationInfo controller = env.javac;
1124: final Trees trees = controller.getTrees();
1125: final Elements elements = controller.getElements();
1126: final Types types = controller.getTypes();
1127: final TreeUtilities tu = controller.getTreeUtilities();
1128: // final Scope scope = env.getScope();
1129: Element docelm = env.handle.resolve(controller);
1130: TreePath docpath = trees.getPath(docelm);
1131: final Scope scope = trees.getScope(docpath);
1132: final TypeElement enclClass = scope.getEnclosingClass();
1133: final boolean isStatic = enclClass == null ? false : tu
1134: .isStaticContext(scope);
1135: ElementUtilities.ElementAcceptor acceptor = new ElementUtilities.ElementAcceptor() {
1136: public boolean accept(Element e, TypeMirror t) {
1137: if ((toExclude == null || !toExclude.contains(e))
1138: && (e.getKind().isClass()
1139: || e.getKind().isInterface() || e
1140: .getKind() == TYPE_PARAMETER)) {
1141: String name = e.getSimpleName().toString();
1142: return name.length() > 0
1143: && !Character.isDigit(name.charAt(0))
1144: && startsWith(name, prefix)
1145: && (!isStatic || e.getModifiers().contains(
1146: Modifier.STATIC))
1147: && (Utilities.isShowDeprecatedMembers() || !elements
1148: .isDeprecated(e))
1149: && isOfKindAndType(e.asType(), e, kinds,
1150: baseType, scope, trees, types);
1151: }
1152: return false;
1153: }
1154: };
1155: for (Element e : controller.getElementUtilities()
1156: .getLocalMembersAndVars(scope, acceptor)) {
1157: switch (e.getKind()) {
1158: case CLASS:
1159: case ENUM:
1160: case INTERFACE:
1161: case ANNOTATION_TYPE:
1162: items.add(JavadocCompletionItem.createTypeItem(
1163: (TypeElement) e, substitutionOffset, false,
1164: elements.isDeprecated(e)));
1165: break;
1166: }
1167: }
1168: acceptor = new ElementUtilities.ElementAcceptor() {
1169: public boolean accept(Element e, TypeMirror t) {
1170: if ((e.getKind().isClass() || e.getKind().isInterface())) {
1171: return (toExclude == null || !toExclude.contains(e))
1172: && startsWith(e.getSimpleName().toString(),
1173: prefix)
1174: && (Utilities.isShowDeprecatedMembers() || !elements
1175: .isDeprecated(e))
1176: && trees.isAccessible(scope,
1177: (TypeElement) e)
1178: && isOfKindAndType(e.asType(), e, kinds,
1179: baseType, scope, trees, types);
1180: }
1181: return false;
1182: }
1183: };
1184: for (TypeElement e : controller.getElementUtilities()
1185: .getGlobalTypes(acceptor)) {
1186: items.add(JavadocCompletionItem.createTypeItem(
1187: (TypeElement) e, substitutionOffset, false,
1188: elements.isDeprecated(e)));
1189: }
1190: }
1191:
1192: private void addAllTypes(JavadocContext env,
1193: EnumSet<ElementKind> kinds, String prefix,
1194: int substitutionOffset) {
1195: // String prefix = env.getPrefix();
1196: CompilationInfo controller = env.javac;
1197: boolean isCaseSensitive = false;
1198: ClassIndex.NameKind kind = isCaseSensitive ? ClassIndex.NameKind.PREFIX
1199: : ClassIndex.NameKind.CASE_INSENSITIVE_PREFIX;
1200: // ClassIndex.NameKind kind = env.isCamelCasePrefix() ?
1201: // Utilities.isCaseSensitive() ? ClassIndex.NameKind.CAMEL_CASE : ClassIndex.NameKind.CAMEL_CASE_INSENSITIVE :
1202: // Utilities.isCaseSensitive() ? ClassIndex.NameKind.PREFIX : ClassIndex.NameKind.CASE_INSENSITIVE_PREFIX;
1203: for (ElementHandle<TypeElement> name : controller
1204: .getJavaSource().getClasspathInfo().getClassIndex()
1205: .getDeclaredTypes(prefix, kind,
1206: EnumSet.allOf(ClassIndex.SearchScope.class))) {
1207: LazyTypeCompletionItem item = LazyTypeCompletionItem
1208: .create(name, kinds, substitutionOffset, controller
1209: .getJavaSource(), false);
1210: // XXX item.isAnnonInner() is package private :-(
1211: // if (item.isAnnonInner())
1212: // continue;
1213: items.add(item);
1214: }
1215: }
1216:
1217: private void addInnerClasses(TypeElement te,
1218: EnumSet<ElementKind> kinds, DeclaredType baseType,
1219: String prefix, int substitutionOffset, JavadocContext jdctx) {
1220: CompilationInfo controller = jdctx.javac;
1221: Element srcEl = jdctx.handle.resolve(controller);
1222: Elements elements = controller.getElements();
1223: Types types = controller.getTypes();
1224: Trees trees = controller.getTrees();
1225: Scope scope = /*env.getScope()*/trees.getScope(trees
1226: .getPath(srcEl));
1227: for (Element e : controller.getElementUtilities().getMembers(
1228: te.asType(), null)) {
1229: if (e.getKind().isClass() || e.getKind().isInterface()) {
1230: String name = e.getSimpleName().toString();
1231: if (Utilities.startsWith(name, prefix)
1232: && (Utilities.isShowDeprecatedMembers() || !elements
1233: .isDeprecated(e))
1234: && trees.isAccessible(scope, (TypeElement) e)
1235: && isOfKindAndType(e.asType(), e, kinds,
1236: baseType, scope, trees, types)) {
1237: items
1238: .add(JavadocCompletionItem
1239: .createTypeItem((TypeElement) e,
1240: substitutionOffset, false,
1241: elements.isDeprecated(e)/*, isOfSmartType(env, e.asType(), smartTypes)*/));
1242: }
1243: }
1244: }
1245: }
1246:
1247: private void addPackageContent(PackageElement pe,
1248: EnumSet<ElementKind> kinds, DeclaredType baseType,
1249: String prefix, int substitutionOffset, JavadocContext jdctx) {
1250: CompilationInfo controller = jdctx.javac;
1251: Element srcEl = jdctx.handle.resolve(controller);
1252: Elements elements = controller.getElements();
1253: Types types = controller.getTypes();
1254: Trees trees = controller.getTrees();
1255: Scope scope = trees.getScope(trees.getPath(srcEl));
1256: for (Element e : pe.getEnclosedElements()) {
1257: if (e.getKind().isClass() || e.getKind().isInterface()) {
1258: String name = e.getSimpleName().toString();
1259: if (Utilities.startsWith(name, prefix)
1260: && (Utilities.isShowDeprecatedMembers() || !elements
1261: .isDeprecated(e))
1262: && trees.isAccessible(scope, (TypeElement) e)
1263: && isOfKindAndType(e.asType(), e, kinds,
1264: baseType, scope, trees, types)) {
1265: items
1266: .add(JavadocCompletionItem
1267: .createTypeItem((TypeElement) e,
1268: substitutionOffset, false,
1269: elements.isDeprecated(e)/*, isOfSmartType(env, e.asType(), smartTypes)*/));
1270: }
1271: }
1272: }
1273: }
1274:
1275: private boolean isOfKindAndType(TypeMirror type, Element e,
1276: EnumSet<ElementKind> kinds, TypeMirror base, Scope scope,
1277: Trees trees, Types types) {
1278: if (kinds.contains(e.getKind())) {
1279: if (base == null)
1280: return true;
1281: if (types.isSubtype(type, base))
1282: return true;
1283: }
1284: if ((e.getKind().isClass() || e.getKind().isInterface())
1285: && (kinds.contains(ANNOTATION_TYPE)
1286: || kinds.contains(CLASS)
1287: || kinds.contains(ENUM) || kinds
1288: .contains(INTERFACE))) {
1289: DeclaredType dt = (DeclaredType) e.asType();
1290: for (Element ee : e.getEnclosedElements())
1291: if (trees.isAccessible(scope, ee, dt)
1292: && isOfKindAndType(ee.asType(), ee, kinds,
1293: base, scope, trees, types))
1294: return true;
1295: }
1296: return false;
1297: }
1298:
1299: /* copied from JavaCompletionProvider */
1300: private List<DeclaredType> getSubtypesOf(DeclaredType baseType,
1301: String prefix, JavadocContext jdctx) {
1302: if (((TypeElement) baseType.asElement()).getQualifiedName()
1303: .contentEquals("java.lang.Object"))
1304: return Collections.emptyList();
1305: LinkedList<DeclaredType> subtypes = new LinkedList<DeclaredType>();
1306: CompilationInfo controller = jdctx.javac;
1307: Types types = controller.getTypes();
1308: Trees trees = controller.getTrees();
1309: Element resolvedElm = jdctx.handle.resolve(controller);
1310: Scope scope = trees.getScope(trees.getPath(resolvedElm));
1311: if (prefix != null && prefix.length() > 2
1312: && baseType.getTypeArguments().isEmpty()) {
1313: // XXX resolve camels
1314: // ClassIndex.NameKind kind = env.isCamelCasePrefix() ?
1315: ClassIndex.NameKind kind = false ? Utilities
1316: .isCaseSensitive() ? ClassIndex.NameKind.CAMEL_CASE
1317: : ClassIndex.NameKind.CAMEL_CASE_INSENSITIVE
1318: : Utilities.isCaseSensitive() ? ClassIndex.NameKind.PREFIX
1319: : ClassIndex.NameKind.CASE_INSENSITIVE_PREFIX;
1320: for (ElementHandle<TypeElement> handle : controller
1321: .getJavaSource()
1322: .getClasspathInfo()
1323: .getClassIndex()
1324: .getDeclaredTypes(prefix, kind,
1325: EnumSet.allOf(ClassIndex.SearchScope.class))) {
1326: TypeElement te = handle.resolve(controller);
1327: if (te != null
1328: && trees.isAccessible(scope, te)
1329: && types.isSubtype(types.getDeclaredType(te),
1330: baseType))
1331: subtypes.add(types.getDeclaredType(te));
1332: }
1333: } else {
1334: HashSet<TypeElement> elems = new HashSet<TypeElement>();
1335: LinkedList<DeclaredType> bases = new LinkedList<DeclaredType>();
1336: bases.add(baseType);
1337: ClassIndex index = controller.getJavaSource()
1338: .getClasspathInfo().getClassIndex();
1339: while (!bases.isEmpty()) {
1340: DeclaredType head = bases.remove();
1341: TypeElement elem = (TypeElement) head.asElement();
1342: if (!elems.add(elem))
1343: continue;
1344: if (startsWith(elem.getSimpleName().toString(), prefix)
1345: && trees.isAccessible(scope, elem))
1346: subtypes.add(head);
1347: List<? extends TypeMirror> tas = head
1348: .getTypeArguments();
1349: boolean isRaw = !tas.iterator().hasNext();
1350: subtypes: for (ElementHandle<TypeElement> eh : index
1351: .getElements(
1352: ElementHandle.create(elem),
1353: EnumSet
1354: .of(ClassIndex.SearchKind.IMPLEMENTORS),
1355: EnumSet
1356: .allOf(ClassIndex.SearchScope.class))) {
1357: TypeElement e = eh.resolve(controller);
1358: if (e != null) {
1359: if (trees.isAccessible(scope, e)) {
1360: if (isRaw) {
1361: DeclaredType dt = types
1362: .getDeclaredType(e);
1363: bases.add(dt);
1364: } else {
1365: HashMap<Element, TypeMirror> map = new HashMap<Element, TypeMirror>();
1366: TypeMirror sup = e.getSuperclass();
1367: if (sup.getKind() == TypeKind.DECLARED
1368: && ((DeclaredType) sup)
1369: .asElement() == elem) {
1370: DeclaredType dt = (DeclaredType) sup;
1371: Iterator<? extends TypeMirror> ittas = tas
1372: .iterator();
1373: Iterator<? extends TypeMirror> it = dt
1374: .getTypeArguments()
1375: .iterator();
1376: while (it.hasNext()
1377: && ittas.hasNext()) {
1378: TypeMirror basetm = ittas
1379: .next();
1380: TypeMirror stm = it.next();
1381: if (basetm != stm) {
1382: if (stm.getKind() == TypeKind.TYPEVAR) {
1383: map
1384: .put(
1385: ((TypeVariable) stm)
1386: .asElement(),
1387: basetm);
1388: } else {
1389: continue subtypes;
1390: }
1391: }
1392: }
1393: if (it.hasNext() != ittas.hasNext()) {
1394: continue subtypes;
1395: }
1396: } else {
1397: for (TypeMirror tm : e
1398: .getInterfaces()) {
1399: if (((DeclaredType) tm)
1400: .asElement() == elem) {
1401: DeclaredType dt = (DeclaredType) tm;
1402: Iterator<? extends TypeMirror> ittas = tas
1403: .iterator();
1404: Iterator<? extends TypeMirror> it = dt
1405: .getTypeArguments()
1406: .iterator();
1407: while (it.hasNext()
1408: && ittas.hasNext()) {
1409: TypeMirror basetm = ittas
1410: .next();
1411: TypeMirror stm = it
1412: .next();
1413: if (basetm != stm) {
1414: if (stm.getKind() == TypeKind.TYPEVAR) {
1415: map
1416: .put(
1417: ((TypeVariable) stm)
1418: .asElement(),
1419: basetm);
1420: } else {
1421: continue subtypes;
1422: }
1423: }
1424: }
1425: if (it.hasNext() != ittas
1426: .hasNext()) {
1427: continue subtypes;
1428: }
1429: break;
1430: }
1431: }
1432: }
1433: bases
1434: .add(getDeclaredType(e, map,
1435: types));
1436: }
1437: }
1438: } else {
1439: Logger
1440: .getLogger("global")
1441: .log(
1442: Level.FINE,
1443: String
1444: .format(
1445: "Cannot resolve: %s on bootpath: %s classpath: %s sourcepath: %s\n",
1446: eh.toString(),
1447: controller
1448: .getClasspathInfo()
1449: .getClassPath(
1450: ClasspathInfo.PathKind.BOOT),
1451: controller
1452: .getClasspathInfo()
1453: .getClassPath(
1454: ClasspathInfo.PathKind.COMPILE),
1455: controller
1456: .getClasspathInfo()
1457: .getClassPath(
1458: ClasspathInfo.PathKind.SOURCE)));
1459: }
1460: }
1461: }
1462: }
1463: return subtypes;
1464: }
1465:
1466: private DeclaredType getDeclaredType(TypeElement e,
1467: HashMap<? extends Element, ? extends TypeMirror> map,
1468: Types types) {
1469: List<? extends TypeParameterElement> tpes = e
1470: .getTypeParameters();
1471: TypeMirror[] targs = new TypeMirror[tpes.size()];
1472: int i = 0;
1473: for (Iterator<? extends TypeParameterElement> it = tpes
1474: .iterator(); it.hasNext();) {
1475: TypeParameterElement tpe = it.next();
1476: TypeMirror t = map.get(tpe);
1477: targs[i++] = t != null ? t : tpe.asType();
1478: }
1479: Element encl = e.getEnclosingElement();
1480: if ((encl.getKind().isClass() || encl.getKind().isInterface())
1481: && !((TypeElement) encl).getTypeParameters().isEmpty())
1482: return types.getDeclaredType(getDeclaredType(
1483: (TypeElement) encl, map, types), e, targs);
1484: return types.getDeclaredType(e, targs);
1485: }
1486:
1487: private boolean startsWith(String theString, String prefix) {
1488: // XXX isCamelCasePrefix
1489: return /*env.isCamelCasePrefix()*/false ? Utilities
1490: .isCaseSensitive() ? Utilities.startsWithCamelCase(
1491: theString, prefix) : Utilities.startsWithCamelCase(
1492: theString, prefix)
1493: || Utilities.startsWith(theString, prefix) : Utilities
1494: .startsWith(theString, prefix);
1495: }
1496:
1497: void resolveOtherText(JavadocContext jdctx,
1498: TokenSequence<JavadocTokenId> jdts) {
1499: Token<JavadocTokenId> token = jdts.token();
1500: assert token != null;
1501: assert token.id() == JavadocTokenId.OTHER_TEXT;
1502:
1503: CharSequence text = token.text();
1504: int pos = caretOffset - jdts.offset();
1505: Tag tag = jdctx.positions.getTag(caretOffset);
1506: if (pos > 0 && pos <= text.length()
1507: && text.charAt(pos - 1) == '{') {
1508: if (tag != null && !JavadocCompletionUtils.isBlockTag(tag)) {
1509: int[] span = jdctx.positions.getTagSpan(tag);
1510: if (span[0] + 1 != caretOffset) {
1511: return;
1512: }
1513: }
1514: resolveInlineTag(null, jdctx);
1515: return;
1516: }
1517:
1518: if (tag != null) {
1519: insideTag(tag, jdctx);
1520: if (JavadocCompletionUtils.isBlockTag(tag)
1521: && JavadocCompletionUtils.isLineBreak(token, pos)) {
1522: resolveBlockTag(null, jdctx);
1523: }
1524: } else if (JavadocCompletionUtils.isLineBreak(token, pos)) {
1525: resolveBlockTag(null, jdctx);
1526: }
1527: }
1528:
1529: static final class JavadocContext {
1530: int anchorOffset = -1;
1531: ElementHandle<Element> handle;
1532: Doc jdoc;
1533: DocPositions positions;
1534: TokenSequence<JavadocTokenId> jdts;
1535: Document doc;
1536: CompilationInfo javac;
1537: }
1538:
1539: }
|