1 /*******************************************************************************
2 * Copyright (c) 2008, 2012 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * IBM Corporation - initial API and implementation
10 * Tom Hofmann, Google <eclipse@tom.eicher.name> - [hovering] NPE when hovering over @value reference within a type's javadoc - https://bugs.eclipse.org/bugs/show_bug.cgi?id=320084
11 *******************************************************************************/
12 package org.eclipse.jdt.internal.ui.text.javadoc;
14 import java.io.IOException;
15 import java.io.Reader;
16 import java.net.URISyntaxException;
18 import java.text.MessageFormat;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.List;
27 import org.eclipse.core.runtime.IPath;
29 import org.eclipse.core.resources.IResource;
31 import org.eclipse.jface.internal.text.html.HTMLPrinter;
33 import org.eclipse.jdt.core.IBuffer;
34 import org.eclipse.jdt.core.IField;
35 import org.eclipse.jdt.core.IJavaProject;
36 import org.eclipse.jdt.core.IMember;
37 import org.eclipse.jdt.core.IMethod;
38 import org.eclipse.jdt.core.IPackageFragmentRoot;
39 import org.eclipse.jdt.core.ISourceRange;
40 import org.eclipse.jdt.core.IType;
41 import org.eclipse.jdt.core.ITypeHierarchy;
42 import org.eclipse.jdt.core.JavaCore;
43 import org.eclipse.jdt.core.JavaModelException;
44 import org.eclipse.jdt.core.Signature;
45 import org.eclipse.jdt.core.SourceRange;
46 import org.eclipse.jdt.core.dom.ASTNode;
47 import org.eclipse.jdt.core.dom.ASTParser;
48 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
49 import org.eclipse.jdt.core.dom.CompilationUnit;
50 import org.eclipse.jdt.core.dom.IBinding;
51 import org.eclipse.jdt.core.dom.IVariableBinding;
52 import org.eclipse.jdt.core.dom.Javadoc;
53 import org.eclipse.jdt.core.dom.MemberRef;
54 import org.eclipse.jdt.core.dom.MethodRef;
55 import org.eclipse.jdt.core.dom.MethodRefParameter;
56 import org.eclipse.jdt.core.dom.Name;
57 import org.eclipse.jdt.core.dom.NodeFinder;
58 import org.eclipse.jdt.core.dom.SimpleName;
59 import org.eclipse.jdt.core.dom.TagElement;
60 import org.eclipse.jdt.core.dom.TextElement;
62 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
63 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
64 import org.eclipse.jdt.internal.corext.util.JdtFlags;
65 import org.eclipse.jdt.internal.corext.util.MethodOverrideTester;
66 import org.eclipse.jdt.internal.corext.util.SuperTypeHierarchyCache;
68 import org.eclipse.jdt.ui.JavaElementLabels;
69 import org.eclipse.jdt.ui.JavaUI;
70 import org.eclipse.jdt.ui.JavadocContentAccess;
71 import org.eclipse.jdt.ui.SharedASTProvider;
73 import org.eclipse.jdt.internal.ui.JavaPlugin;
74 import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
75 import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks;
79 * Helper to get the content of a Javadoc comment as HTML.
82 * <strong>This is work in progress. Parts of this will later become
83 * API through {@link JavadocContentAccess}</strong>
88 public class JavadocContentAccess2 {
90 private static final String BLOCK_TAG_START= "<dl>"; //$NON-NLS-1$
91 private static final String BLOCK_TAG_END= "</dl>"; //$NON-NLS-1$
93 private static final String BlOCK_TAG_ENTRY_START= "<dd>"; //$NON-NLS-1$
94 private static final String BlOCK_TAG_ENTRY_END= "</dd>"; //$NON-NLS-1$
96 private static final String PARAM_NAME_START= "<b>"; //$NON-NLS-1$
97 private static final String PARAM_NAME_END= "</b> "; //$NON-NLS-1$
100 * Implements the "Algorithm for Inheriting Method Comments" as specified for <a href=
101 * "http://download.oracle.com/javase/1.4.2/docs/tooldocs/solaris/javadoc.html#inheritingcomments"
102 * >1.4.2</a>, <a href=
103 * "http://download.oracle.com/javase/1.5.0/docs/tooldocs/windows/javadoc.html#inheritingcomments"
104 * >1.5</a>, and <a href=
105 * "http://download.oracle.com/javase/6/docs/technotes/tools/windows/javadoc.html#inheritingcomments"
109 * Unfortunately, the implementation is broken in Javadoc implementations since 1.5, see <a
110 * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6376959">Sun's bug</a>.
114 * We adhere to the spec.
117 private static abstract class InheritDocVisitor {
118 public static final Object STOP_BRANCH= new Object() {
120 public String toString() { return "STOP_BRANCH"; } //$NON-NLS-1$
122 public static final Object CONTINUE= new Object() {
124 public String toString() { return "CONTINUE"; } //$NON-NLS-1$
128 * Visits a type and decides how the visitor should proceed.
130 * @param currType the current type
132 * <li>{@link #STOP_BRANCH} to indicate that no Javadoc has been found and visiting
133 * super types should stop here</li>
134 * <li>{@link #CONTINUE} to indicate that no Javadoc has been found and visiting
135 * super types should continue</li>
136 * <li>an {@link Object} or <code>null</code>, to indicate that visiting should be
137 * cancelled immediately. The returned value is the result of
138 * {@link #visitInheritDoc(IType, ITypeHierarchy)}</li>
140 * @throws JavaModelException unexpected problem
141 * @see #visitInheritDoc(IType, ITypeHierarchy)
143 public abstract Object visit(IType currType) throws JavaModelException;
146 * Visits the super types of the given <code>currentType</code>.
148 * @param currentType the starting type
149 * @param typeHierarchy a super type hierarchy that contains <code>currentType</code>
150 * @return the result from a call to {@link #visit(IType)}, or <code>null</code> if none of
151 * the calls returned a result
152 * @throws JavaModelException unexpected problem
154 public Object visitInheritDoc(IType currentType, ITypeHierarchy typeHierarchy) throws JavaModelException {
155 ArrayList<IType> visited= new ArrayList<IType>();
156 visited.add(currentType);
157 Object result= visitInheritDocInterfaces(visited, currentType, typeHierarchy);
158 if (result != InheritDocVisitor.CONTINUE)
162 if (currentType.isInterface())
163 superClass= currentType.getJavaProject().findType("java.lang.Object"); //$NON-NLS-1$
165 superClass= typeHierarchy.getSuperclass(currentType);
167 while (superClass != null && ! visited.contains(superClass)) {
168 result= visit(superClass);
169 if (result == InheritDocVisitor.STOP_BRANCH) {
171 } else if (result == InheritDocVisitor.CONTINUE) {
172 visited.add(superClass);
173 result= visitInheritDocInterfaces(visited, superClass, typeHierarchy);
174 if (result != InheritDocVisitor.CONTINUE)
177 superClass= typeHierarchy.getSuperclass(superClass);
187 * Visits the super interfaces of the given type in the given hierarchy, thereby skipping already visited types.
189 * @param visited set of visited types
190 * @param currentType type whose super interfaces should be visited
191 * @param typeHierarchy type hierarchy (must include <code>currentType</code>)
192 * @return the result, or {@link #CONTINUE} if no result has been found
193 * @throws JavaModelException unexpected problem
195 private Object visitInheritDocInterfaces(ArrayList<IType> visited, IType currentType, ITypeHierarchy typeHierarchy) throws JavaModelException {
196 ArrayList<IType> toVisitChildren= new ArrayList<IType>();
197 IType[] superInterfaces= typeHierarchy.getSuperInterfaces(currentType);
198 for (int i= 0; i < superInterfaces.length; i++) {
199 IType superInterface= superInterfaces[i];
200 if (visited.contains(superInterface))
202 visited.add(superInterface);
203 Object result= visit(superInterface);
204 if (result == InheritDocVisitor.STOP_BRANCH) {
206 } else if (result == InheritDocVisitor.CONTINUE) {
207 toVisitChildren.add(superInterface);
212 for (Iterator<IType> iter= toVisitChildren.iterator(); iter.hasNext(); ) {
213 IType child= iter.next();
214 Object result= visitInheritDocInterfaces(visited, child, typeHierarchy);
215 if (result != InheritDocVisitor.CONTINUE)
218 return InheritDocVisitor.CONTINUE;
222 static class JavadocLookup {
223 private static final JavadocLookup NONE= new JavadocLookup(null) {
225 public CharSequence getInheritedMainDescription(IMethod method) {
229 public CharSequence getInheritedParamDescription(IMethod method, int i) {
233 public CharSequence getInheritedReturnDescription(IMethod method) {
237 public CharSequence getInheritedExceptionDescription(IMethod method, String name) {
242 private static interface DescriptionGetter {
244 * Returns a Javadoc tag description or <code>null</code>.
246 * @param contentAccess the content access
247 * @return the description, or <code>null</code> if none
248 * @throws JavaModelException unexpected problem
250 CharSequence getDescription(JavadocContentAccess2 contentAccess) throws JavaModelException;
253 private final IType fStartingType;
254 private final HashMap<IMethod, JavadocContentAccess2> fContentAccesses;
256 private ITypeHierarchy fTypeHierarchy;
257 private MethodOverrideTester fOverrideTester;
260 private JavadocLookup(IType startingType) {
261 fStartingType= startingType;
262 fContentAccesses= new HashMap<IMethod, JavadocContentAccess2>();
266 * For the given method, returns the main description from an overridden method.
268 * @param method a method
269 * @return the description that replaces the <code>{@inheritDoc}</code> tag,
270 * or <code>null</code> if none could be found
272 public CharSequence getInheritedMainDescription(IMethod method) {
273 return getInheritedDescription(method, new DescriptionGetter() {
274 public CharSequence getDescription(JavadocContentAccess2 contentAccess) {
275 return contentAccess.getMainDescription();
281 * For the given method, returns the @param tag description for the given parameter
282 * from an overridden method.
284 * @param method a method
285 * @param paramIndex the index of the parameter
286 * @return the description that replaces the <code>{@inheritDoc}</code> tag,
287 * or <code>null</code> if none could be found
289 public CharSequence getInheritedParamDescription(IMethod method, final int paramIndex) {
290 return getInheritedDescription(method, new DescriptionGetter() {
291 public CharSequence getDescription(JavadocContentAccess2 contentAccess) throws JavaModelException {
292 return contentAccess.getInheritedParamDescription(paramIndex);
298 * For the given method, returns the @return tag description from an overridden method.
300 * @param method a method
301 * @return the description that replaces the <code>{@inheritDoc}</code> tag,
302 * or <code>null</code> if none could be found
304 public CharSequence getInheritedReturnDescription(IMethod method) {
305 return getInheritedDescription(method, new DescriptionGetter() {
306 public CharSequence getDescription(JavadocContentAccess2 contentAccess) {
307 return contentAccess.getReturnDescription();
313 * For the given method, returns the @throws/@exception tag description for the given
314 * exception from an overridden method.
316 * @param method a method
317 * @param simpleName the simple name of an exception
318 * @return the description that replaces the <code>{@inheritDoc}</code> tag,
319 * or <code>null</code> if none could be found
321 public CharSequence getInheritedExceptionDescription(IMethod method, final String simpleName) {
322 return getInheritedDescription(method, new DescriptionGetter() {
323 public CharSequence getDescription(JavadocContentAccess2 contentAccess) {
324 return contentAccess.getExceptionDescription(simpleName);
329 private CharSequence getInheritedDescription(final IMethod method, final DescriptionGetter descriptionGetter) {
331 return (CharSequence) new InheritDocVisitor() {
333 public Object visit(IType currType) throws JavaModelException {
334 IMethod overridden= getOverrideTester().findOverriddenMethodInType(currType, method);
335 if (overridden == null)
336 return InheritDocVisitor.CONTINUE;
338 JavadocContentAccess2 contentAccess= getJavadocContentAccess(overridden);
339 if (contentAccess == null) {
340 if (overridden.getOpenable().getBuffer() == null) {
341 // Don't continue this branch when no source is available.
342 // We don't extract individual tags from Javadoc attachments,
343 // and it would be wrong to copy doc from further up the branch,
344 // thereby skipping doc from this overridden method.
345 return InheritDocVisitor.STOP_BRANCH;
347 return InheritDocVisitor.CONTINUE;
351 CharSequence overriddenDescription= descriptionGetter.getDescription(contentAccess);
352 if (overriddenDescription != null)
353 return overriddenDescription;
355 return InheritDocVisitor.CONTINUE;
357 }.visitInheritDoc(method.getDeclaringType(), getTypeHierarchy());
358 } catch (JavaModelException e) {
365 * @param method the method
366 * @return the Javadoc content access for the given method, or
367 * <code>null</code> if no Javadoc could be found in source
368 * @throws JavaModelException unexpected problem
370 private JavadocContentAccess2 getJavadocContentAccess(IMethod method) throws JavaModelException {
371 Object cached= fContentAccesses.get(method);
373 return (JavadocContentAccess2) cached;
374 if (fContentAccesses.containsKey(method))
377 IBuffer buf= method.getOpenable().getBuffer();
378 if (buf == null) { // no source attachment found
379 fContentAccesses.put(method, null);
383 ISourceRange javadocRange= method.getJavadocRange();
384 if (javadocRange == null) {
385 fContentAccesses.put(method, null);
389 String rawJavadoc= buf.getText(javadocRange.getOffset(), javadocRange.getLength());
390 Javadoc javadoc= getJavadocNode(method, rawJavadoc);
391 if (javadoc == null) {
392 fContentAccesses.put(method, null);
396 JavadocContentAccess2 contentAccess= new JavadocContentAccess2(method, javadoc, rawJavadoc, this);
397 return contentAccess.generated_4672312213436743719(method, JavadocLookup.this);
400 private ITypeHierarchy getTypeHierarchy() throws JavaModelException {
401 if (fTypeHierarchy == null)
402 fTypeHierarchy= SuperTypeHierarchyCache.getTypeHierarchy(fStartingType);
403 return fTypeHierarchy;
406 private MethodOverrideTester getOverrideTester() throws JavaModelException {
407 if (fOverrideTester == null)
408 fOverrideTester= SuperTypeHierarchyCache.getMethodOverrideTester(fStartingType);
409 return fOverrideTester;
412 public String generated_7350347271025408250(JavadocContentAccess2 javadoccontentaccess2, List<String> parameterNames, List<String> exceptionNames, TagElement deprecatedTag, TagElement start) {
413 List<TagElement> parameters= new ArrayList<TagElement>();
414 TagElement returnTag= null;
415 List<TagElement> exceptions= new ArrayList<TagElement>();
416 List<TagElement> versions= new ArrayList<TagElement>();
417 List<TagElement> authors= new ArrayList<TagElement>();
418 List<TagElement> sees= new ArrayList<TagElement>();
419 List<TagElement> since= new ArrayList<TagElement>();
420 List<TagElement> rest= new ArrayList<TagElement>();
422 List<TagElement> tags= javadoccontentaccess2.fJavadoc.tags();
423 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
424 TagElement tag= iter.next();
425 String tagName= tag.getTagName();
426 if (tagName == null) {
429 } else if (TagElement.TAG_PARAM.equals(tagName)) {
431 List<? extends ASTNode> fragments= tag.fragments();
432 if (fragments.size() > 0) {
433 Object first= fragments.get(0);
434 if (first instanceof SimpleName) {
435 String name= ((SimpleName) first).getIdentifier();
436 int paramIndex= parameterNames.indexOf(name);
437 if (paramIndex != -1) {
438 parameterNames.set(paramIndex, null);
443 } else if (TagElement.TAG_RETURN.equals(tagName)) {
444 if (returnTag == null)
445 returnTag= tag; // the Javadoc tool only shows the first return tag
447 } else if (TagElement.TAG_EXCEPTION.equals(tagName) || TagElement.TAG_THROWS.equals(tagName)) {
449 List<? extends ASTNode> fragments= tag.fragments();
450 if (fragments.size() > 0) {
451 Object first= fragments.get(0);
452 if (first instanceof Name) {
453 String name= ASTNodes.getSimpleNameIdentifier((Name) first);
454 int exceptionIndex= exceptionNames.indexOf(name);
455 if (exceptionIndex != -1) {
456 exceptionNames.set(exceptionIndex, null);
461 } else if (TagElement.TAG_SINCE.equals(tagName)) {
463 } else if (TagElement.TAG_VERSION.equals(tagName)) {
465 } else if (TagElement.TAG_AUTHOR.equals(tagName)) {
467 } else if (TagElement.TAG_SEE.equals(tagName)) {
469 } else if (TagElement.TAG_DEPRECATED.equals(tagName)) {
470 if (deprecatedTag == null)
471 deprecatedTag= tag; // the Javadoc tool only shows the first deprecated tag
477 //TODO: @Documented annotations before header
478 if (deprecatedTag != null)
479 javadoccontentaccess2.handleDeprecatedTag(deprecatedTag);
481 javadoccontentaccess2.handleContentElements(start.fragments());
482 else if (javadoccontentaccess2.fMethod != null) {
483 CharSequence inherited= getInheritedMainDescription(javadoccontentaccess2.fMethod);
484 // The Javadoc tool adds "Description copied from class: ..." (only for the main description).
485 // We don't bother doing that.
486 javadoccontentaccess2.handleInherited(inherited);
489 CharSequence[] parameterDescriptions= new CharSequence[parameterNames.size()];
490 boolean hasInheritedParameters= javadoccontentaccess2.inheritParameterDescriptions(parameterNames, parameterDescriptions);
491 boolean hasParameters= parameters.size() > 0 || hasInheritedParameters;
493 CharSequence returnDescription= null;
494 if (returnTag == null && javadoccontentaccess2.needsReturnTag())
495 returnDescription= getInheritedReturnDescription(javadoccontentaccess2.fMethod);
496 boolean hasReturnTag= returnTag != null || returnDescription != null;
498 CharSequence[] exceptionDescriptions= new CharSequence[exceptionNames.size()];
499 boolean hasInheritedExceptions= javadoccontentaccess2.inheritExceptionDescriptions(exceptionNames, exceptionDescriptions);
500 boolean hasExceptions= exceptions.size() > 0 || hasInheritedExceptions;
502 if (hasParameters || hasReturnTag || hasExceptions
503 || versions.size() > 0 || authors.size() > 0 || since.size() > 0 || sees.size() > 0 || rest.size() > 0
504 || (javadoccontentaccess2.fBuf.length() > 0 && (parameterDescriptions.length > 0 || exceptionDescriptions.length > 0))
506 javadoccontentaccess2.handleSuperMethodReferences();
507 javadoccontentaccess2.fBuf.append(JavadocContentAccess2.BLOCK_TAG_START);
508 javadoccontentaccess2.handleParameterTags(parameters, parameterNames, parameterDescriptions);
509 javadoccontentaccess2.handleReturnTag(returnTag, returnDescription);
510 javadoccontentaccess2.handleExceptionTags(exceptions, exceptionNames, exceptionDescriptions);
511 javadoccontentaccess2.handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_since_section, since);
512 javadoccontentaccess2.handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_version_section, versions);
513 javadoccontentaccess2.handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_author_section, authors);
514 javadoccontentaccess2.handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_see_section, sees);
515 javadoccontentaccess2.handleBlockTags(rest);
516 javadoccontentaccess2.fBuf.append(JavadocContentAccess2.BLOCK_TAG_END);
518 } else if (javadoccontentaccess2.fBuf.length() > 0) {
519 javadoccontentaccess2.handleSuperMethodReferences();
522 String result= javadoccontentaccess2.fBuf.toString();
523 javadoccontentaccess2.fBuf= null;
527 public boolean generated_8554163332790850784(JavadocContentAccess2 javadoccontentaccess2, TagElement node) {
529 if (javadoccontentaccess2.fMethod == null)
532 TagElement blockTag= (TagElement) node.getParent();
533 String blockTagName= blockTag.getTagName();
535 if (blockTagName == null) {
536 CharSequence inherited= getInheritedMainDescription(javadoccontentaccess2.fMethod);
537 return javadoccontentaccess2.handleInherited(inherited);
539 } else if (TagElement.TAG_PARAM.equals(blockTagName)) {
540 List<? extends ASTNode> fragments= blockTag.fragments();
541 if (fragments.size() > 0) {
542 Object first= fragments.get(0);
543 if (first instanceof SimpleName) {
544 String name= ((SimpleName) first).getIdentifier();
545 String[] parameterNames= javadoccontentaccess2.fMethod.getParameterNames();
546 for (int i= 0; i < parameterNames.length; i++) {
547 if (name.equals(parameterNames[i])) {
548 CharSequence inherited= getInheritedParamDescription(javadoccontentaccess2.fMethod, i);
549 return javadoccontentaccess2.handleInherited(inherited);
555 } else if (TagElement.TAG_RETURN.equals(blockTagName)) {
556 CharSequence inherited= getInheritedReturnDescription(javadoccontentaccess2.fMethod);
557 return javadoccontentaccess2.handleInherited(inherited);
559 } else if (TagElement.TAG_THROWS.equals(blockTagName) || TagElement.TAG_EXCEPTION.equals(blockTagName)) {
560 List<? extends ASTNode> fragments= blockTag.fragments();
561 if (fragments.size() > 0) {
562 Object first= fragments.get(0);
563 if (first instanceof Name) {
564 String name= ASTNodes.getSimpleNameIdentifier((Name) first);
565 CharSequence inherited= getInheritedExceptionDescription(javadoccontentaccess2.fMethod, name);
566 return javadoccontentaccess2.handleInherited(inherited);
570 } catch (JavaModelException e) {
577 private final IMember fMember;
579 * The method, or <code>null</code> if {@link #fMember} is not a method where @inheritDoc could work.
581 private final IMethod fMethod;
582 private final Javadoc fJavadoc;
583 private final String fSource;
584 private final JavadocLookup fJavadocLookup;
586 private StringBuffer fBuf;
587 private int fLiteralContent;
588 private StringBuffer fMainDescription;
589 private StringBuffer fReturnDescription;
590 private StringBuffer[] fParamDescriptions;
591 private HashMap<String, StringBuffer> fExceptionDescriptions;
593 private JavadocContentAccess2(IMethod method, Javadoc javadoc, String source, JavadocLookup lookup) {
598 fJavadocLookup= lookup;
601 private JavadocContentAccess2(IMember member, Javadoc javadoc, String source) {
606 fJavadocLookup= JavadocLookup.NONE;
610 * Gets an IMember's Javadoc comment content from the source or Javadoc attachment
611 * and renders the tags and links in HTML.
612 * Returns <code>null</code> if the member does not contain a Javadoc comment or if no source is available.
614 * @param member the member to get the Javadoc of
615 * @param useAttachedJavadoc if <code>true</code> Javadoc will be extracted from attached Javadoc
616 * if there's no source
617 * @return the Javadoc comment content in HTML or <code>null</code> if the member
618 * does not have a Javadoc comment or if no source is available
619 * @throws JavaModelException is thrown when the element's Javadoc can not be accessed
621 public static String getHTMLContent(IMember member, boolean useAttachedJavadoc) throws JavaModelException {
622 String sourceJavadoc= getHTMLContentFromSource(member);
623 if (sourceJavadoc == null || sourceJavadoc.length() == 0 || sourceJavadoc.trim().equals("{@inheritDoc}")) { //$NON-NLS-1$
624 if (useAttachedJavadoc) {
625 if (member.getOpenable().getBuffer() == null) { // only if no source available
626 return member.getAttachedJavadoc(null);
628 if (canInheritJavadoc(member)) {
629 IMethod method= (IMethod) member;
630 String attachedDocInHierarchy= findAttachedDocInHierarchy(method);
632 // Prepend "Overrides:" / "Specified by:" reference headers to make clear
633 // that description has been copied from super method.
634 if (attachedDocInHierarchy == null)
635 return sourceJavadoc;
636 StringBuffer superMethodReferences= createSuperMethodReferences(method);
637 if (superMethodReferences == null)
638 return attachedDocInHierarchy;
639 superMethodReferences.append(attachedDocInHierarchy);
640 return superMethodReferences.toString();
644 return sourceJavadoc;
647 private static StringBuffer createSuperMethodReferences(final IMethod method) throws JavaModelException {
648 IType type= method.getDeclaringType();
649 ITypeHierarchy hierarchy= SuperTypeHierarchyCache.getTypeHierarchy(type);
650 final MethodOverrideTester tester= SuperTypeHierarchyCache.getMethodOverrideTester(type);
652 final ArrayList<IMethod> superInterfaceMethods= new ArrayList<IMethod>();
653 final IMethod[] superClassMethod= { null };
654 new InheritDocVisitor() {
656 public Object visit(IType currType) throws JavaModelException {
657 IMethod overridden= tester.findOverriddenMethodInType(currType, method);
658 if (overridden == null)
659 return InheritDocVisitor.CONTINUE;
661 if (currType.isInterface())
662 superInterfaceMethods.add(overridden);
664 superClassMethod[0]= overridden;
668 }.visitInheritDoc(type, hierarchy);
670 boolean hasSuperInterfaceMethods= superInterfaceMethods.size() != 0;
671 if (!hasSuperInterfaceMethods && superClassMethod[0] == null)
674 StringBuffer buf= new StringBuffer();
675 buf.append("<div>"); //$NON-NLS-1$
676 if (hasSuperInterfaceMethods) {
677 buf.append("<b>"); //$NON-NLS-1$
678 buf.append(JavaDocMessages.JavaDoc2HTMLTextReader_specified_by_section);
679 buf.append("</b> "); //$NON-NLS-1$
680 for (Iterator<IMethod> iter= superInterfaceMethods.iterator(); iter.hasNext(); ) {
681 IMethod overridden= iter.next();
682 buf.append(createMethodInTypeLinks(overridden));
684 buf.append(JavaElementLabels.COMMA_STRING);
687 if (superClassMethod[0] != null) {
688 if (hasSuperInterfaceMethods)
689 buf.append(JavaElementLabels.COMMA_STRING);
690 buf.append("<b>"); //$NON-NLS-1$
691 buf.append(JavaDocMessages.JavaDoc2HTMLTextReader_overrides_section);
692 buf.append("</b> "); //$NON-NLS-1$
693 buf.append(createMethodInTypeLinks(superClassMethod[0]));
695 buf.append("</div>"); //$NON-NLS-1$
699 private static String createMethodInTypeLinks(IMethod overridden) {
700 CharSequence methodLink= createSimpleMemberLink(overridden);
701 CharSequence typeLink= createSimpleMemberLink(overridden.getDeclaringType());
702 String methodInType= MessageFormat.format(JavaDocMessages.JavaDoc2HTMLTextReader_method_in_type, new Object[] { methodLink, typeLink });
706 private static CharSequence createSimpleMemberLink(IMember member) {
707 StringBuffer buf= new StringBuffer();
708 buf.append("<a href='"); //$NON-NLS-1$
710 String uri= JavaElementLinks.createURI(JavaElementLinks.JAVADOC_SCHEME, member);
712 } catch (URISyntaxException e) {
715 buf.append("'>"); //$NON-NLS-1$
716 JavaElementLabels.getElementLabel(member, 0, buf);
717 buf.append("</a>"); //$NON-NLS-1$
721 private static String getHTMLContentFromSource(IMember member) throws JavaModelException {
722 IBuffer buf= member.getOpenable().getBuffer();
724 return null; // no source attachment found
727 ISourceRange javadocRange= member.getJavadocRange();
728 if (javadocRange == null) {
729 if (canInheritJavadoc(member)) {
730 // Try to use the inheritDoc algorithm. If it finds nothing (in source), return null.
731 String inheritedJavadoc= javadoc2HTML(member, "/***/"); //$NON-NLS-1$
732 return inheritedJavadoc != null && inheritedJavadoc.length() > 0 ? inheritedJavadoc : null;
738 String rawJavadoc= buf.getText(javadocRange.getOffset(), javadocRange.getLength());
739 return javadoc2HTML(member, rawJavadoc);
742 private static Javadoc getJavadocNode(IMember member, String rawJavadoc) {
743 //FIXME: take from SharedASTProvider if available
744 //Caveat: Javadoc nodes are not available when Javadoc processing has been disabled!
745 //https://bugs.eclipse.org/bugs/show_bug.cgi?id=212207
747 ASTParser parser= ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL);
749 IJavaProject javaProject= member.getJavaProject();
750 parser.setProject(javaProject);
751 Map<String, String> options= javaProject.getOptions(true);
752 options.put(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, JavaCore.ENABLED); // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=212207
753 parser.setCompilerOptions(options);
755 String source= rawJavadoc + "class C{}"; //$NON-NLS-1$
756 parser.setSource(source.toCharArray());
758 CompilationUnit root= (CompilationUnit) parser.createAST(null);
761 List<AbstractTypeDeclaration> types= root.types();
762 if (types.size() != 1)
764 AbstractTypeDeclaration type= types.get(0);
765 return type.getJavadoc();
768 private static String javadoc2HTML(IMember member, String rawJavadoc) {
769 Javadoc javadoc= getJavadocNode(member, rawJavadoc);
771 if (javadoc == null) {
772 // fall back to JavadocContentAccess:
774 Reader contentReader= JavadocContentAccess.getHTMLContentReader(member, false, false);
775 if (contentReader != null)
776 return getString(contentReader);
777 } catch (JavaModelException e) {
783 if (canInheritJavadoc(member)) {
784 IMethod method= (IMethod) member;
785 return new JavadocContentAccess2(method, javadoc, rawJavadoc, new JavadocLookup(method.getDeclaringType())).toHTML();
787 return new JavadocContentAccess2(member, javadoc, rawJavadoc).toHTML();
790 private static boolean canInheritJavadoc(IMember member) {
791 if (member instanceof IMethod && member.getJavaProject().exists()) {
793 * Exists test catches ExternalJavaProject, in which case no hierarchy can be built.
796 return ! ((IMethod) member).isConstructor();
797 } catch (JavaModelException e) {
805 * Gets the reader content as a String
807 * @param reader the reader
808 * @return the reader content as string
810 private static String getString(Reader reader) {
811 StringBuffer buf= new StringBuffer();
812 char[] buffer= new char[1024];
815 while ((count= reader.read(buffer)) != -1)
816 buf.append(buffer, 0, count);
817 } catch (IOException e) {
820 return buf.toString();
824 * Finds the first available attached Javadoc in the hierarchy of the given method.
826 * @param method the method
827 * @return the inherited Javadoc from the Javadoc attachment, or <code>null</code> if none
828 * @throws JavaModelException unexpected problem
830 private static String findAttachedDocInHierarchy(final IMethod method) throws JavaModelException {
831 IType type= method.getDeclaringType();
832 ITypeHierarchy hierarchy= SuperTypeHierarchyCache.getTypeHierarchy(type);
833 final MethodOverrideTester tester= SuperTypeHierarchyCache.getMethodOverrideTester(type);
835 return (String) new InheritDocVisitor() {
837 public Object visit(IType currType) throws JavaModelException {
838 IMethod overridden= tester.findOverriddenMethodInType(currType, method);
839 if (overridden == null)
840 return InheritDocVisitor.CONTINUE;
842 if (overridden.getOpenable().getBuffer() == null) { // only if no source available
843 //TODO: BaseURL for method can be wrong for attached Javadoc from overridden
844 // (e.g. when overridden is from rt.jar). Fix would be to add baseURL here.
845 String attachedJavadoc= overridden.getAttachedJavadoc(null);
846 if (attachedJavadoc != null)
847 return attachedJavadoc;
851 }.visitInheritDoc(type, hierarchy);
854 private String toHTML() {
855 fBuf= new StringBuffer();
858 // After first loop, non-null entries in the following two lists are missing and need to be inherited:
859 List<String> parameterNames= initParameterNames();
860 List<String> exceptionNames= initExceptionNames();
862 TagElement deprecatedTag= null;
863 TagElement start= null;
864 return fJavadocLookup.generated_7350347271025408250(this, parameterNames, exceptionNames, deprecatedTag, start);
867 private void handleDeprecatedTag(TagElement tag) {
868 fBuf.append("<p><b>"); //$NON-NLS-1$
869 fBuf.append(JavaDocMessages.JavaDoc2HTMLTextReader_deprecated_section);
870 fBuf.append("</b> <i>"); //$NON-NLS-1$
871 handleContentElements(tag.fragments());
872 fBuf.append("</i><p>"); //$NON-NLS-1$ TODO: Why not </p>? See https://bugs.eclipse.org/bugs/show_bug.cgi?id=243318 .
875 private void handleSuperMethodReferences() {
876 if (fMethod != null) {
878 StringBuffer superMethodReferences= createSuperMethodReferences(fMethod);
879 if (superMethodReferences != null)
880 fBuf.append(superMethodReferences);
881 } catch (JavaModelException e) {
887 private List<String> initParameterNames() {
888 if (fMethod != null) {
890 return new ArrayList<String>(Arrays.asList(fMethod.getParameterNames()));
891 } catch (JavaModelException e) {
895 return Collections.emptyList();
898 private List<String> initExceptionNames() {
899 if (fMethod != null) {
901 String[] exceptionTypes= fMethod.getExceptionTypes();
902 ArrayList<String> exceptionNames= new ArrayList<String>();
903 for (int i= 0; i < exceptionTypes.length; i++) {
904 exceptionNames.add(Signature.getSimpleName(Signature.toString(exceptionTypes[i])));
906 return exceptionNames;
907 } catch (JavaModelException e) {
911 return Collections.emptyList();
914 private boolean needsReturnTag() {
918 return ! Signature.SIG_VOID.equals(fMethod.getReturnType());
919 } catch (JavaModelException e) {
925 private boolean inheritParameterDescriptions(List<String> parameterNames, CharSequence[] parameterDescriptions) {
926 boolean hasInheritedParameters= false;
927 for (int i= 0; i < parameterNames.size(); i++) {
928 String name= parameterNames.get(i);
930 parameterDescriptions[i]= fJavadocLookup.getInheritedParamDescription(fMethod, i);
931 if (parameterDescriptions[i] != null)
932 hasInheritedParameters= true;
935 return hasInheritedParameters;
938 private boolean inheritExceptionDescriptions(List<String> exceptionNames, CharSequence[] exceptionDescriptions) {
939 boolean hasInheritedExceptions= false;
940 for (int i= 0; i < exceptionNames.size(); i++) {
941 String name= exceptionNames.get(i);
943 exceptionDescriptions[i]= fJavadocLookup.getInheritedExceptionDescription(fMethod, name);
944 if (exceptionDescriptions[i] != null)
945 hasInheritedExceptions= true;
948 return hasInheritedExceptions;
951 CharSequence getMainDescription() {
952 if (fMainDescription == null) {
953 fMainDescription= new StringBuffer();
954 fBuf= fMainDescription;
957 List<TagElement> tags= fJavadoc.tags();
958 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
959 TagElement tag= iter.next();
960 String tagName= tag.getTagName();
961 if (tagName == null) {
962 handleContentElements(tag.fragments());
969 return fMainDescription.length() > 0 ? fMainDescription : null;
972 CharSequence getReturnDescription() {
973 if (fReturnDescription == null) {
974 fReturnDescription= new StringBuffer();
975 fBuf= fReturnDescription;
978 List<TagElement> tags= fJavadoc.tags();
979 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
980 TagElement tag= iter.next();
981 String tagName= tag.getTagName();
982 if (TagElement.TAG_RETURN.equals(tagName)) {
983 handleContentElements(tag.fragments());
990 return fReturnDescription.length() > 0 ? fReturnDescription : null;
993 CharSequence getInheritedParamDescription(int paramIndex) throws JavaModelException {
994 if (fMethod != null) {
995 String[] parameterNames= fMethod.getParameterNames();
996 if (fParamDescriptions == null) {
997 fParamDescriptions= new StringBuffer[parameterNames.length];
999 StringBuffer description= fParamDescriptions[paramIndex];
1000 if (description != null) {
1001 return description.length() > 0 ? description : null;
1005 StringBuffer description= new StringBuffer();
1006 fParamDescriptions[paramIndex]= description;
1010 String paramName= parameterNames[paramIndex];
1011 List<TagElement> tags= fJavadoc.tags();
1012 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
1013 TagElement tag= iter.next();
1014 String tagName= tag.getTagName();
1015 if (TagElement.TAG_PARAM.equals(tagName)) {
1016 List<? extends ASTNode> fragments= tag.fragments();
1017 if (fragments.size() > 0) {
1018 Object first= fragments.get(0);
1019 if (first instanceof SimpleName) {
1020 String name= ((SimpleName) first).getIdentifier();
1021 if (name.equals(paramName)) {
1022 handleContentElements(fragments.subList(1, fragments.size()));
1031 return description.length() > 0 ? description : null;
1036 CharSequence getExceptionDescription(String simpleName) {
1037 if (fMethod != null) {
1038 if (fExceptionDescriptions == null) {
1039 fExceptionDescriptions= new HashMap<String, StringBuffer>();
1041 StringBuffer description= fExceptionDescriptions.get(simpleName);
1042 if (description != null) {
1043 return description.length() > 0 ? description : null;
1047 StringBuffer description= new StringBuffer();
1048 fExceptionDescriptions.put(simpleName, description);
1052 List<TagElement> tags= fJavadoc.tags();
1053 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
1054 TagElement tag= iter.next();
1055 String tagName= tag.getTagName();
1056 if (TagElement.TAG_THROWS.equals(tagName) || TagElement.TAG_EXCEPTION.equals(tagName)) {
1057 List<? extends ASTNode> fragments= tag.fragments();
1058 if (fragments.size() > 0) {
1059 Object first= fragments.get(0);
1060 if (first instanceof Name) {
1061 String name= ASTNodes.getSimpleNameIdentifier((Name) first);
1062 if (name.equals(simpleName)) {
1063 if (fragments.size() > 1)
1064 handleContentElements(fragments.subList(1, fragments.size()));
1073 return description.length() > 0 ? description : null;
1079 private void handleContentElements(List<? extends ASTNode> nodes) {
1080 handleContentElements(nodes, false);
1083 private void handleContentElements(List<? extends ASTNode> nodes, boolean skipLeadingWhitespace) {
1084 ASTNode previousNode= null;
1085 for (Iterator<? extends ASTNode> iter= nodes.iterator(); iter.hasNext(); ) {
1086 ASTNode child= iter.next();
1087 if (previousNode != null) {
1088 int previousEnd= previousNode.getStartPosition() + previousNode.getLength();
1089 int childStart= child.getStartPosition();
1090 if (previousEnd > childStart) {
1091 // should never happen, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=304826
1092 Exception exception= new Exception("Illegal ASTNode positions: previousEnd=" + previousEnd //$NON-NLS-1$
1093 + ", childStart=" + childStart //$NON-NLS-1$
1094 + ", member=" + fMember.getHandleIdentifier() //$NON-NLS-1$
1095 + ", Javadoc:\n" + fSource); //$NON-NLS-1$
1096 JavaPlugin.log(exception);
1097 } else if (previousEnd != childStart) {
1098 // Need to preserve whitespace before a node that's not
1099 // directly following the previous node (e.g. on a new line)
1100 // due to https://bugs.eclipse.org/bugs/show_bug.cgi?id=206518 :
1101 String textWithStars= fSource.substring(previousEnd, childStart);
1102 String text= removeDocLineIntros(textWithStars);
1106 previousNode= child;
1107 if (child instanceof TextElement) {
1108 String text= ((TextElement) child).getText();
1109 if (skipLeadingWhitespace) {
1110 text= text.replaceFirst("^\\s+", ""); //$NON-NLS-1$ //$NON-NLS-2$
1113 } else if (child instanceof TagElement) {
1114 handleInlineTagElement((TagElement) child);
1116 // This is unexpected. Fail gracefully by just copying the source.
1117 int start= child.getStartPosition();
1118 String text= fSource.substring(start, start + child.getLength());
1119 fBuf.append(removeDocLineIntros(text));
1124 private String removeDocLineIntros(String textWithStars) {
1125 String lineBreakGroup= "(\\r\\n?|\\n)"; //$NON-NLS-1$
1126 String noBreakSpace= "[^\r\n&&\\s]"; //$NON-NLS-1$
1127 return textWithStars.replaceAll(lineBreakGroup + noBreakSpace + "*\\*" /*+ noBreakSpace + '?'*/, "$1"); //$NON-NLS-1$ //$NON-NLS-2$
1130 private void handleText(String text) {
1131 if (fLiteralContent == 0) {
1134 appendEscaped(fBuf, text);
1138 private static void appendEscaped(StringBuffer buf, String text) {
1140 int length= text.length();
1141 for (int i= 0; i < length; i++) {
1142 char ch= text.charAt(i);
1146 rep= "&"; //$NON-NLS-1$
1149 rep= """; //$NON-NLS-1$
1152 rep= "<"; //$NON-NLS-1$
1155 rep= ">"; //$NON-NLS-1$
1160 buf.append(text.substring(nextToCopy, i));
1165 if (nextToCopy < length)
1166 buf.append(text.substring(nextToCopy));
1169 private void handleInlineTagElement(TagElement node) {
1170 String name= node.getTagName();
1172 if (TagElement.TAG_VALUE.equals(name) && handleValueTag(node))
1175 boolean isLink= TagElement.TAG_LINK.equals(name);
1176 boolean isLinkplain= TagElement.TAG_LINKPLAIN.equals(name);
1177 boolean isCode= TagElement.TAG_CODE.equals(name);
1178 boolean isLiteral= TagElement.TAG_LITERAL.equals(name);
1180 if (isLiteral || isCode)
1182 if (isLink || isCode)
1183 fBuf.append("<code>"); //$NON-NLS-1$
1185 if (isLink || isLinkplain)
1186 handleLink(node.fragments());
1187 else if (isCode || isLiteral)
1188 handleContentElements(node.fragments(), true);
1189 else if (handleInheritDoc(node)) {
1191 } else if (handleDocRoot(node)) {
1194 //print uninterpreted source {@tagname ...} for unknown tags
1195 int start= node.getStartPosition();
1196 String text= fSource.substring(start, start + node.getLength());
1197 fBuf.append(removeDocLineIntros(text));
1200 if (isLink || isCode)
1201 fBuf.append("</code>"); //$NON-NLS-1$
1202 if (isLiteral || isCode)
1207 private boolean handleValueTag(TagElement node) {
1209 List<? extends ASTNode> fragments= node.fragments();
1211 if (fragments.isEmpty()) {
1212 if (fMember instanceof IField && JdtFlags.isStatic(fMember) && JdtFlags.isFinal(fMember)) {
1213 IField field= (IField) fMember;
1214 return handleConstantValue(field, false);
1216 } else if (fragments.size() == 1) {
1217 Object first= fragments.get(0);
1218 if (first instanceof MemberRef) {
1219 MemberRef memberRef= (MemberRef) first;
1220 if (memberRef.getQualifier() == null) {
1221 SimpleName name= memberRef.getName();
1222 IType type= fMember instanceof IType ? (IType)fMember : fMember.getDeclaringType();
1223 while (type != null) {
1224 IField field= type.getField(name.getIdentifier());
1225 if (field != null && field.exists()) {
1226 if (JdtFlags.isStatic(field) && JdtFlags.isFinal(field))
1227 return handleConstantValue(field, true);
1230 type= type.getDeclaringType();
1235 } catch (JavaModelException e) {
1242 private boolean handleConstantValue(IField field, boolean link) throws JavaModelException {
1245 ISourceRange nameRange= field.getNameRange();
1246 if (SourceRange.isAvailable(nameRange)) {
1247 CompilationUnit cuNode= SharedASTProvider.getAST(field.getTypeRoot(), SharedASTProvider.WAIT_ACTIVE_ONLY, null);
1248 if (cuNode != null) {
1249 ASTNode nameNode= NodeFinder.perform(cuNode, nameRange);
1250 if (nameNode instanceof SimpleName) {
1251 IBinding binding= ((SimpleName) nameNode).resolveBinding();
1252 if (binding instanceof IVariableBinding) {
1253 IVariableBinding variableBinding= (IVariableBinding) binding;
1254 Object constantValue= variableBinding.getConstantValue();
1255 if (constantValue != null) {
1256 if (constantValue instanceof String) {
1257 text= ASTNodes.getEscapedStringLiteral((String) constantValue);
1259 text= constantValue.toString(); // Javadoc tool is even worse for chars...
1268 Object constant= field.getConstant();
1269 if (constant != null) {
1270 text= constant.toString();
1275 text= HTMLPrinter.convertToHTMLContentWithWhitespace(text);
1279 uri= JavaElementLinks.createURI(JavaElementLinks.JAVADOC_SCHEME, field);
1280 fBuf.append(JavaElementLinks.createLink(uri, text));
1281 } catch (URISyntaxException e) {
1293 private boolean handleDocRoot(TagElement node) {
1294 if (!TagElement.TAG_DOCROOT.equals(node.getTagName()))
1299 if (fMember.isBinary()) {
1300 URL javadocBaseLocation= JavaUI.getJavadocBaseLocation(fMember);
1301 if (javadocBaseLocation != null) {
1302 url= javadocBaseLocation.toExternalForm();
1305 IPackageFragmentRoot srcRoot= JavaModelUtil.getPackageFragmentRoot(fMember);
1306 if (srcRoot != null) {
1307 IResource resource= srcRoot.getResource();
1308 if (resource != null) {
1310 * Too bad: Browser widget knows nothing about EFS and custom URL handlers,
1311 * so IResource#getLocationURI() does not work in all cases.
1312 * We only support the local file system for now.
1313 * A solution could be https://bugs.eclipse.org/bugs/show_bug.cgi?id=149022 .
1315 IPath location= resource.getLocation();
1316 if (location != null) {
1317 url= location.toFile().toURI().toASCIIString();
1324 if (url.endsWith("/")) { //$NON-NLS-1$
1325 url= url.substring(0, url.length() -1);
1330 } catch (JavaModelException e) {
1337 * Handle {@inheritDoc}.
1339 * @param node the node
1340 * @return <code>true</code> iff the node was an {@inheritDoc} node and has been handled
1342 private boolean handleInheritDoc(TagElement node) {
1343 if (! TagElement.TAG_INHERITDOC.equals(node.getTagName()))
1345 return fJavadocLookup.generated_8554163332790850784(this, node);
1348 private boolean handleInherited(CharSequence inherited) {
1349 if (inherited == null)
1352 fBuf.append(inherited);
1356 private void handleBlockTags(String title, List<TagElement> tags) {
1360 handleBlockTagTitle(title);
1362 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
1363 TagElement tag= iter.next();
1364 fBuf.append(BlOCK_TAG_ENTRY_START);
1365 if (TagElement.TAG_SEE.equals(tag.getTagName())) {
1368 handleContentElements(tag.fragments());
1370 fBuf.append(BlOCK_TAG_ENTRY_END);
1374 private void handleReturnTag(TagElement tag, CharSequence returnDescription) {
1375 if (tag == null && returnDescription == null)
1378 handleBlockTagTitle(JavaDocMessages.JavaDoc2HTMLTextReader_returns_section);
1379 fBuf.append(BlOCK_TAG_ENTRY_START);
1381 handleContentElements(tag.fragments());
1383 fBuf.append(returnDescription);
1384 fBuf.append(BlOCK_TAG_ENTRY_END);
1387 private void handleBlockTags(List<TagElement> tags) {
1388 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
1389 TagElement tag= iter.next();
1390 handleBlockTagTitle(tag.getTagName());
1391 fBuf.append(BlOCK_TAG_ENTRY_START);
1392 handleContentElements(tag.fragments());
1393 fBuf.append(BlOCK_TAG_ENTRY_END);
1397 private void handleBlockTagTitle(String title) {
1398 fBuf.append("<dt>"); //$NON-NLS-1$
1400 fBuf.append("</dt>"); //$NON-NLS-1$
1403 private void handleSeeTag(TagElement tag) {
1404 handleLink(tag.fragments());
1407 private void handleExceptionTags(List<TagElement> tags, List<String> exceptionNames, CharSequence[] exceptionDescriptions) {
1408 if (tags.size() == 0 && containsOnlyNull(exceptionNames))
1411 handleBlockTagTitle(JavaDocMessages.JavaDoc2HTMLTextReader_throws_section);
1413 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
1414 TagElement tag= iter.next();
1415 fBuf.append(BlOCK_TAG_ENTRY_START);
1416 handleThrowsTag(tag);
1417 fBuf.append(BlOCK_TAG_ENTRY_END);
1419 for (int i= 0; i < exceptionDescriptions.length; i++) {
1420 CharSequence description= exceptionDescriptions[i];
1421 String name= exceptionNames.get(i);
1423 fBuf.append(BlOCK_TAG_ENTRY_START);
1424 handleLink(Collections.singletonList(fJavadoc.getAST().newSimpleName(name)));
1425 if (description != null) {
1426 fBuf.append(JavaElementLabels.CONCAT_STRING);
1427 fBuf.append(description);
1429 fBuf.append(BlOCK_TAG_ENTRY_END);
1434 private void handleThrowsTag(TagElement tag) {
1435 List<? extends ASTNode> fragments= tag.fragments();
1436 int size= fragments.size();
1438 handleLink(fragments.subList(0, 1));
1440 fBuf.append(JavaElementLabels.CONCAT_STRING);
1441 handleContentElements(fragments.subList(1, size));
1446 private void handleParameterTags(List<TagElement> tags, List<String> parameterNames, CharSequence[] parameterDescriptions) {
1447 if (tags.size() == 0 && containsOnlyNull(parameterNames))
1450 handleBlockTagTitle(JavaDocMessages.JavaDoc2HTMLTextReader_parameters_section);
1452 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
1453 TagElement tag= iter.next();
1454 fBuf.append(BlOCK_TAG_ENTRY_START);
1455 handleParamTag(tag);
1456 fBuf.append(BlOCK_TAG_ENTRY_END);
1458 for (int i= 0; i < parameterDescriptions.length; i++) {
1459 CharSequence description= parameterDescriptions[i];
1460 String name= parameterNames.get(i);
1462 fBuf.append(BlOCK_TAG_ENTRY_START);
1463 fBuf.append(PARAM_NAME_START);
1465 fBuf.append(PARAM_NAME_END);
1466 if (description != null)
1467 fBuf.append(description);
1468 fBuf.append(BlOCK_TAG_ENTRY_END);
1473 private void handleParamTag(TagElement tag) {
1474 List<? extends ASTNode> fragments= tag.fragments();
1476 int size= fragments.size();
1478 Object first= fragments.get(0);
1479 fBuf.append(PARAM_NAME_START);
1480 if (first instanceof SimpleName) {
1481 String name= ((SimpleName) first).getIdentifier();
1484 } else if (first instanceof TextElement) {
1485 String firstText= ((TextElement) first).getText();
1486 if ("<".equals(firstText)) { //$NON-NLS-1$
1487 fBuf.append("<"); //$NON-NLS-1$
1490 Object second= fragments.get(1);
1491 if (second instanceof SimpleName) {
1492 String name= ((SimpleName) second).getIdentifier();
1496 Object third= fragments.get(2);
1497 String thirdText= ((TextElement) third).getText();
1498 if (">".equals(thirdText)) { //$NON-NLS-1$
1499 fBuf.append(">"); //$NON-NLS-1$
1507 fBuf.append(PARAM_NAME_END);
1509 handleContentElements(fragments.subList(i, fragments.size()));
1513 private void handleLink(List<? extends ASTNode> fragments) {
1514 //TODO: Javadoc shortens type names to minimal length according to context
1515 int fs= fragments.size();
1517 Object first= fragments.get(0);
1518 String refTypeName= null;
1519 String refMemberName= null;
1520 String[] refMethodParamTypes= null;
1521 String[] refMethodParamNames= null;
1522 if (first instanceof Name) {
1523 Name name = (Name) first;
1524 refTypeName= name.getFullyQualifiedName();
1525 } else if (first instanceof MemberRef) {
1526 MemberRef memberRef= (MemberRef) first;
1527 Name qualifier= memberRef.getQualifier();
1528 refTypeName= qualifier == null ? "" : qualifier.getFullyQualifiedName(); //$NON-NLS-1$
1529 refMemberName= memberRef.getName().getIdentifier();
1530 } else if (first instanceof MethodRef) {
1531 MethodRef methodRef= (MethodRef) first;
1532 Name qualifier= methodRef.getQualifier();
1533 refTypeName= qualifier == null ? "" : qualifier.getFullyQualifiedName(); //$NON-NLS-1$
1534 refMemberName= methodRef.getName().getIdentifier();
1535 List<MethodRefParameter> params= methodRef.parameters();
1536 int ps= params.size();
1537 refMethodParamTypes= new String[ps];
1538 refMethodParamNames= new String[ps];
1539 for (int i= 0; i < ps; i++) {
1540 MethodRefParameter param= params.get(i);
1541 refMethodParamTypes[i]= ASTNodes.asString(param.getType());
1542 SimpleName paramName= param.getName();
1543 if (paramName != null)
1544 refMethodParamNames[i]= paramName.getIdentifier();
1548 if (refTypeName != null) {
1549 fBuf.append("<a href='"); //$NON-NLS-1$
1551 String scheme= JavaElementLinks.JAVADOC_SCHEME;
1552 String uri= JavaElementLinks.createURI(scheme, fMember, refTypeName, refMemberName, refMethodParamTypes);
1554 } catch (URISyntaxException e) {
1557 fBuf.append("'>"); //$NON-NLS-1$
1558 if (fs > 1 && !(fs == 2 && isWhitespaceTextElement(fragments.get(1)))) {
1559 handleContentElements(fragments.subList(1, fs), true);
1561 fBuf.append(refTypeName);
1562 if (refMemberName != null) {
1563 if (refTypeName.length() > 0) {
1566 fBuf.append(refMemberName);
1567 if (refMethodParamTypes != null) {
1569 for (int i= 0; i < refMethodParamTypes.length; i++) {
1570 String pType= refMethodParamTypes[i];
1572 String pName= refMethodParamNames[i];
1573 if (pName != null) {
1574 fBuf.append(' ').append(pName);
1576 if (i < refMethodParamTypes.length - 1) {
1577 fBuf.append(", "); //$NON-NLS-1$
1584 fBuf.append("</a>"); //$NON-NLS-1$
1586 handleContentElements(fragments);
1591 private static boolean isWhitespaceTextElement(Object fragment) {
1592 if (!(fragment instanceof TextElement))
1595 TextElement textElement= (TextElement) fragment;
1596 return textElement.getText().trim().length() == 0;
1599 private boolean containsOnlyNull(List<String> parameterNames) {
1600 for (Iterator<String> iter= parameterNames.iterator(); iter.hasNext(); ) {
1601 if (iter.next() != null)
1607 public JavadocContentAccess2 generated_4672312213436743719(IMethod method, JavadocLookup javadoclookup) {
1608 javadoclookup.fContentAccesses.put(method, this);