001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * Portions Copyrighted 2007 Sun Microsystems, Inc.
027: */
028: package org.netbeans.api.java.source.ui;
029:
030: import java.awt.event.ActionEvent;
031: import java.io.IOException;
032: import java.lang.reflect.Modifier;
033: import java.net.URI;
034: import java.net.URL;
035: import java.util.Hashtable;
036: import java.util.Iterator;
037: import java.util.Set;
038: import javax.lang.model.element.Element;
039: import javax.lang.model.element.ElementKind;
040: import javax.lang.model.element.ExecutableElement;
041: import javax.lang.model.element.TypeElement;
042: import javax.lang.model.element.VariableElement;
043: import javax.lang.model.type.ArrayType;
044: import javax.lang.model.type.DeclaredType;
045: import javax.lang.model.type.TypeMirror;
046: import javax.swing.AbstractAction;
047: import javax.swing.Action;
048: import com.sun.javadoc.*;
049: import com.sun.javadoc.AnnotationDesc.ElementValuePair;
050: import java.util.Arrays;
051: import java.util.Collections;
052: import java.util.HashSet;
053: import java.util.Locale;
054: import javax.lang.model.element.PackageElement;
055: import javax.lang.model.type.TypeKind;
056: import org.netbeans.api.java.source.ClasspathInfo;
057: import org.netbeans.api.java.source.CompilationController;
058: import org.netbeans.api.java.source.CompilationInfo;
059: import org.netbeans.api.java.source.ElementHandle;
060: import org.netbeans.api.java.source.ElementUtilities;
061: import org.netbeans.api.java.source.JavaSource;
062: import org.netbeans.api.java.source.JavaSource.Phase;
063: import org.netbeans.api.java.source.SourceUtils;
064: import org.netbeans.api.java.source.Task;
065: import org.openide.filesystems.FileObject;
066: import org.openide.util.Exceptions;
067: import org.openide.util.NbBundle;
068:
069: /** Utility class for viewing Javdoc comments as HTML.
070: *
071: * @author Dusan Balek, Petr Hrebejk
072: */
073: public class ElementJavadoc {
074:
075: private static final String API = "/api"; //NOI18N
076: private static final Set<String> LANGS = Collections
077: .<String> unmodifiableSet(new HashSet<String>(Arrays
078: .<String> asList(Locale.getISOLanguages())));
079:
080: private ElementJavadoc() {
081: }
082:
083: private ClasspathInfo cpInfo;
084: //private Doc doc;
085: private String content = null;
086: private Hashtable<String, ElementHandle<? extends Element>> links = new Hashtable<String, ElementHandle<? extends Element>>();
087: private int linkCounter = 0;
088: private URL docURL = null;
089: private AbstractAction goToSource = null;
090:
091: private static final String PARAM_TAG = "@param"; //NOI18N
092: private static final String RETURN_TAG = "@return"; //NOI18N
093: private static final String THROWS_TAG = "@throws"; //NOI18N
094: private static final String SEE_TAG = "@see"; //NOI18N
095: private static final String SINCE_TAG = "@since"; //NOI18N
096: private static final String INHERIT_DOC_TAG = "@inheritDoc"; //NOI18N
097: private static final String LINKPLAIN_TAG = "@linkplain"; //NOI18N
098: private static final String CODE_TAG = "@code"; //NOI18N
099: private static final String DEPRECATED_TAG = "@deprecated"; //NOI18N
100:
101: /** Creates an object describing the Javadoc of given element. The object
102: * is capable of getting the text formated into HTML, resolve the links,
103: * jump to external javadoc.
104: *
105: * @param compilationInfo CompilationInfo
106: * @param element Element the javadoc is required for
107: * @return ElementJavadoc describing the jaadoc
108: */
109: public static final ElementJavadoc create(
110: CompilationInfo compilationInfo, Element element) {
111: return new ElementJavadoc(compilationInfo, element, null);
112: }
113:
114: /** Gets the javadoc comment formated as HTML.
115: * @return HTML text of the javadoc
116: */
117: public String getText() {
118: return content;
119: }
120:
121: /** Gets URL of the external javadoc.
122: * @return Text of the Javadoc comment formated as HTML
123: */
124: public URL getURL() {
125: return docURL;
126: }
127:
128: /** Resolves a link contained in the Javadoc comment to a n object
129: * describing the linked javadoc
130: * @param link Link which has to be resolved
131: * @return ElementJavadoc describing the javadoc of liked element
132: */
133: public ElementJavadoc resolveLink(final String link) {
134: final ElementJavadoc[] ret = new ElementJavadoc[1];
135: try {
136: final ElementHandle<? extends Element> linkDoc = links
137: .get(link);
138: FileObject fo = linkDoc != null ? SourceUtils.getFile(
139: linkDoc, cpInfo) : null;
140: if (fo != null && fo.isFolder()
141: && linkDoc.getKind() == ElementKind.PACKAGE) {
142: fo = fo.getFileObject("package-info", "java"); //NOI18N
143: }
144: JavaSource js = fo != null ? JavaSource.forFileObject(fo)
145: : JavaSource.create(cpInfo);
146: if (js != null) {
147: js.runUserActionTask(new Task<CompilationController>() {
148: public void run(CompilationController controller)
149: throws IOException {
150: controller.toPhase(Phase.ELEMENTS_RESOLVED);
151: if (linkDoc != null) {
152: ret[0] = new ElementJavadoc(controller,
153: linkDoc.resolve(controller), null);
154: } else {
155: int idx = link.indexOf('#'); //NOI18N
156: URI uri = URI.create(idx < 0 ? link : link
157: .substring(0, idx));
158: if (uri != null) {
159: if (!uri.isAbsolute())
160: uri = uri.normalize();
161: String path = uri.toString();
162: int startIdx = path.lastIndexOf(".."); //NOI18N
163: startIdx = startIdx < 0 ? 0
164: : startIdx + 3;
165: int endIdx = path.lastIndexOf('.'); //NOI18N
166: if (endIdx >= 0)
167: path = path.substring(startIdx,
168: endIdx);
169: String clsName = path.replace('/', '.'); //NOI18N
170: Element e = controller.getElements()
171: .getTypeElement(clsName);
172: if (e != null) {
173: if (idx >= 0) {
174: String fragment = link
175: .substring(idx + 1);
176: idx = fragment.indexOf('('); //NOI18N
177: String name = idx < 0 ? fragment
178: : fragment.substring(0,
179: idx);
180: for (Element member : e
181: .getEnclosedElements()) {
182: if (member
183: .getSimpleName()
184: .contentEquals(name)
185: && fragment
186: .contentEquals(getFragment(member))) {
187: e = member;
188: break;
189: }
190: }
191: }
192: ret[0] = new ElementJavadoc(
193: controller, e, new URL(
194: docURL, link));
195: } else {
196: //external URL
197: if (uri.isAbsolute())
198: ret[0] = new ElementJavadoc(uri
199: .toURL());
200: }
201: }
202: }
203: }
204: }, true);
205: }
206: } catch (IOException ioe) {
207: Exceptions.printStackTrace(ioe);
208: }
209: return ret[0];
210: }
211:
212: /** Gets action capable of juming to source of the Element this Javadoc
213: * belongs to.
214: * @return Action going to the source of the Element described by this javadoc.
215: */
216: public Action getGotoSourceAction() {
217: return goToSource;
218: }
219:
220: private ElementJavadoc(CompilationInfo compilationInfo,
221: Element element, URL url) {
222: ElementUtilities eu = compilationInfo.getElementUtilities();
223: this .cpInfo = compilationInfo.getClasspathInfo();
224: Doc doc = eu.javaDocFor(element);
225: boolean localized = false;
226: if (element != null) {
227: docURL = SourceUtils.getJavadoc(element, cpInfo);
228: localized = isLocalized(docURL, element);
229: if (!localized) {
230: final FileObject fo = SourceUtils.getFile(element,
231: compilationInfo.getClasspathInfo());
232: if (fo != null) {
233: final ElementHandle<? extends Element> handle = ElementHandle
234: .create(element);
235: goToSource = new AbstractAction() {
236: public void actionPerformed(ActionEvent evt) {
237: ElementOpen.open(fo, handle);
238: }
239: };
240: }
241: if (url != null) {
242: docURL = url;
243: }
244: }
245: }
246: this .content = prepareContent(eu, doc, localized);
247: }
248:
249: private ElementJavadoc(URL url) {
250: assert url != null;
251: this .content = null;
252: this .docURL = url;
253: }
254:
255: // Private section ---------------------------------------------------------
256:
257: private boolean isLocalized(final URL docURL, final Element element) {
258: if (docURL == null) {
259: return false;
260: }
261: Element pkg = element;
262: while (pkg.getKind() != ElementKind.PACKAGE) {
263: pkg = pkg.getEnclosingElement();
264: if (pkg == null) {
265: return false;
266: }
267: }
268: String pkgBinName = ((PackageElement) pkg).getQualifiedName()
269: .toString();
270: String surl = docURL.toString();
271: int index = surl.lastIndexOf('/'); //NOI18N
272: if (index < 0) {
273: return false;
274: }
275: index -= (pkgBinName.length() + 1);
276: if (index < 0) {
277: return false;
278: }
279: index -= API.length();
280: if (index < 0
281: || !surl.regionMatches(index, API, 0, API.length())) {
282: return false;
283: }
284: int index2 = surl.lastIndexOf('/', index - 1); //NOI18N
285: if (index2 < 0) {
286: return false;
287: }
288: String lang = surl.substring(index2 + 1, index);
289: return LANGS.contains(lang);
290: }
291:
292: /**
293: * Creates javadoc content
294: * @param eu element utilities to find out elements
295: * @param doc javac javadoc model
296: * @param useJavadoc preffer javadoc to sources
297: * @return Javadoc content
298: */
299: private String prepareContent(ElementUtilities eu, Doc doc,
300: final boolean useJavadoc) {
301: StringBuilder sb = new StringBuilder();
302: if (doc != null) {
303: if (doc instanceof ProgramElementDoc) {
304: sb.append(getContainingClassOrPackageHeader(eu,
305: (ProgramElementDoc) doc));
306: }
307: if (doc.isMethod() || doc.isConstructor()
308: || doc.isAnnotationTypeElement()) {
309: sb
310: .append(getMethodHeader(eu,
311: (ExecutableMemberDoc) doc));
312: } else if (doc.isField() || doc.isEnumConstant()) {
313: sb.append(getFieldHeader(eu, (FieldDoc) doc));
314: } else if (doc.isClass() || doc.isInterface()
315: || doc.isAnnotationType()) {
316: sb.append(getClassHeader(eu, (ClassDoc) doc));
317: } else if (doc instanceof PackageDoc) {
318: sb.append(getPackageHeader(eu, (PackageDoc) doc));
319: }
320: sb.append("<p>"); //NOI18N
321: if (!useJavadoc
322: && (doc.commentText().length() > 0 || doc.tags().length > 0)) {
323: sb.append(getDeprecatedTag(eu, doc));
324: sb.append(inlineTags(eu, doc, doc.inlineTags()));
325: sb.append("</p><p>"); //NOI18N
326: sb.append(getTags(eu, doc));
327: } else {
328: String jdText = docURL != null ? HTMLJavadocParser
329: .getJavadocText(docURL, false) : null;
330: if (jdText != null)
331: sb.append(jdText);
332: else
333: sb.append(NbBundle.getMessage(ElementJavadoc.class,
334: "javadoc_content_not_found")); //NOI18N
335: }
336: sb.append("</p>"); //NOI18N
337: } else {
338: sb.append(NbBundle.getMessage(ElementJavadoc.class,
339: "javadoc_content_not_found")); //NOI18N
340: }
341: return sb.toString();
342: }
343:
344: private CharSequence getContainingClassOrPackageHeader(
345: ElementUtilities eu, ProgramElementDoc peDoc) {
346: StringBuilder sb = new StringBuilder();
347: ClassDoc cls = peDoc.containingClass();
348: if (cls != null) {
349: Element e = eu.elementFor(cls);
350: if (e != null) {
351: switch (e.getEnclosingElement().getKind()) {
352: case ANNOTATION_TYPE:
353: case CLASS:
354: case ENUM:
355: case INTERFACE:
356: case PACKAGE:
357: if (cls.containingClass() != null
358: || cls.containingPackage() != null) {
359: sb.append("<font size='+0'><b>"); //NOI18N
360: createLink(sb, e, makeNameLineBreakable(cls
361: .qualifiedName()));
362: sb.append("</b></font>"); //NOI18N)
363: }
364: }
365: }
366: } else {
367: PackageDoc pkg = peDoc.containingPackage();
368: if (pkg != null) {
369: sb.append("<font size='+0'><b>"); //NOI18N
370: createLink(sb, eu.elementFor(pkg),
371: makeNameLineBreakable(pkg.name()));
372: sb.append("</b></font>"); //NOI18N)
373: }
374: }
375: return sb;
376: }
377:
378: private String makeNameLineBreakable(String name) {
379: return name.replace(".", /* ZERO WIDTH SPACE */".​");
380: }
381:
382: private CharSequence getMethodHeader(ElementUtilities eu,
383: ExecutableMemberDoc mdoc) {
384: StringBuilder sb = new StringBuilder();
385: sb.append("<p><tt>"); //NOI18N
386: sb.append(getAnnotations(eu, mdoc.annotations()));
387: int len = sb.length();
388: sb.append(Modifier.toString(mdoc.modifierSpecifier()
389: & ~Modifier.NATIVE));
390: len = sb.length() - len;
391: TypeVariable[] tvars = mdoc.typeParameters();
392: if (tvars.length > 0) {
393: if (len > 0) {
394: sb.append(' '); //NOI18N
395: len++;
396: }
397: sb.append("<"); //NOI18N
398: for (int i = 0; i < tvars.length; i++) {
399: len += appendType(eu, sb, tvars[i], false, true);
400: if (i < tvars.length - 1) {
401: sb.append(","); //NOI18N
402: len++;
403: }
404: }
405: sb.append(">"); //NOI18N
406: len += 2;
407: }
408: if (!mdoc.isConstructor()) {
409: if (len > 0) {
410: sb.append(' '); //NOI18N
411: len++;
412: }
413: len += appendType(eu, sb, ((MethodDoc) mdoc).returnType(),
414: false, false);
415: }
416: String name = mdoc.name();
417: len += name.length();
418: sb.append(" <b>").append(name).append("</b>"); //NOI18N
419: if (!mdoc.isAnnotationTypeElement()) {
420: sb.append('('); //NOI18N
421: len++;
422: Parameter[] params = mdoc.parameters();
423: for (int i = 0; i < params.length; i++) {
424: boolean varArg = i == params.length - 1
425: && mdoc.isVarArgs();
426: appendType(eu, sb, params[i].type(), varArg, false);
427: sb.append(' ').append(params[i].name()); //NOI18N
428: String dim = params[i].type().dimension();
429: if (dim.length() > 0) {
430: if (varArg)
431: dim = dim.substring(2) + "..."; //NOI18N
432: }
433: if (i < params.length - 1) {
434: sb.append(",\n"); //NOI18N
435: appendSpace(sb, len);
436: }
437: }
438: sb.append(')'); //NOI18N
439: }
440: Type[] exs = mdoc.thrownExceptionTypes();
441: if (exs.length > 0) {
442: sb.append("\nthrows "); //NOI18N
443: for (int i = 0; i < exs.length; i++) {
444: appendType(eu, sb, exs[i], false, false);
445: if (i < exs.length - 1)
446: sb.append(", "); //NOI18N
447: }
448: }
449: sb.append("</tt></p>"); //NOI18N
450: return sb;
451: }
452:
453: private CharSequence getFieldHeader(ElementUtilities eu,
454: FieldDoc fdoc) {
455: StringBuilder sb = new StringBuilder();
456: sb.append("<p><tt>"); //NOI18N
457: sb.append(getAnnotations(eu, fdoc.annotations()));
458: int len = sb.length();
459: sb.append(fdoc.modifiers());
460: len = sb.length() - len;
461: if (len > 0)
462: sb.append(' '); //NOI18N
463: appendType(eu, sb, fdoc.type(), false, false);
464: sb.append(" <b>").append(fdoc.name()).append("</b>"); //NOI18N
465: sb.append("</tt></p>"); //NOI18N
466: return sb;
467: }
468:
469: private CharSequence getClassHeader(ElementUtilities eu,
470: ClassDoc cdoc) {
471: StringBuilder sb = new StringBuilder();
472: sb.append("<p><tt>"); //NOI18N
473: sb.append(getAnnotations(eu, cdoc.annotations()));
474: int mods = cdoc.modifierSpecifier() & ~Modifier.INTERFACE;
475: if (cdoc.isEnum())
476: mods &= ~Modifier.FINAL;
477: sb.append(Modifier.toString(mods));
478: if (sb.length() > 0)
479: sb.append(' '); //NOI18N
480: if (cdoc.isAnnotationType())
481: sb.append("@interface "); //NOI18N
482: else if (cdoc.isEnum())
483: sb.append("enum "); //NOI18N
484: else if (cdoc.isInterface())
485: sb.append("interface "); //NOI18N
486: else
487: sb.append("class "); //NOI18N
488: sb.append("<b>").append(cdoc.simpleTypeName()); //NOI18N
489: TypeVariable[] tvars = cdoc.typeParameters();
490: if (tvars.length > 0) {
491: sb.append("<"); //NOI18N
492: for (int i = 0; i < tvars.length; i++) {
493: appendType(eu, sb, tvars[i], false, true);
494: if (i < tvars.length - 1)
495: sb.append(","); //NOI18N
496: }
497: sb.append(">"); //NOI18N
498: }
499: sb.append("</b>"); //NOi18N
500: if (!cdoc.isAnnotationType()) {
501: if (cdoc.isClass()) {
502: Type super cls = cdoc.super classType();
503: if (super cls != null) {
504: sb.append("\nextends "); //NOI18N
505: appendType(eu, sb, super cls, false, false);
506: }
507:
508: }
509: Type[] ifaces = cdoc.interfaceTypes();
510: if (ifaces.length > 0) {
511: sb.append(cdoc.isInterface() ? "\nextends "
512: : "\nimplements "); //NOI18N
513: for (int i = 0; i < ifaces.length; i++) {
514: appendType(eu, sb, ifaces[i], false, false);
515: if (i < ifaces.length - 1)
516: sb.append(", "); //NOI18N
517: }
518: }
519: }
520: sb.append("</tt></p>"); //NOI18N
521: return sb;
522: }
523:
524: private CharSequence getPackageHeader(ElementUtilities eu,
525: PackageDoc pdoc) {
526: StringBuilder sb = new StringBuilder();
527: sb.append("<p><tt>"); //NOI18N
528: sb.append(getAnnotations(eu, pdoc.annotations()));
529: sb.append("package <b>").append(pdoc.name()).append("</b>"); //NOI18N
530: sb.append("</tt></p>"); //NOI18N
531: return sb;
532: }
533:
534: private CharSequence getAnnotations(ElementUtilities eu,
535: AnnotationDesc[] annotations) {
536: StringBuilder sb = new StringBuilder();
537: for (AnnotationDesc annotationDesc : annotations) {
538: AnnotationTypeDoc annotationType = annotationDesc
539: .annotationType();
540: if (annotationType != null) {
541: appendType(eu, sb, annotationType, false, false);
542: ElementValuePair[] pairs = annotationDesc
543: .elementValues();
544: if (pairs.length > 0) {
545: sb.append('('); //NOI18N
546: for (int i = 0; i < pairs.length; i++) {
547: AnnotationTypeElementDoc ated = pairs[i]
548: .element();
549: createLink(sb, eu.elementFor(ated), ated.name());
550: sb.append('='); //NOI18N
551: appendAnnotationValue(eu, sb, pairs[i].value());
552: if (i < pairs.length - 1)
553: sb.append(","); //NOI18N
554: }
555: sb.append(')'); //NOI18N
556: }
557: sb.append('\n'); //NOI18N
558: }
559: }
560: return sb;
561: }
562:
563: private void appendAnnotationValue(ElementUtilities eu,
564: StringBuilder sb, AnnotationValue av) {
565: Object value = av.value();
566: if (value instanceof AnnotationValue[]) {
567: int length = ((AnnotationValue[]) value).length;
568: if (length > 1)
569: sb.append('{'); //NOI18N
570: for (int i = 0; i < ((AnnotationValue[]) value).length; i++) {
571: appendAnnotationValue(eu, sb,
572: ((AnnotationValue[]) value)[i]);
573: if (i < ((AnnotationValue[]) value).length - 1)
574: sb.append(","); //NOI18N
575: }
576: if (length > 1)
577: sb.append('}'); //NOI18N
578: } else if (value instanceof Doc) {
579: createLink(sb, eu.elementFor((Doc) value), ((Doc) value)
580: .name());
581: } else {
582: sb.append(value.toString());
583: }
584: }
585:
586: private CharSequence getTags(ElementUtilities eu, Doc doc) {
587: StringBuilder see = new StringBuilder();
588: StringBuilder par = new StringBuilder();
589: StringBuilder thr = new StringBuilder();
590: StringBuilder ret = new StringBuilder();
591: String since = null;
592: for (Tag tag : doc.tags()) {
593: if (PARAM_TAG.equals(tag.kind())) {
594: par.append("<code>").append(
595: ((ParamTag) tag).parameterName()).append(
596: "</code>"); //NOI18N
597: Tag[] its = tag.inlineTags();
598: if (its.length > 0) {
599: par.append(" - "); //NOI18N
600: par.append(inlineTags(eu, doc, its));
601: }
602: par.append("<br>"); //NOI18N
603: } else if (THROWS_TAG.equals(tag.kind())) {
604: thr.append("<code>"); //NOI18N
605: Type exType = ((ThrowsTag) tag).exceptionType();
606: if (exType != null)
607: createLink(thr, eu.elementFor(exType.asClassDoc()),
608: exType.simpleTypeName());
609: else
610: thr.append(((ThrowsTag) tag).exceptionName());
611: thr.append("</code>"); //NOI18N
612: Tag[] its = tag.inlineTags();
613: if (its.length > 0) {
614: thr.append(" - "); //NOI18N
615: thr.append(inlineTags(eu, doc, its));
616: }
617: thr.append("<br>"); //NOI18N
618: } else if (RETURN_TAG.equals(tag.kind())) {
619: ret.append(inlineTags(eu, doc, tag.inlineTags()));
620: ret.append("<br>"); //NOI18N
621: } else if (SEE_TAG.equals(tag.kind())) {
622: SeeTag stag = (SeeTag) tag;
623: ClassDoc refClass = stag.referencedClass();
624: String className = stag.referencedClassName();
625: String memberName = stag.referencedMemberName();
626: String label = stag.label();
627: if (memberName != null) {
628: if (refClass != null) {
629: createLink(
630: see,
631: eu.elementFor(stag.referencedMember()),
632: "<code>"
633: + (label != null
634: && label.length() > 0 ? label
635: : (refClass
636: .simpleTypeName()
637: + "." + memberName))
638: + "</code>"); //NOI18N
639: } else {
640: see.append(className);
641: see.append('.'); //NOI18N
642: see.append(memberName);
643: }
644: see.append(", "); //NOI18N
645: } else if (className != null) {
646: if (refClass != null) {
647: createLink(
648: see,
649: eu.elementFor(refClass),
650: "<code>"
651: + (label != null
652: && label.length() > 0 ? label
653: : refClass
654: .simpleTypeName())
655: + "</code>"); //NOI18N
656: } else {
657: see.append(className);
658: }
659: see.append(", "); //NOI18N
660: } else {
661: see.append(stag.text()).append(", "); //NOI18N
662: }
663: } else if (SINCE_TAG.equals(tag.kind())) {
664: since = tag.text();
665: }
666: }
667: StringBuilder sb = new StringBuilder();
668: if (par.length() > 0) {
669: sb.append("<b>").append(
670: NbBundle.getMessage(ElementJavadoc.class,
671: "JCD-params")).append("</b><blockquote>")
672: .append(par).append("</blockquote>"); //NOI18N
673: }
674: if (ret.length() > 0) {
675: sb.append("<b>").append(
676: NbBundle.getMessage(ElementJavadoc.class,
677: "JCD-returns")).append("</b><blockquote>")
678: .append(ret).append("</blockquote>"); //NOI18N
679: }
680: if (thr.length() > 0) {
681: sb.append("<b>").append(
682: NbBundle.getMessage(ElementJavadoc.class,
683: "JCD-throws")).append("</b><blockquote>")
684: .append(thr).append("</blockquote>"); //NOI18N
685: }
686: if (since != null) {
687: sb.append("<b>").append(
688: NbBundle.getMessage(ElementJavadoc.class,
689: "JCD-since")).append("</b><blockquote>")
690: .append(since).append("</blockquote>"); //NOI18N
691: }
692: int length = see.length();
693: if (length > 0) {
694: sb.append("<b>")
695: .append(
696: NbBundle.getMessage(ElementJavadoc.class,
697: "JCD-see")).append(
698: "</b><blockquote>").append(
699: see.delete(length - 2, length)).append(
700: "</blockquote>"); //NOI18N
701: }
702: return sb;
703: }
704:
705: private CharSequence getDeprecatedTag(ElementUtilities eu, Doc doc) {
706: StringBuilder sb = new StringBuilder();
707: for (Tag tag : doc.tags()) {
708: if (DEPRECATED_TAG.equals(tag.kind()))
709: sb.append("<b>").append(
710: NbBundle.getMessage(ElementJavadoc.class,
711: "JCD-deprecated")).append("</b> <i>")
712: .append(inlineTags(eu, doc, tag.inlineTags()))
713: .append("</i></p><p>"); //NOI18N
714: }
715: return sb;
716: }
717:
718: private CharSequence inlineTags(ElementUtilities eu, Doc doc,
719: Tag[] tags) {
720: StringBuilder sb = new StringBuilder();
721: for (Tag tag : tags) {
722: if (SEE_TAG.equals(tag.kind())) {
723: SeeTag stag = (SeeTag) tag;
724: ClassDoc refClass = stag.referencedClass();
725: String memberName = stag.referencedMemberName();
726: String label = stag.label();
727: boolean plain = LINKPLAIN_TAG.equals(stag.name());
728: if (memberName != null) {
729: if (refClass != null) {
730: createLink(
731: sb,
732: eu.elementFor(stag.referencedMember()),
733: (plain ? "" : "<code>")
734: + (label != null
735: && label.length() > 0 ? label
736: : (refClass
737: .simpleTypeName()
738: + "." + memberName))
739: + (plain ? "" : "</code>")); //NOI18N
740: } else {
741: sb.append(stag.referencedClassName());
742: sb.append('.'); //NOI18N
743: sb.append(memberName);
744: }
745: } else {
746: if (refClass != null) {
747: createLink(
748: sb,
749: eu.elementFor(refClass),
750: (plain ? "" : "<code>")
751: + (label != null
752: && label.length() > 0 ? label
753: : refClass
754: .simpleTypeName())
755: + (plain ? "" : "</code>")); //NOI18N
756: } else {
757: sb.append(stag.referencedClassName());
758: }
759: }
760: } else if (INHERIT_DOC_TAG.equals(tag.kind())) {
761: if (doc.isMethod()) {
762: MethodDoc mdoc = ((MethodDoc) doc)
763: .overriddenMethod();
764: if (mdoc != null)
765: sb.append(inlineTags(eu, mdoc, mdoc
766: .inlineTags()));
767: } else if (doc.isClass() || doc.isInterface()) {
768: ClassDoc cdoc = ((ClassDoc) doc).super class();
769: if (cdoc != null)
770: sb.append(inlineTags(eu, cdoc, cdoc
771: .inlineTags()));
772: }
773: } else if (CODE_TAG.equals(tag.kind())) {
774: sb.append("<code>"); //NOI18N
775: sb.append(tag.text());
776: sb.append("</code>"); //NOI18N
777: } else {
778: sb.append(tag.text());
779: }
780: }
781: return sb;
782: }
783:
784: private CharSequence getFragment(Element e) {
785: StringBuilder sb = new StringBuilder();
786: if (!e.getKind().isClass() && !e.getKind().isInterface()) {
787: if (e.getKind() == ElementKind.CONSTRUCTOR) {
788: sb.append(e.getEnclosingElement().getSimpleName());
789: } else {
790: sb.append(e.getSimpleName());
791: }
792: if (e.getKind() == ElementKind.METHOD
793: || e.getKind() == ElementKind.CONSTRUCTOR) {
794: ExecutableElement ee = (ExecutableElement) e;
795: sb.append('('); //NOI18N
796: for (Iterator<? extends VariableElement> it = ee
797: .getParameters().iterator(); it.hasNext();) {
798: VariableElement param = it.next();
799: appendType(sb, param.asType(), ee.isVarArgs()
800: && !it.hasNext());
801: if (it.hasNext())
802: sb.append(", ");
803: }
804: sb.append(')'); //NOI18N
805: }
806: }
807: return sb;
808: }
809:
810: private void appendType(StringBuilder sb, TypeMirror type,
811: boolean varArg) {
812: switch (type.getKind()) {
813: case ARRAY:
814: appendType(sb, ((ArrayType) type).getComponentType(), false);
815: sb.append(varArg ? "..." : "[]"); //NOI18N
816: break;
817: case DECLARED:
818: sb.append(((TypeElement) ((DeclaredType) type).asElement())
819: .getQualifiedName());
820: break;
821: default:
822: sb.append(type);
823: }
824: }
825:
826: private void appendSpace(StringBuilder sb, int length) {
827: while (length-- >= 0)
828: sb.append(' '); //NOI18N
829: }
830:
831: private int appendType(ElementUtilities eu, StringBuilder sb,
832: Type type, boolean varArg, boolean typeVar) {
833: int len = 0;
834: WildcardType wt = type.asWildcardType();
835: if (wt != null) {
836: sb.append('?'); //NOI18N
837: len++;
838: Type[] bounds = wt.extendsBounds();
839: if (bounds != null && bounds.length > 0) {
840: sb.append(" extends "); //NOI18N
841: len += 9;
842: len += appendType(eu, sb, bounds[0], false, false);
843: }
844: bounds = wt.super Bounds();
845: if (bounds != null && bounds.length > 0) {
846: sb.append(" super "); //NOI18N
847: len += 7;
848: len += appendType(eu, sb, bounds[0], false, false);
849: }
850: } else {
851: TypeVariable tv = type.asTypeVariable();
852: if (tv != null) {
853: len += createLink(sb, null, tv.simpleTypeName());
854: Type[] bounds = tv.bounds();
855: if (typeVar && bounds != null && bounds.length > 0) {
856: sb.append(" extends "); //NOI18N
857: len += 9;
858: for (int i = 0; i < bounds.length; i++) {
859: len += appendType(eu, sb, bounds[i], false,
860: false);
861: if (i < bounds.length - 1) {
862: sb.append(" & "); //NOI18N
863: len += 3;
864: }
865: }
866: }
867: } else {
868: String tName = type.simpleTypeName();
869: ClassDoc cd = type.asClassDoc();
870: if (cd != null && cd.isAnnotationType())
871: tName = "@" + tName; //NOI18N
872: len += createLink(sb, eu.elementFor(type.asClassDoc()),
873: tName);
874: ParameterizedType pt = type.asParameterizedType();
875: if (pt != null) {
876: Type[] targs = pt.typeArguments();
877: if (targs.length > 0) {
878: sb.append("<"); //NOI18N
879: for (int j = 0; j < targs.length; j++) {
880: len += appendType(eu, sb, targs[j], false,
881: false);
882: if (j < targs.length - 1) {
883: sb.append(","); //NOI18N
884: len++;
885: }
886: }
887: sb.append(">"); //NOI18N
888: len += 2;
889: }
890: }
891: }
892: }
893: String dim = type.dimension();
894: if (dim.length() > 0) {
895: if (varArg)
896: dim = dim.substring(2) + "..."; //NOI18N
897: sb.append(dim);
898: len += dim.length();
899: }
900: return len;
901: }
902:
903: private int createLink(StringBuilder sb, Element e, String text) {
904: if (e != null && e.asType().getKind() != TypeKind.ERROR) {
905: String link = "*" + linkCounter++; //NOI18N
906: links.put(link, ElementHandle.create(e));
907: sb.append("<a href='").append(link).append("'>"); //NOI18N
908: }
909: sb.append(text);
910: if (e != null)
911: sb.append("</a>"); //NOI18N
912: return text.length();
913: }
914:
915: }
|