| /******************************************************************************* |
| * Copyright (c) 2008, 2022 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * This is an implementation of an early-draft specification developed under the Java |
| * Community Process (JCP) and is made available for testing and evaluation purposes |
| * only. The code is not compatible with any specification of the JCP. |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * 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 |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.ui.text.javadoc; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.net.URISyntaxException; |
| import java.net.URL; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import java.util.regex.PatternSyntaxException; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipFile; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Status; |
| |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| |
| import org.eclipse.core.filebuffers.FileBuffers; |
| import org.eclipse.core.filebuffers.ITextFileBuffer; |
| import org.eclipse.core.filebuffers.ITextFileBufferManager; |
| import org.eclipse.core.filebuffers.LocationKind; |
| |
| import org.eclipse.jface.internal.text.html.HTMLPrinter; |
| |
| import org.eclipse.jdt.core.IBuffer; |
| import org.eclipse.jdt.core.IClasspathAttribute; |
| import org.eclipse.jdt.core.IClasspathEntry; |
| import org.eclipse.jdt.core.IField; |
| import org.eclipse.jdt.core.IJarEntryResource; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.ILocalVariable; |
| import org.eclipse.jdt.core.IMember; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IOrdinaryClassFile; |
| import org.eclipse.jdt.core.IPackageDeclaration; |
| import org.eclipse.jdt.core.IPackageFragment; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.ISourceRange; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.ITypeHierarchy; |
| import org.eclipse.jdt.core.ITypeParameter; |
| import org.eclipse.jdt.core.ITypeRoot; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jdt.core.SourceRange; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.ASTParser; |
| import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.IBinding; |
| import org.eclipse.jdt.core.dom.IVariableBinding; |
| import org.eclipse.jdt.core.dom.Javadoc; |
| import org.eclipse.jdt.core.dom.MemberRef; |
| import org.eclipse.jdt.core.dom.MethodRef; |
| import org.eclipse.jdt.core.dom.MethodRefParameter; |
| import org.eclipse.jdt.core.dom.Name; |
| import org.eclipse.jdt.core.dom.NodeFinder; |
| import org.eclipse.jdt.core.dom.PackageDeclaration; |
| import org.eclipse.jdt.core.dom.SimpleName; |
| import org.eclipse.jdt.core.dom.TagElement; |
| import org.eclipse.jdt.core.dom.TagProperty; |
| import org.eclipse.jdt.core.dom.TextElement; |
| import org.eclipse.jdt.core.manipulation.SharedASTProviderCore; |
| |
| import org.eclipse.jdt.internal.corext.dom.ASTNodes; |
| import org.eclipse.jdt.internal.corext.dom.IASTSharedValues; |
| import org.eclipse.jdt.internal.corext.javadoc.JavaDocLocations; |
| import org.eclipse.jdt.internal.corext.util.JavaModelUtil; |
| import org.eclipse.jdt.internal.corext.util.JdtFlags; |
| import org.eclipse.jdt.internal.corext.util.Messages; |
| import org.eclipse.jdt.internal.corext.util.MethodOverrideTester; |
| import org.eclipse.jdt.internal.corext.util.SuperTypeHierarchyCache; |
| |
| import org.eclipse.jdt.ui.JavaElementLabels; |
| import org.eclipse.jdt.ui.JavaUI; |
| import org.eclipse.jdt.ui.JavadocContentAccess; |
| |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks; |
| |
| |
| /** |
| * Helper to get the content of a Javadoc comment as HTML. |
| * |
| * <p> |
| * <strong>This is work in progress. Parts of this will later become |
| * API through {@link JavadocContentAccess}</strong> |
| * </p> |
| * |
| * @since 3.4 |
| */ |
| public class JavadocContentAccess2 { |
| |
| private static final String BASE_URL_COMMENT_INTRO= "<!-- baseURL=\""; //$NON-NLS-1$ |
| |
| private static final String BLOCK_TAG_START= "<dl>"; //$NON-NLS-1$ |
| private static final String BLOCK_TAG_END= "</dl>"; //$NON-NLS-1$ |
| |
| public static final String BlOCK_TAG_TITLE_START= "<dt>"; //$NON-NLS-1$ |
| public static final String BlOCK_TAG_TITLE_END= "</dt>"; //$NON-NLS-1$ |
| |
| public static final String BlOCK_TAG_ENTRY_START= "<dd>"; //$NON-NLS-1$ |
| public static final String BlOCK_TAG_ENTRY_END= "</dd>"; //$NON-NLS-1$ |
| |
| private static final String PARAM_NAME_START= "<b>"; //$NON-NLS-1$ |
| private static final String PARAM_NAME_END= "</b> "; //$NON-NLS-1$ |
| |
| /** |
| * Implements the "Algorithm for Inheriting Method Comments" as specified for <a href= |
| * "http://download.oracle.com/javase/1.4.2/docs/tooldocs/solaris/javadoc.html#inheritingcomments" |
| * >1.4.2</a>, <a href= |
| * "http://download.oracle.com/javase/1.5.0/docs/tooldocs/windows/javadoc.html#inheritingcomments" |
| * >1.5</a>, and <a href= |
| * "http://download.oracle.com/javase/6/docs/technotes/tools/windows/javadoc.html#inheritingcomments" |
| * >1.6</a>. |
| * |
| * <p> |
| * Unfortunately, the implementation is broken in Javadoc implementations since 1.5, see <a |
| * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6376959">Sun's bug</a>. |
| * </p> |
| * |
| * <p> |
| * We adhere to the spec. |
| * </p> |
| */ |
| private static abstract class InheritDocVisitor { |
| public static final Object STOP_BRANCH= new Object() { |
| @Override |
| public String toString() { return "STOP_BRANCH"; } //$NON-NLS-1$ |
| }; |
| public static final Object CONTINUE= new Object() { |
| @Override |
| public String toString() { return "CONTINUE"; } //$NON-NLS-1$ |
| }; |
| |
| /** |
| * Visits a type and decides how the visitor should proceed. |
| * |
| * @param currType the current type |
| * @return <ul> |
| * <li>{@link #STOP_BRANCH} to indicate that no Javadoc has been found and visiting |
| * super types should stop here</li> |
| * <li>{@link #CONTINUE} to indicate that no Javadoc has been found and visiting |
| * super types should continue</li> |
| * <li>an {@link Object} or <code>null</code>, to indicate that visiting should be |
| * cancelled immediately. The returned value is the result of |
| * {@link #visitInheritDoc(IType, ITypeHierarchy)}</li> |
| * </ul> |
| * @throws JavaModelException unexpected problem |
| * @see #visitInheritDoc(IType, ITypeHierarchy) |
| */ |
| public abstract Object visit(IType currType) throws JavaModelException; |
| |
| /** |
| * Visits the super types of the given <code>currentType</code>. |
| * |
| * @param currentType the starting type |
| * @param typeHierarchy a super type hierarchy that contains <code>currentType</code> |
| * @return the result from a call to {@link #visit(IType)}, or <code>null</code> if none of |
| * the calls returned a result |
| * @throws JavaModelException unexpected problem |
| */ |
| public Object visitInheritDoc(IType currentType, ITypeHierarchy typeHierarchy) throws JavaModelException { |
| ArrayList<IType> visited= new ArrayList<>(); |
| visited.add(currentType); |
| Object result= visitInheritDocInterfaces(visited, currentType, typeHierarchy); |
| if (result != InheritDocVisitor.CONTINUE) |
| return result; |
| |
| IType superClass; |
| if (currentType.isInterface()) |
| superClass= currentType.getJavaProject().findType("java.lang.Object"); //$NON-NLS-1$ |
| else |
| superClass= typeHierarchy.getSuperclass(currentType); |
| |
| while (superClass != null && ! visited.contains(superClass)) { |
| result= visit(superClass); |
| if (result == InheritDocVisitor.STOP_BRANCH) { |
| return null; |
| } else if (result == InheritDocVisitor.CONTINUE) { |
| visited.add(superClass); |
| result= visitInheritDocInterfaces(visited, superClass, typeHierarchy); |
| if (result != InheritDocVisitor.CONTINUE) |
| return result; |
| else |
| superClass= typeHierarchy.getSuperclass(superClass); |
| } else { |
| return result; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Visits the super interfaces of the given type in the given hierarchy, thereby skipping already visited types. |
| * |
| * @param visited set of visited types |
| * @param currentType type whose super interfaces should be visited |
| * @param typeHierarchy type hierarchy (must include <code>currentType</code>) |
| * @return the result, or {@link #CONTINUE} if no result has been found |
| * @throws JavaModelException unexpected problem |
| */ |
| private Object visitInheritDocInterfaces(ArrayList<IType> visited, IType currentType, ITypeHierarchy typeHierarchy) throws JavaModelException { |
| ArrayList<IType> toVisitChildren= new ArrayList<>(); |
| for (IType superInterface : typeHierarchy.getSuperInterfaces(currentType)) { |
| if (visited.contains(superInterface)) |
| continue; |
| visited.add(superInterface); |
| Object result= visit(superInterface); |
| if (result == InheritDocVisitor.STOP_BRANCH) { |
| //skip |
| } else if (result == InheritDocVisitor.CONTINUE) { |
| toVisitChildren.add(superInterface); |
| } else { |
| return result; |
| } |
| } |
| for (IType child : toVisitChildren) { |
| Object result= visitInheritDocInterfaces(visited, child, typeHierarchy); |
| if (result != InheritDocVisitor.CONTINUE) |
| return result; |
| } |
| return InheritDocVisitor.CONTINUE; |
| } |
| } |
| |
| private static class JavadocLookup { |
| private static final JavadocLookup NONE= new JavadocLookup(null) { |
| @Override |
| public CharSequence getInheritedMainDescription(IMethod method) { |
| return null; |
| } |
| @Override |
| public CharSequence getInheritedParamDescription(IMethod method, int i) { |
| return null; |
| } |
| @Override |
| public CharSequence getInheritedReturnDescription(IMethod method) { |
| return null; |
| } |
| @Override |
| public CharSequence getInheritedExceptionDescription(IMethod method, String name) { |
| return null; |
| } |
| }; |
| |
| private interface DescriptionGetter { |
| /** |
| * Returns a Javadoc tag description or <code>null</code>. |
| * |
| * @param contentAccess the content access |
| * @return the description, or <code>null</code> if none |
| * @throws JavaModelException unexpected problem |
| */ |
| CharSequence getDescription(JavadocContentAccess2 contentAccess) throws JavaModelException; |
| } |
| |
| private final IType fStartingType; |
| private final HashMap<IMethod, JavadocContentAccess2> fContentAccesses; |
| |
| private ITypeHierarchy fTypeHierarchy; |
| private MethodOverrideTester fOverrideTester; |
| |
| |
| private JavadocLookup(IType startingType) { |
| fStartingType= startingType; |
| fContentAccesses= new HashMap<>(); |
| } |
| |
| /** |
| * For the given method, returns the main description from an overridden method. |
| * |
| * @param method a method |
| * @return the description that replaces the <code>{@inheritDoc}</code> tag, |
| * or <code>null</code> if none could be found |
| */ |
| public CharSequence getInheritedMainDescription(IMethod method) { |
| return getInheritedDescription(method, JavadocContentAccess2::getMainDescription); |
| } |
| |
| /** |
| * For the given method, returns the @param tag description for the given type parameter |
| * from an overridden method. |
| * |
| * @param method a method |
| * @param typeParamIndex the index of the type parameter |
| * @return the description that replaces the <code>{@inheritDoc}</code> tag, or |
| * <code>null</code> if none could be found |
| */ |
| public CharSequence getInheritedTypeParamDescription(IMethod method, final int typeParamIndex) { |
| return getInheritedDescription(method, contentAccess -> contentAccess.getInheritedTypeParamDescription(typeParamIndex)); |
| } |
| |
| /** |
| * For the given method, returns the @param tag description for the given parameter from an |
| * overridden method. |
| * |
| * @param method a method |
| * @param paramIndex the index of the parameter |
| * @return the description that replaces the <code>{@inheritDoc}</code> tag, |
| * or <code>null</code> if none could be found |
| */ |
| public CharSequence getInheritedParamDescription(IMethod method, final int paramIndex) { |
| return getInheritedDescription(method, contentAccess -> contentAccess.getInheritedParamDescription(paramIndex)); |
| } |
| |
| /** |
| * For the given method, returns the @return tag description from an overridden method. |
| * |
| * @param method a method |
| * @return the description that replaces the <code>{@inheritDoc}</code> tag, |
| * or <code>null</code> if none could be found |
| */ |
| public CharSequence getInheritedReturnDescription(IMethod method) { |
| return getInheritedDescription(method, JavadocContentAccess2::getReturnDescription); |
| } |
| |
| /** |
| * For the given method, returns the @throws/@exception tag description for the given |
| * exception from an overridden method. |
| * |
| * @param method a method |
| * @param simpleName the simple name of an exception |
| * @return the description that replaces the <code>{@inheritDoc}</code> tag, |
| * or <code>null</code> if none could be found |
| */ |
| public CharSequence getInheritedExceptionDescription(IMethod method, final String simpleName) { |
| return getInheritedDescription(method, contentAccess -> contentAccess.getExceptionDescription(simpleName)); |
| } |
| |
| private CharSequence getInheritedDescription(final IMethod method, final DescriptionGetter descriptionGetter) { |
| try { |
| return (CharSequence) new InheritDocVisitor() { |
| @Override |
| public Object visit(IType currType) throws JavaModelException { |
| IMethod overridden= getOverrideTester().findOverriddenMethodInType(currType, method); |
| if (overridden == null) |
| return InheritDocVisitor.CONTINUE; |
| |
| JavadocContentAccess2 contentAccess= getJavadocContentAccess(overridden); |
| if (contentAccess == null) { |
| if (overridden.getOpenable().getBuffer() == null) { |
| // Don't continue this branch when no source is available. |
| // We don't extract individual tags from Javadoc attachments, |
| // and it would be wrong to copy doc from further up the branch, |
| // thereby skipping doc from this overridden method. |
| return InheritDocVisitor.STOP_BRANCH; |
| } else { |
| return InheritDocVisitor.CONTINUE; |
| } |
| } |
| |
| CharSequence overriddenDescription= descriptionGetter.getDescription(contentAccess); |
| if (overriddenDescription != null) |
| return overriddenDescription; |
| else |
| return InheritDocVisitor.CONTINUE; |
| } |
| }.visitInheritDoc(method.getDeclaringType(), getTypeHierarchy()); |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| } |
| return null; |
| } |
| |
| /** |
| * @param method the method |
| * @return the Javadoc content access for the given method, or |
| * <code>null</code> if no Javadoc could be found in source |
| * @throws JavaModelException unexpected problem |
| */ |
| private JavadocContentAccess2 getJavadocContentAccess(IMethod method) throws JavaModelException { |
| Object cached= fContentAccesses.get(method); |
| if (cached != null) |
| return (JavadocContentAccess2) cached; |
| if (fContentAccesses.containsKey(method)) |
| return null; |
| |
| IBuffer buf= method.getOpenable().getBuffer(); |
| if (buf == null) { // no source attachment found |
| fContentAccesses.put(method, null); |
| return null; |
| } |
| |
| ISourceRange javadocRange= method.getJavadocRange(); |
| if (javadocRange == null) { |
| fContentAccesses.put(method, null); |
| return null; |
| } |
| |
| String rawJavadoc= buf.getText(javadocRange.getOffset(), javadocRange.getLength()); |
| Javadoc javadoc= getJavadocNode(method, rawJavadoc); |
| if (javadoc == null) { |
| fContentAccesses.put(method, null); |
| return null; |
| } |
| |
| JavadocContentAccess2 contentAccess= new JavadocContentAccess2(method, javadoc, rawJavadoc, this); |
| fContentAccesses.put(method, contentAccess); |
| return contentAccess; |
| } |
| |
| private ITypeHierarchy getTypeHierarchy() throws JavaModelException { |
| if (fTypeHierarchy == null) |
| fTypeHierarchy= SuperTypeHierarchyCache.getTypeHierarchy(fStartingType); |
| return fTypeHierarchy; |
| } |
| |
| private MethodOverrideTester getOverrideTester() throws JavaModelException { |
| if (fOverrideTester == null) |
| fOverrideTester= SuperTypeHierarchyCache.getMethodOverrideTester(fStartingType); |
| return fOverrideTester; |
| } |
| } |
| |
| /** |
| * Either an IMember or an IPackageFragment. |
| */ |
| private final IJavaElement fElement; |
| |
| /** |
| * The method, or <code>null</code> if {@link #fElement} is not a method where @inheritDoc could |
| * work. |
| */ |
| private final IMethod fMethod; |
| private final Javadoc fJavadoc; |
| private final String fSource; |
| private final JavadocLookup fJavadocLookup; |
| |
| private StringBuffer fBuf; |
| private int fLiteralContent; |
| private StringBuffer fMainDescription; |
| private StringBuffer fReturnDescription; |
| private StringBuffer[] fTypeParamDescriptions; |
| private StringBuffer[] fParamDescriptions; |
| private HashMap<String, StringBuffer> fExceptionDescriptions; |
| |
| private JavadocContentAccess2(IJavaElement element, Javadoc javadoc, String source, JavadocLookup lookup) { |
| Assert.isNotNull(element); |
| Assert.isTrue(element instanceof IMethod || element instanceof ILocalVariable || element instanceof ITypeParameter); |
| fElement= element; |
| fMethod= (IMethod) ((element instanceof ILocalVariable || element instanceof ITypeParameter) ? element.getParent() : element); |
| fJavadoc= javadoc; |
| fSource= source; |
| fJavadocLookup= lookup; |
| } |
| |
| private JavadocContentAccess2(IJavaElement element, Javadoc javadoc, String source) { |
| Assert.isTrue(element instanceof IMember || element instanceof IPackageFragment || element instanceof ILocalVariable || element instanceof ITypeParameter); |
| fElement= element; |
| fMethod= null; |
| fJavadoc= javadoc; |
| fSource= source; |
| fJavadocLookup= JavadocLookup.NONE; |
| } |
| |
| /** |
| * Gets an IJavaElement's Javadoc comment content from the source or Javadoc attachment |
| * and renders the tags and links in HTML. |
| * Returns <code>null</code> if the element does not have a Javadoc comment or if no source is available. |
| * |
| * @param element the element to get the Javadoc of |
| * @param useAttachedJavadoc if <code>true</code> Javadoc will be extracted from attached Javadoc |
| * if there's no source |
| * @return the Javadoc comment content in HTML or <code>null</code> if the element |
| * does not have a Javadoc comment or if no source is available |
| * @throws CoreException is thrown when the element's Javadoc cannot be accessed |
| */ |
| public static String getHTMLContent(IJavaElement element, boolean useAttachedJavadoc) throws CoreException { |
| if (element instanceof IPackageFragment) { |
| return getHTMLContent((IPackageFragment) element); |
| } |
| if (element instanceof IPackageDeclaration) { |
| return getHTMLContent((IPackageDeclaration) element); |
| } |
| if (!(element instanceof IMember) |
| && !(element instanceof ITypeParameter) |
| && (!(element instanceof ILocalVariable) || !(((ILocalVariable) element).isParameter()))) { |
| return null; |
| } |
| String sourceJavadoc= getHTMLContentFromSource(element); |
| if (sourceJavadoc == null || sourceJavadoc.length() == 0 || "{@inheritDoc}".equals(sourceJavadoc.trim())) { //$NON-NLS-1$ |
| if (useAttachedJavadoc) { |
| if (element.getOpenable().getBuffer() == null) { // only if no source available |
| return element.getAttachedJavadoc(null); |
| } |
| IMember member= null; |
| if (element instanceof ILocalVariable) { |
| member= ((ILocalVariable) element).getDeclaringMember(); |
| } else if (element instanceof ITypeParameter) { |
| member= ((ITypeParameter) element).getDeclaringMember(); |
| } else if (element instanceof IMember) { |
| member= (IMember) element; |
| } |
| if (canInheritJavadoc(member)) { |
| IMethod method= (IMethod) member; |
| String attachedDocInHierarchy= findAttachedDocInHierarchy(method); |
| |
| // Prepend "Overrides:" / "Specified by:" reference headers to make clear |
| // that description has been copied from super method. |
| if (attachedDocInHierarchy == null) |
| return sourceJavadoc; |
| StringBuffer superMethodReferences= createSuperMethodReferences(method); |
| if (superMethodReferences == null) |
| return attachedDocInHierarchy; |
| superMethodReferences.append(attachedDocInHierarchy); |
| return superMethodReferences.toString(); |
| } |
| } |
| } |
| return sourceJavadoc; |
| } |
| |
| private static StringBuffer createSuperMethodReferences(final IMethod method) throws JavaModelException { |
| IType type= method.getDeclaringType(); |
| ITypeHierarchy hierarchy= SuperTypeHierarchyCache.getTypeHierarchy(type); |
| final MethodOverrideTester tester= SuperTypeHierarchyCache.getMethodOverrideTester(type); |
| |
| final ArrayList<IMethod> superInterfaceMethods= new ArrayList<>(); |
| final IMethod[] superClassMethod= { null }; |
| new InheritDocVisitor() { |
| @Override |
| public Object visit(IType currType) throws JavaModelException { |
| IMethod overridden= tester.findOverriddenMethodInType(currType, method); |
| if (overridden == null) |
| return InheritDocVisitor.CONTINUE; |
| |
| if (currType.isInterface()) |
| superInterfaceMethods.add(overridden); |
| else |
| superClassMethod[0]= overridden; |
| |
| return STOP_BRANCH; |
| } |
| }.visitInheritDoc(type, hierarchy); |
| |
| boolean hasSuperInterfaceMethods= !superInterfaceMethods.isEmpty(); |
| if (!hasSuperInterfaceMethods && superClassMethod[0] == null) |
| return null; |
| |
| StringBuffer buf= new StringBuffer(); |
| buf.append("<div>"); //$NON-NLS-1$ |
| if (hasSuperInterfaceMethods) { |
| buf.append("<b>"); //$NON-NLS-1$ |
| buf.append(JavaDocMessages.JavaDoc2HTMLTextReader_specified_by_section); |
| buf.append("</b> "); //$NON-NLS-1$ |
| for (Iterator<IMethod> iter= superInterfaceMethods.iterator(); iter.hasNext(); ) { |
| IMethod overridden= iter.next(); |
| buf.append(createMethodInTypeLinks(overridden)); |
| if (iter.hasNext()) |
| buf.append(JavaElementLabels.COMMA_STRING); |
| } |
| } |
| if (superClassMethod[0] != null) { |
| if (hasSuperInterfaceMethods) |
| buf.append(JavaElementLabels.COMMA_STRING); |
| buf.append("<b>"); //$NON-NLS-1$ |
| buf.append(JavaDocMessages.JavaDoc2HTMLTextReader_overrides_section); |
| buf.append("</b> "); //$NON-NLS-1$ |
| buf.append(createMethodInTypeLinks(superClassMethod[0])); |
| } |
| buf.append("</div>"); //$NON-NLS-1$ |
| return buf; |
| } |
| |
| private static String createMethodInTypeLinks(IMethod overridden) { |
| CharSequence methodLink= createSimpleMemberLink(overridden); |
| CharSequence typeLink= createSimpleMemberLink(overridden.getDeclaringType()); |
| String methodInType= MessageFormat.format(JavaDocMessages.JavaDoc2HTMLTextReader_method_in_type, methodLink, typeLink); |
| return methodInType; |
| } |
| |
| private static CharSequence createSimpleMemberLink(IMember member) { |
| StringBuffer buf= new StringBuffer(); |
| buf.append("<a href='"); //$NON-NLS-1$ |
| try { |
| String uri= JavaElementLinks.createURI(JavaElementLinks.JAVADOC_SCHEME, member); |
| buf.append(uri); |
| } catch (URISyntaxException e) { |
| JavaPlugin.log(e); |
| } |
| buf.append("'>"); //$NON-NLS-1$ |
| JavaElementLabels.getElementLabel(member, 0, buf); |
| buf.append("</a>"); //$NON-NLS-1$ |
| return buf; |
| } |
| |
| private static String getHTMLContentFromSource(IJavaElement element) throws JavaModelException { |
| IMember member; |
| if (element instanceof ILocalVariable) { |
| member= ((ILocalVariable) element).getDeclaringMember(); |
| } else if (element instanceof ITypeParameter) { |
| member= ((ITypeParameter) element).getDeclaringMember(); |
| } else if (element instanceof IMember) { |
| member= (IMember) element; |
| } else { |
| return null; |
| } |
| |
| IBuffer buf= member.getOpenable().getBuffer(); |
| if (buf == null) { |
| return null; // no source attachment found |
| } |
| |
| ISourceRange javadocRange= member.getJavadocRange(); |
| if (javadocRange == null) { |
| if (canInheritJavadoc(member)) { |
| // Try to use the inheritDoc algorithm. |
| String inheritedJavadoc= javadoc2HTML(member, element, "/***/"); //$NON-NLS-1$ |
| if (inheritedJavadoc != null && inheritedJavadoc.length() > 0) { |
| return inheritedJavadoc; |
| } |
| } |
| return getJavaFxPropertyDoc(member); |
| } |
| |
| String rawJavadoc= buf.getText(javadocRange.getOffset(), javadocRange.getLength()); |
| return javadoc2HTML(member, element, rawJavadoc); |
| } |
| |
| private static String getJavaFxPropertyDoc(IMember member) throws JavaModelException { |
| // XXX: should not do this by default (but we don't have settings for Javadoc, see https://bugs.eclipse.org/424283 ) |
| if (member instanceof IMethod) { |
| String name= member.getElementName(); |
| boolean isGetter= name.startsWith("get") && name.length() > 3; //$NON-NLS-1$ |
| boolean isBooleanGetter= name.startsWith("is") && name.length() > 2; //$NON-NLS-1$ |
| boolean isSetter= name.startsWith("set") && name.length() > 3; //$NON-NLS-1$ |
| |
| if (isGetter || isBooleanGetter || isSetter) { |
| String propertyName= firstToLower(name.substring(isBooleanGetter ? 2 : 3)); |
| IType type= member.getDeclaringType(); |
| IMethod method= type.getMethod(propertyName + "Property", new String[0]); //$NON-NLS-1$ |
| |
| if (method.exists()) { |
| String content= getHTMLContentFromSource(method); |
| if (content != null) { |
| if (isSetter) { |
| content= Messages.format(JavaDocMessages.JavadocContentAccess2_setproperty_message, new Object[] { propertyName, content }); |
| } else { |
| content= Messages.format(JavaDocMessages.JavadocContentAccess2_getproperty_message, new Object[] { propertyName, content }); |
| } |
| } |
| return content; |
| } |
| } else if (name.endsWith("Property")) { //$NON-NLS-1$ |
| String propertyName= name.substring(0, name.length() - 8); |
| |
| IType type= member.getDeclaringType(); |
| IField field= type.getField(propertyName); |
| if (field.exists()) { |
| return getHTMLContentFromSource(field); |
| } |
| } |
| } |
| return null; |
| } |
| |
| private static String firstToLower(String propertyName) { |
| char[] c = propertyName.toCharArray(); |
| c[0] = Character.toLowerCase(c[0]); |
| return String.valueOf(c); |
| } |
| |
| private static Javadoc getJavadocNode(IJavaElement element, String rawJavadoc) { |
| //FIXME: take from SharedASTProvider if available |
| //Caveat: Javadoc nodes are not available when Javadoc processing has been disabled! |
| //https://bugs.eclipse.org/bugs/show_bug.cgi?id=212207 |
| |
| String source= rawJavadoc + "class C{}"; //$NON-NLS-1$ |
| CompilationUnit root= createAST(element, source); |
| if (root == null) |
| return null; |
| List<AbstractTypeDeclaration> types= root.types(); |
| if (types.size() != 1) |
| return null; |
| AbstractTypeDeclaration type= types.get(0); |
| return type.getJavadoc(); |
| } |
| |
| private static Javadoc getPackageJavadocNode(IJavaElement element, String cuSource) { |
| CompilationUnit cu= createAST(element, cuSource); |
| if (cu != null) { |
| PackageDeclaration packDecl= cu.getPackage(); |
| if (packDecl != null) { |
| return packDecl.getJavadoc(); |
| } |
| } |
| return null; |
| } |
| |
| private static CompilationUnit createAST(IJavaElement element, String cuSource) { |
| Assert.isNotNull(element); |
| ASTParser parser= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL); |
| |
| IJavaProject javaProject= element.getJavaProject(); |
| parser.setProject(javaProject); |
| Map<String, String> options= javaProject.getOptions(true); |
| options.put(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, JavaCore.ENABLED); // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=212207 |
| parser.setCompilerOptions(options); |
| |
| parser.setSource(cuSource.toCharArray()); |
| return (CompilationUnit) parser.createAST(null); |
| } |
| |
| private static String javadoc2HTML(IMember member, IJavaElement element, String rawJavadoc) { |
| Javadoc javadoc= getJavadocNode(member, rawJavadoc); |
| |
| if (javadoc == null) { |
| Reader contentReader= null; |
| // fall back to JavadocContentAccess: |
| try { |
| contentReader= JavadocContentAccess.getHTMLContentReader(member, false, false); |
| if (contentReader != null) |
| return getString(contentReader); |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| } finally { |
| if (contentReader != null) { |
| try { |
| contentReader.close(); |
| } catch (IOException e) { |
| //ignore |
| } |
| } |
| } |
| return null; |
| } |
| |
| if (canInheritJavadoc(member)) { |
| IMethod method= (IMethod) member; |
| return new JavadocContentAccess2(element, javadoc, rawJavadoc, new JavadocLookup(method.getDeclaringType())).toHTML(); |
| } |
| return new JavadocContentAccess2(element, javadoc, rawJavadoc).toHTML(); |
| } |
| |
| private static boolean canInheritJavadoc(IMember member) { |
| if (member instanceof IMethod && member.getJavaProject().exists()) { |
| /* |
| * Exists test catches ExternalJavaProject, in which case no hierarchy can be built. |
| */ |
| try { |
| return ! ((IMethod) member).isConstructor(); |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Gets the reader content as a String |
| * |
| * @param reader the reader |
| * @return the reader content as string |
| */ |
| private static String getString(Reader reader) { |
| StringBuilder buf= new StringBuilder(); |
| char[] buffer= new char[1024]; |
| int count; |
| try { |
| while ((count= reader.read(buffer)) != -1) |
| buf.append(buffer, 0, count); |
| } catch (IOException e) { |
| return null; |
| } |
| return buf.toString(); |
| } |
| |
| /** |
| * Finds the first available attached Javadoc in the hierarchy of the given method. |
| * |
| * @param method the method |
| * @return the inherited Javadoc from the Javadoc attachment, or <code>null</code> if none |
| * @throws JavaModelException unexpected problem |
| */ |
| private static String findAttachedDocInHierarchy(final IMethod method) throws JavaModelException { |
| IType type= method.getDeclaringType(); |
| ITypeHierarchy hierarchy= SuperTypeHierarchyCache.getTypeHierarchy(type); |
| final MethodOverrideTester tester= SuperTypeHierarchyCache.getMethodOverrideTester(type); |
| |
| return (String) new InheritDocVisitor() { |
| @Override |
| public Object visit(IType currType) throws JavaModelException { |
| IMethod overridden= tester.findOverriddenMethodInType(currType, method); |
| if (overridden == null) |
| return InheritDocVisitor.CONTINUE; |
| |
| if (overridden.getOpenable().getBuffer() == null) { // only if no source available |
| String attachedJavadoc= overridden.getAttachedJavadoc(null); |
| if (attachedJavadoc != null) { |
| // BaseURL for the original method can be wrong for attached Javadoc from overridden |
| // (e.g. when overridden is from rt.jar). |
| // Fix is to store the baseURL inside the doc content and later fetch it with #extractBaseURL(String). |
| String baseURL= JavaDocLocations.getBaseURL(overridden, overridden.isBinary()); |
| if (baseURL != null) { |
| attachedJavadoc= BASE_URL_COMMENT_INTRO + baseURL + "\"--> " + attachedJavadoc; //$NON-NLS-1$ |
| } |
| return attachedJavadoc; |
| } |
| } |
| return CONTINUE; |
| } |
| }.visitInheritDoc(type, hierarchy); |
| } |
| |
| private String toHTML() { |
| fBuf= new StringBuffer(); |
| fLiteralContent= 0; |
| |
| if (fElement instanceof ILocalVariable || fElement instanceof ITypeParameter) { |
| parameterToHTML(); |
| } else { |
| elementToHTML(); |
| } |
| |
| String result= fBuf.toString(); |
| fBuf= null; |
| return result; |
| } |
| |
| private void parameterToHTML() { |
| String elementName= fElement.getElementName(); |
| List<TagElement> tags= fJavadoc.tags(); |
| for (TagElement tag : tags) { |
| String tagName= tag.getTagName(); |
| if (TagElement.TAG_PARAM.equals(tagName)) { |
| List<? extends ASTNode> fragments= tag.fragments(); |
| int size= fragments.size(); |
| if (size > 0) { |
| Object first= fragments.get(0); |
| if (first instanceof SimpleName) { |
| String name= ((SimpleName) first).getIdentifier(); |
| if (elementName.equals(name)) { |
| handleContentElements(fragments.subList(1, size)); |
| return; |
| } |
| } else if (size > 2 && fElement instanceof ITypeParameter && first instanceof TextElement) { |
| String firstText= ((TextElement) first).getText(); |
| if ("<".equals(firstText)) { //$NON-NLS-1$ |
| Object second= fragments.get(1); |
| Object third= fragments.get(2); |
| if (second instanceof SimpleName && third instanceof TextElement) { |
| String name= ((SimpleName) second).getIdentifier(); |
| String thirdText= ((TextElement) third).getText(); |
| if (elementName.equals(name) && ">".equals(thirdText)) { //$NON-NLS-1$ |
| handleContentElements(fragments.subList(3, size)); |
| return; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| if (fElement instanceof ILocalVariable) { |
| List<String> parameterNames= initParameterNames(); |
| int i= parameterNames.indexOf(elementName); |
| if (i != -1) { |
| CharSequence inheritedParamDescription= fJavadocLookup.getInheritedParamDescription(fMethod, i); |
| handleInherited(inheritedParamDescription); |
| } |
| } else if (fElement instanceof ITypeParameter) { |
| List<String> typeParameterNames= initTypeParameterNames(); |
| int i= typeParameterNames.indexOf(elementName); |
| if (i != -1) { |
| CharSequence inheritedTypeParamDescription= fJavadocLookup.getInheritedTypeParamDescription(fMethod, i); |
| handleInherited(inheritedTypeParamDescription); |
| } |
| } |
| } |
| |
| private void elementToHTML() { |
| // After first loop, non-null entries in the following two lists are missing and need to be inherited: |
| List<String> typeParameterNames= initTypeParameterNames(); |
| List<String> parameterNames= initParameterNames(); |
| List<String> exceptionNames= initExceptionNames(); |
| |
| TagElement deprecatedTag= null; |
| TagElement start= null; |
| List<TagElement> typeParameters= new ArrayList<>(); |
| List<TagElement> parameters= new ArrayList<>(); |
| TagElement returnTag= null; |
| List<TagElement> exceptions= new ArrayList<>(); |
| List<TagElement> provides= new ArrayList<>(); |
| List<TagElement> uses= new ArrayList<>(); |
| List<TagElement> versions= new ArrayList<>(); |
| List<TagElement> authors= new ArrayList<>(); |
| List<TagElement> sees= new ArrayList<>(); |
| List<TagElement> since= new ArrayList<>(); |
| List<TagElement> rest= new ArrayList<>(); |
| List<TagElement> apinote= new ArrayList<>(1); |
| List<TagElement> implspec= new ArrayList<>(1); |
| List<TagElement> implnote= new ArrayList<>(1); |
| List<TagElement> hidden= new ArrayList<>(1); |
| |
| List<TagElement> tags= fJavadoc.tags(); |
| for (TagElement tag : tags) { |
| String tagName= tag.getTagName(); |
| if (tagName == null) { |
| start= tag; |
| |
| } else { |
| switch (tagName) { |
| case TagElement.TAG_PARAM: |
| List<? extends ASTNode> fragments= tag.fragments(); |
| int size= fragments.size(); |
| if (size > 0) { |
| Object first= fragments.get(0); |
| if (first instanceof SimpleName) { |
| String name= ((SimpleName) first).getIdentifier(); |
| int paramIndex= parameterNames.indexOf(name); |
| if (paramIndex != -1) { |
| parameterNames.set(paramIndex, null); |
| } |
| parameters.add(tag); |
| } else if (size > 2 && first instanceof TextElement) { |
| String firstText= ((TextElement) first).getText(); |
| if ("<".equals(firstText)) { //$NON-NLS-1$ |
| Object second= fragments.get(1); |
| Object third= fragments.get(2); |
| if (second instanceof SimpleName && third instanceof TextElement) { |
| String name= ((SimpleName) second).getIdentifier(); |
| String thirdText= ((TextElement) third).getText(); |
| if (">".equals(thirdText)) { //$NON-NLS-1$ |
| int paramIndex= typeParameterNames.indexOf(name); |
| if (paramIndex != -1) { |
| typeParameterNames.set(paramIndex, null); |
| } |
| typeParameters.add(tag); |
| } |
| } |
| } |
| } |
| } |
| break; |
| case TagElement.TAG_RETURN: |
| if (returnTag == null) |
| returnTag= tag; // the Javadoc tool only shows the first return tag |
| break; |
| case TagElement.TAG_EXCEPTION: |
| case TagElement.TAG_THROWS: |
| exceptions.add(tag); |
| List<? extends ASTNode> fragments2= tag.fragments(); |
| if (fragments2.size() > 0) { |
| Object first= fragments2.get(0); |
| if (first instanceof Name) { |
| String name= ASTNodes.getSimpleNameIdentifier((Name) first); |
| int exceptionIndex= exceptionNames.indexOf(name); |
| if (exceptionIndex != -1) { |
| exceptionNames.set(exceptionIndex, null); |
| } |
| } |
| } |
| break; |
| case TagElement.TAG_PROVIDES: |
| provides.add(tag); |
| break; |
| case TagElement.TAG_USES: |
| uses.add(tag); |
| break; |
| case TagElement.TAG_SINCE: |
| since.add(tag); |
| break; |
| case TagElement.TAG_VERSION: |
| versions.add(tag); |
| break; |
| case TagElement.TAG_AUTHOR: |
| authors.add(tag); |
| break; |
| case TagElement.TAG_SEE: |
| sees.add(tag); |
| break; |
| case TagElement.TAG_DEPRECATED: |
| if (deprecatedTag == null) |
| deprecatedTag= tag; // the Javadoc tool only shows the first deprecated tag |
| break; |
| case TagElement.TAG_API_NOTE: |
| apinote.add(tag); |
| break; |
| case TagElement.TAG_IMPL_SPEC: |
| implspec.add(tag); |
| break; |
| case TagElement.TAG_IMPL_NOTE: |
| implnote.add(tag); |
| break; |
| case TagElement.TAG_HIDDEN: |
| hidden.add(tag); |
| break; |
| default: |
| rest.add(tag); |
| break; |
| } |
| } |
| } |
| |
| //TODO: @Documented annotations before header |
| if (deprecatedTag != null) |
| handleDeprecatedTag(deprecatedTag); |
| if (start != null) |
| handleContentElements(start.fragments()); |
| else if (fMethod != null) { |
| CharSequence inherited= fJavadocLookup.getInheritedMainDescription(fMethod); |
| // The Javadoc tool adds "Description copied from class: ..." (only for the main description). |
| // We don't bother doing that. |
| handleInherited(inherited); |
| } |
| |
| CharSequence[] typeParameterDescriptions= new CharSequence[typeParameterNames.size()]; |
| boolean hasInheritedTypeParameters= inheritTypeParameterDescriptions(typeParameterNames, typeParameterDescriptions); |
| boolean hasTypeParameters= typeParameters.size() > 0 || hasInheritedTypeParameters; |
| |
| CharSequence[] parameterDescriptions= new CharSequence[parameterNames.size()]; |
| boolean hasInheritedParameters= inheritParameterDescriptions(parameterNames, parameterDescriptions); |
| boolean hasParameters= parameters.size() > 0 || hasInheritedParameters; |
| |
| CharSequence returnDescription= null; |
| if (returnTag == null && needsReturnTag()) |
| returnDescription= fJavadocLookup.getInheritedReturnDescription(fMethod); |
| boolean hasReturnTag= returnTag != null || returnDescription != null; |
| |
| CharSequence[] exceptionDescriptions= new CharSequence[exceptionNames.size()]; |
| boolean hasInheritedExceptions= inheritExceptionDescriptions(exceptionNames, exceptionDescriptions); |
| boolean hasExceptions= exceptions.size() > 0 || hasInheritedExceptions; |
| |
| if (hasParameters || hasTypeParameters || hasReturnTag || hasExceptions |
| || versions.size() > 0 || authors.size() > 0 || since.size() > 0 || sees.size() > 0 || |
| apinote.size() > 0 || implnote.size() > 0 || implspec.size() > 0 || uses.size() > 0 || |
| provides.size() > 0 || hidden.size() > 0 || rest.size() > 0 |
| || (fBuf.length() > 0 && (parameterDescriptions.length > 0 || exceptionDescriptions.length > 0)) |
| ) { |
| handleSuperMethodReferences(); |
| fBuf.append(BLOCK_TAG_START); |
| handleParameterTags(typeParameters, typeParameterNames, typeParameterDescriptions, true); |
| handleParameterTags(parameters, parameterNames, parameterDescriptions, false); |
| handleReturnTag(returnTag, returnDescription); |
| handleExceptionTags(exceptions, exceptionNames, exceptionDescriptions); |
| handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_since_section, since); |
| handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_version_section, versions); |
| handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_author_section, authors); |
| handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_see_section, sees); |
| handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_api_note, apinote); |
| handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_impl_spec, implspec); |
| handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_impl_note, implnote); |
| handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_uses, uses); |
| handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_provides, provides); |
| if (hidden.size() > 0) { |
| handleBlockTagsHidden(); |
| } |
| handleBlockTags(rest); |
| fBuf.append(BLOCK_TAG_END); |
| |
| } else if (fBuf.length() > 0) { |
| handleSuperMethodReferences(); |
| } |
| } |
| |
| private void handleBlockTagsHidden() { |
| String replaceAll= fBuf.toString().replaceAll(BLOCK_TAG_START, "<dl hidden>"); //$NON-NLS-1$ |
| replaceAll= replaceAll.replaceAll(BlOCK_TAG_TITLE_START, "<dt hidden>"); //$NON-NLS-1$ |
| replaceAll= replaceAll.replaceAll(BlOCK_TAG_ENTRY_START, "<dd hidden>"); //$NON-NLS-1$ |
| // For tags like deprecated |
| replaceAll= replaceAll.replaceAll(PARAM_NAME_START, "<b hidden>"); //$NON-NLS-1$ |
| fBuf.setLength(0); |
| fBuf.append(replaceAll); |
| } |
| |
| private void handleDeprecatedTag(TagElement tag) { |
| fBuf.append("<p><b>"); //$NON-NLS-1$ |
| fBuf.append(JavaDocMessages.JavaDoc2HTMLTextReader_deprecated_section); |
| fBuf.append("</b> <i>"); //$NON-NLS-1$ |
| handleContentElements(tag.fragments()); |
| fBuf.append("</i><p>"); //$NON-NLS-1$ TODO: Why not </p>? See https://bugs.eclipse.org/bugs/show_bug.cgi?id=243318 . |
| } |
| |
| private void handleSuperMethodReferences() { |
| if (fMethod != null) { |
| try { |
| StringBuffer superMethodReferences= createSuperMethodReferences(fMethod); |
| if (superMethodReferences != null) |
| fBuf.append(superMethodReferences); |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| } |
| } |
| } |
| |
| private List<String> initTypeParameterNames() { |
| if (fMethod != null) { |
| try { |
| ArrayList<String> typeParameterNames= new ArrayList<>(); |
| for (ITypeParameter typeParameter : fMethod.getTypeParameters()) { |
| typeParameterNames.add(typeParameter.getElementName()); |
| } |
| return typeParameterNames; |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| } |
| } |
| return Collections.emptyList(); |
| } |
| |
| private List<String> initParameterNames() { |
| if (fMethod != null) { |
| try { |
| return new ArrayList<>(Arrays.asList(fMethod.getParameterNames())); |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| } |
| } |
| return Collections.emptyList(); |
| } |
| |
| private List<String> initExceptionNames() { |
| if (fMethod != null) { |
| try { |
| ArrayList<String> exceptionNames= new ArrayList<>(); |
| for (String exceptionType : fMethod.getExceptionTypes()) { |
| exceptionNames.add(Signature.getSimpleName(Signature.toString(exceptionType))); |
| } |
| return exceptionNames; |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| } |
| } |
| return Collections.emptyList(); |
| } |
| |
| private boolean needsReturnTag() { |
| if (fMethod == null) |
| return false; |
| try { |
| return ! Signature.SIG_VOID.equals(fMethod.getReturnType()); |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| return false; |
| } |
| } |
| |
| private boolean inheritTypeParameterDescriptions(List<String> typeParameterNames, CharSequence[] typeParameterDescriptions) { |
| boolean hasInheritedTypeParameters= false; |
| for (int i= 0; i < typeParameterNames.size(); i++) { |
| String name= typeParameterNames.get(i); |
| if (name != null) { |
| typeParameterDescriptions[i]= fJavadocLookup.getInheritedTypeParamDescription(fMethod, i); |
| if (typeParameterDescriptions[i] != null) |
| hasInheritedTypeParameters= true; |
| } |
| } |
| return hasInheritedTypeParameters; |
| } |
| |
| private boolean inheritParameterDescriptions(List<String> parameterNames, CharSequence[] parameterDescriptions) { |
| boolean hasInheritedParameters= false; |
| for (int i= 0; i < parameterNames.size(); i++) { |
| String name= parameterNames.get(i); |
| if (name != null) { |
| parameterDescriptions[i]= fJavadocLookup.getInheritedParamDescription(fMethod, i); |
| if (parameterDescriptions[i] != null) |
| hasInheritedParameters= true; |
| } |
| } |
| return hasInheritedParameters; |
| } |
| |
| private boolean inheritExceptionDescriptions(List<String> exceptionNames, CharSequence[] exceptionDescriptions) { |
| boolean hasInheritedExceptions= false; |
| for (int i= 0; i < exceptionNames.size(); i++) { |
| String name= exceptionNames.get(i); |
| if (name != null) { |
| exceptionDescriptions[i]= fJavadocLookup.getInheritedExceptionDescription(fMethod, name); |
| if (exceptionDescriptions[i] != null) |
| hasInheritedExceptions= true; |
| } |
| } |
| return hasInheritedExceptions; |
| } |
| |
| CharSequence getMainDescription() { |
| if (fMainDescription == null) { |
| fMainDescription= new StringBuffer(); |
| fBuf= fMainDescription; |
| fLiteralContent= 0; |
| |
| List<TagElement> tags= fJavadoc.tags(); |
| for (TagElement tag : tags) { |
| String tagName= tag.getTagName(); |
| if (tagName == null) { |
| handleContentElements(tag.fragments()); |
| break; |
| } |
| } |
| |
| fBuf= null; |
| } |
| return fMainDescription.length() > 0 ? fMainDescription : null; |
| } |
| |
| CharSequence getReturnDescription() { |
| if (fReturnDescription == null) { |
| fReturnDescription= new StringBuffer(); |
| fBuf= fReturnDescription; |
| fLiteralContent= 0; |
| |
| List<TagElement> tags= fJavadoc.tags(); |
| for (TagElement tag : tags) { |
| String tagName= tag.getTagName(); |
| if (TagElement.TAG_RETURN.equals(tagName)) { |
| handleContentElements(tag.fragments()); |
| break; |
| } |
| } |
| |
| fBuf= null; |
| } |
| return fReturnDescription.length() > 0 ? fReturnDescription : null; |
| } |
| |
| CharSequence getInheritedTypeParamDescription(int typeParamIndex) { |
| if (fMethod != null) { |
| List<String> typeParameterNames= initTypeParameterNames(); |
| if (fTypeParamDescriptions == null) { |
| fTypeParamDescriptions= new StringBuffer[typeParameterNames.size()]; |
| } else { |
| StringBuffer description= fTypeParamDescriptions[typeParamIndex]; |
| if (description != null) { |
| return description.length() > 0 ? description : null; |
| } |
| } |
| |
| StringBuffer description= new StringBuffer(); |
| fTypeParamDescriptions[typeParamIndex]= description; |
| fBuf= description; |
| fLiteralContent= 0; |
| |
| String typeParamName= typeParameterNames.get(typeParamIndex); |
| List<TagElement> tags= fJavadoc.tags(); |
| for (TagElement tag : tags) { |
| String tagName= tag.getTagName(); |
| if (TagElement.TAG_PARAM.equals(tagName)) { |
| List<? extends ASTNode> fragments= tag.fragments(); |
| if (fragments.size() > 2) { |
| Object first= fragments.get(0); |
| Object second= fragments.get(1); |
| Object third= fragments.get(2); |
| if (first instanceof TextElement && second instanceof SimpleName && third instanceof TextElement) { |
| String firstText= ((TextElement) first).getText(); |
| String thirdText= ((TextElement) third).getText(); |
| if ("<".equals(firstText) && ">".equals(thirdText)) { //$NON-NLS-1$ //$NON-NLS-2$ |
| String name= ((SimpleName) second).getIdentifier(); |
| if (name.equals(typeParamName)) { |
| handleContentElements(fragments.subList(3, fragments.size())); |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| fBuf= null; |
| return description.length() > 0 ? description : null; |
| } |
| return null; |
| } |
| |
| CharSequence getInheritedParamDescription(int paramIndex) throws JavaModelException { |
| if (fMethod != null) { |
| String[] parameterNames= fMethod.getParameterNames(); |
| if (fParamDescriptions == null) { |
| fParamDescriptions= new StringBuffer[parameterNames.length]; |
| } else { |
| StringBuffer description= fParamDescriptions[paramIndex]; |
| if (description != null) { |
| return description.length() > 0 ? description : null; |
| } |
| } |
| |
| StringBuffer description= new StringBuffer(); |
| fParamDescriptions[paramIndex]= description; |
| fBuf= description; |
| fLiteralContent= 0; |
| |
| String paramName= parameterNames[paramIndex]; |
| List<TagElement> tags= fJavadoc.tags(); |
| for (TagElement tag : tags) { |
| String tagName= tag.getTagName(); |
| if (TagElement.TAG_PARAM.equals(tagName)) { |
| List<? extends ASTNode> fragments= tag.fragments(); |
| if (fragments.size() > 0) { |
| Object first= fragments.get(0); |
| if (first instanceof SimpleName) { |
| String name= ((SimpleName) first).getIdentifier(); |
| if (name.equals(paramName)) { |
| handleContentElements(fragments.subList(1, fragments.size())); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| fBuf= null; |
| return description.length() > 0 ? description : null; |
| } |
| return null; |
| } |
| |
| CharSequence getExceptionDescription(String simpleName) { |
| if (fMethod != null) { |
| if (fExceptionDescriptions == null) { |
| fExceptionDescriptions= new HashMap<>(); |
| } else { |
| StringBuffer description= fExceptionDescriptions.get(simpleName); |
| if (description != null) { |
| return description.length() > 0 ? description : null; |
| } |
| } |
| |
| StringBuffer description= new StringBuffer(); |
| fExceptionDescriptions.put(simpleName, description); |
| fBuf= description; |
| fLiteralContent= 0; |
| |
| List<TagElement> tags= fJavadoc.tags(); |
| for (TagElement tag : tags) { |
| String tagName= tag.getTagName(); |
| if (TagElement.TAG_THROWS.equals(tagName) || TagElement.TAG_EXCEPTION.equals(tagName)) { |
| List<? extends ASTNode> fragments= tag.fragments(); |
| if (fragments.size() > 0) { |
| Object first= fragments.get(0); |
| if (first instanceof Name) { |
| String name= ASTNodes.getSimpleNameIdentifier((Name) first); |
| if (name.equals(simpleName)) { |
| if (fragments.size() > 1) |
| handleContentElements(fragments.subList(1, fragments.size())); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| fBuf= null; |
| return description.length() > 0 ? description : null; |
| } |
| return null; |
| } |
| |
| |
| private void handleContentElements(List<? extends ASTNode> nodes) { |
| handleContentElements(nodes, false); |
| } |
| |
| private void handleContentElements(List<? extends ASTNode> nodes, boolean skipLeadingWhitespace) { |
| ASTNode previousNode= null; |
| for (ASTNode child : nodes) { |
| if (previousNode != null) { |
| int previousEnd= previousNode.getStartPosition() + previousNode.getLength(); |
| int childStart= child.getStartPosition(); |
| if (previousEnd > childStart) { |
| // should never happen, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=304826 |
| Exception exception= new Exception("Illegal ASTNode positions: previousEnd=" + previousEnd //$NON-NLS-1$ |
| + ", childStart=" + childStart //$NON-NLS-1$ |
| + ", element=" + fElement.getHandleIdentifier() //$NON-NLS-1$ |
| + ", Javadoc:\n" + fSource); //$NON-NLS-1$ |
| JavaPlugin.log(exception); |
| } else if (previousEnd != childStart) { |
| // Need to preserve whitespace before a node that's not |
| // directly following the previous node (e.g. on a new line) |
| // due to https://bugs.eclipse.org/bugs/show_bug.cgi?id=206518 : |
| String textWithStars= fSource.substring(previousEnd, childStart); |
| String text= removeDocLineIntros(textWithStars); |
| fBuf.append(text); |
| } |
| } |
| previousNode= child; |
| if (child instanceof TextElement) { |
| String text= ((TextElement) child).getText(); |
| if (skipLeadingWhitespace) { |
| text= text.replaceFirst("^\\s", ""); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=233481 : |
| text= text.replaceAll("(\r\n?|\n)([ \t]*\\*)", "$1"); //$NON-NLS-1$ //$NON-NLS-2$ |
| handleText(text); |
| } else if (child instanceof TagElement) { |
| handleInlineTagElement((TagElement) child); |
| } else { |
| // This is unexpected. Fail gracefully by just copying the source. |
| int start= child.getStartPosition(); |
| String text= fSource.substring(start, start + child.getLength()); |
| fBuf.append(removeDocLineIntros(text)); |
| } |
| } |
| } |
| |
| private String removeDocLineIntros(String textWithStars) { |
| String lineBreakGroup= "(\\r\\n?|\\n)"; //$NON-NLS-1$ |
| String noBreakSpace= "[^\r\n&&\\s]"; //$NON-NLS-1$ |
| return textWithStars.replaceAll(lineBreakGroup + noBreakSpace + "*\\*" /*+ noBreakSpace + '?'*/, "$1"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| private void handleText(String text) { |
| if (fLiteralContent == 0) { |
| fBuf.append(text); |
| } else { |
| appendEscaped(fBuf, text); |
| } |
| } |
| |
| private static void appendEscaped(StringBuffer buf, String text) { |
| int nextToCopy= 0; |
| int length= text.length(); |
| for (int i= 0; i < length; i++) { |
| char ch= text.charAt(i); |
| String rep= null; |
| switch (ch) { |
| case '&': |
| rep= "&"; //$NON-NLS-1$ |
| break; |
| case '"': |
| rep= """; //$NON-NLS-1$ |
| break; |
| case '<': |
| rep= "<"; //$NON-NLS-1$ |
| break; |
| case '>': |
| rep= ">"; //$NON-NLS-1$ |
| break; |
| } |
| if (rep != null) { |
| if (nextToCopy < i) |
| buf.append(text.substring(nextToCopy, i)); |
| buf.append(rep); |
| nextToCopy= i + 1; |
| } |
| } |
| if (nextToCopy < length) |
| buf.append(text.substring(nextToCopy)); |
| } |
| |
| private void handleInlineTagElement(TagElement node) { |
| String name= node.getTagName(); |
| if (TagElement.TAG_VALUE.equals(name) && handleValueTag(node)) |
| return; |
| |
| boolean isLink= TagElement.TAG_LINK.equals(name); |
| boolean isLinkplain= TagElement.TAG_LINKPLAIN.equals(name); |
| boolean isCode= TagElement.TAG_CODE.equals(name); |
| boolean isLiteral= TagElement.TAG_LITERAL.equals(name); |
| boolean isSummary = TagElement.TAG_SUMMARY.equals(name); |
| boolean isIndex = TagElement.TAG_INDEX.equals(name); |
| boolean isSnippet = TagElement.TAG_SNIPPET.equals(name); |
| |
| if (isLiteral || isCode || isSummary || isIndex) |
| fLiteralContent++; |
| if (isLink || isCode) |
| fBuf.append("<code>"); //$NON-NLS-1$ |
| |
| if (isLink || isLinkplain) |
| handleLink(node.fragments()); |
| else if (isSummary) |
| handleSummary(node.fragments()); |
| else if (isIndex) |
| handleIndex(node.fragments()); |
| else if (isCode || isLiteral) |
| handleContentElements(node.fragments(), true); |
| else if (isSnippet) { |
| handleSnippet(node.fragments()); |
| } |
| else if (handleInheritDoc(node) || handleDocRoot(node)) { |
| // Handled |
| } else { |
| //print uninterpreted source {@tagname ...} for unknown tags |
| int start= node.getStartPosition(); |
| String text= fSource.substring(start, start + node.getLength()); |
| fBuf.append(removeDocLineIntros(text)); |
| } |
| |
| if (isLink || isCode) |
| fBuf.append("</code>"); //$NON-NLS-1$ |
| if (isSnippet) |
| fBuf.append("</code></pre>"); //$NON-NLS-1$ |
| if (isLiteral || isCode) |
| fLiteralContent--; |
| |
| } |
| |
| private boolean handleValueTag(TagElement node) { |
| |
| List<? extends ASTNode> fragments= node.fragments(); |
| try { |
| if (!(fElement instanceof IMember)) { |
| return false; |
| } |
| if (fragments.isEmpty()) { |
| if (fElement instanceof IField && JdtFlags.isStatic((IField) fElement) && JdtFlags.isFinal((IField) fElement)) { |
| IField field= (IField) fElement; |
| return handleConstantValue(field, false); |
| } |
| } else if (fragments.size() == 1) { |
| Object first= fragments.get(0); |
| if (first instanceof MemberRef) { |
| MemberRef memberRef= (MemberRef) first; |
| IType type= fElement instanceof IType ? (IType) fElement : ((IMember) fElement).getDeclaringType(); |
| if (memberRef.getQualifier() != null) { |
| String[][] qualifierTypes= type.resolveType(memberRef.getQualifier().getFullyQualifiedName()); |
| if (qualifierTypes != null && qualifierTypes.length == 1) { |
| type= type.getJavaProject().findType(String.join(".", qualifierTypes[0]), (IProgressMonitor)null); //$NON-NLS-1$ |
| } |
| } |
| SimpleName name= memberRef.getName(); |
| while (type != null) { |
| IField field= type.getField(name.getIdentifier()); |
| if (field != null && field.exists()) { |
| if (JdtFlags.isStatic(field) && JdtFlags.isFinal(field)) |
| return handleConstantValue(field, true); |
| break; |
| } |
| type= type.getDeclaringType(); |
| } |
| } |
| } |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| } |
| |
| return false; |
| } |
| |
| private boolean handleConstantValue(IField field, boolean link) throws JavaModelException { |
| String text= null; |
| |
| ISourceRange nameRange= field.getNameRange(); |
| if (SourceRange.isAvailable(nameRange)) { |
| CompilationUnit cuNode= SharedASTProviderCore.getAST(field.getTypeRoot(), SharedASTProviderCore.WAIT_ACTIVE_ONLY, null); |
| if (cuNode != null) { |
| ASTNode nameNode= NodeFinder.perform(cuNode, nameRange); |
| if (nameNode instanceof SimpleName) { |
| IBinding binding= ((SimpleName) nameNode).resolveBinding(); |
| if (binding instanceof IVariableBinding) { |
| IVariableBinding variableBinding= (IVariableBinding) binding; |
| Object constantValue= variableBinding.getConstantValue(); |
| if (constantValue != null) { |
| if (constantValue instanceof String) { |
| text= ASTNodes.getEscapedStringLiteral((String) constantValue); |
| } else { |
| text= constantValue.toString(); // Javadoc tool is even worse for chars... |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (text == null) { |
| Object constant= field.getConstant(); |
| if (constant != null) { |
| text= constant.toString(); |
| } |
| } |
| |
| if (text != null) { |
| text= HTMLPrinter.convertToHTMLContentWithWhitespace(text); |
| if (link) { |
| String uri; |
| try { |
| uri= JavaElementLinks.createURI(JavaElementLinks.JAVADOC_SCHEME, field); |
| fBuf.append(JavaElementLinks.createLink(uri, text)); |
| } catch (URISyntaxException e) { |
| JavaPlugin.log(e); |
| return false; |
| } |
| } else { |
| handleText(text); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| private boolean handleDocRoot(TagElement node) { |
| if (!TagElement.TAG_DOCROOT.equals(node.getTagName())) |
| return false; |
| |
| try { |
| String url= null; |
| if (fElement instanceof IMember && ((IMember) fElement).isBinary()) { |
| URL javadocBaseLocation= JavaUI.getJavadocBaseLocation(fElement); |
| if (javadocBaseLocation != null) { |
| url= javadocBaseLocation.toExternalForm(); |
| } |
| } else { |
| IPackageFragmentRoot srcRoot= JavaModelUtil.getPackageFragmentRoot(fElement); |
| if (srcRoot != null) { |
| IResource resource= srcRoot.getResource(); |
| if (resource != null) { |
| /* |
| * Too bad: Browser widget knows nothing about EFS and custom URL handlers, |
| * so IResource#getLocationURI() does not work in all cases. |
| * We only support the local file system for now. |
| * A solution could be https://bugs.eclipse.org/bugs/show_bug.cgi?id=149022 . |
| */ |
| IPath location= resource.getLocation(); |
| if (location != null) { |
| url= location.toFile().toURI().toASCIIString(); |
| } |
| } |
| |
| } |
| } |
| if (url != null) { |
| if (url.endsWith("/")) { //$NON-NLS-1$ |
| url= url.substring(0, url.length() -1); |
| } |
| fBuf.append(url); |
| return true; |
| } |
| } catch (JavaModelException e) { |
| } |
| return false; |
| } |
| |
| |
| /** |
| * Handle {@inheritDoc}. |
| * |
| * @param node the node |
| * @return <code>true</code> iff the node was an {@inheritDoc} node and has been handled |
| */ |
| private boolean handleInheritDoc(TagElement node) { |
| if (! TagElement.TAG_INHERITDOC.equals(node.getTagName())) |
| return false; |
| try { |
| if (fMethod == null) |
| return false; |
| |
| TagElement blockTag= (TagElement) node.getParent(); |
| String blockTagName= blockTag.getTagName(); |
| |
| if (blockTagName == null) { |
| CharSequence inherited= fJavadocLookup.getInheritedMainDescription(fMethod); |
| return handleInherited(inherited); |
| |
| } else if (TagElement.TAG_PARAM.equals(blockTagName)) { |
| List<? extends ASTNode> fragments= blockTag.fragments(); |
| int size= fragments.size(); |
| if (size > 0) { |
| Object first= fragments.get(0); |
| if (first instanceof SimpleName) { |
| String name= ((SimpleName) first).getIdentifier(); |
| String[] parameterNames= fMethod.getParameterNames(); |
| for (int i= 0; i < parameterNames.length; i++) { |
| if (name.equals(parameterNames[i])) { |
| CharSequence inherited= fJavadocLookup.getInheritedParamDescription(fMethod, i); |
| return handleInherited(inherited); |
| } |
| } |
| } else if (size > 2 && first instanceof TextElement) { |
| String firstText= ((TextElement) first).getText(); |
| if ("<".equals(firstText)) { //$NON-NLS-1$ |
| Object second= fragments.get(1); |
| Object third= fragments.get(2); |
| if (second instanceof SimpleName && third instanceof TextElement) { |
| String thirdText= ((TextElement) third).getText(); |
| if (">".equals(thirdText)) { //$NON-NLS-1$ |
| String name= ((SimpleName) second).getIdentifier(); |
| ITypeParameter[] typeParameters= fMethod.getTypeParameters(); |
| for (int i= 0; i < typeParameters.length; i++) { |
| ITypeParameter typeParameter= typeParameters[i]; |
| if (name.equals(typeParameter.getElementName())) { |
| CharSequence inherited= fJavadocLookup.getInheritedTypeParamDescription(fMethod, i); |
| return handleInherited(inherited); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| } else if (TagElement.TAG_RETURN.equals(blockTagName)) { |
| CharSequence inherited= fJavadocLookup.getInheritedReturnDescription(fMethod); |
| return handleInherited(inherited); |
| |
| } else if (TagElement.TAG_THROWS.equals(blockTagName) || TagElement.TAG_EXCEPTION.equals(blockTagName)) { |
| List<? extends ASTNode> fragments= blockTag.fragments(); |
| if (fragments.size() > 0) { |
| Object first= fragments.get(0); |
| if (first instanceof Name) { |
| String name= ASTNodes.getSimpleNameIdentifier((Name) first); |
| CharSequence inherited= fJavadocLookup.getInheritedExceptionDescription(fMethod, name); |
| return handleInherited(inherited); |
| } |
| } |
| } |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| } |
| return false; |
| } |
| |
| private boolean handleInherited(CharSequence inherited) { |
| if (inherited == null) |
| return false; |
| |
| fBuf.append(inherited); |
| return true; |
| } |
| |
| private void handleBlockTags(String title, List<TagElement> tags) { |
| if (tags.isEmpty()) |
| return; |
| |
| handleBlockTagTitle(title); |
| |
| for (TagElement tag : tags) { |
| fBuf.append(BlOCK_TAG_ENTRY_START); |
| if (TagElement.TAG_SEE.equals(tag.getTagName())) { |
| handleSeeTag(tag); |
| } else { |
| handleContentElements(tag.fragments()); |
| } |
| fBuf.append(BlOCK_TAG_ENTRY_END); |
| } |
| } |
| |
| private void handleReturnTag(TagElement tag, CharSequence returnDescription) { |
| if (tag == null && returnDescription == null) |
| return; |
| |
| handleBlockTagTitle(JavaDocMessages.JavaDoc2HTMLTextReader_returns_section); |
| fBuf.append(BlOCK_TAG_ENTRY_START); |
| if (tag != null) |
| handleContentElements(tag.fragments()); |
| else |
| fBuf.append(returnDescription); |
| fBuf.append(BlOCK_TAG_ENTRY_END); |
| } |
| |
| private void handleBlockTags(List<TagElement> tags) { |
| for (TagElement tag : tags) { |
| handleBlockTagTitle(tag.getTagName()); |
| fBuf.append(BlOCK_TAG_ENTRY_START); |
| handleContentElements(tag.fragments()); |
| fBuf.append(BlOCK_TAG_ENTRY_END); |
| } |
| } |
| |
| private void handleBlockTagTitle(String title) { |
| fBuf.append(BlOCK_TAG_TITLE_START); |
| fBuf.append(title); |
| fBuf.append(BlOCK_TAG_TITLE_END); |
| } |
| |
| private void handleSeeTag(TagElement tag) { |
| handleLink(tag.fragments()); |
| } |
| |
| private void handleExceptionTags(List<TagElement> tags, List<String> exceptionNames, CharSequence[] exceptionDescriptions) { |
| if (tags.isEmpty() && containsOnlyNull(exceptionNames)) |
| return; |
| |
| handleBlockTagTitle(JavaDocMessages.JavaDoc2HTMLTextReader_throws_section); |
| |
| for (TagElement tag : tags) { |
| fBuf.append(BlOCK_TAG_ENTRY_START); |
| handleThrowsTag(tag); |
| fBuf.append(BlOCK_TAG_ENTRY_END); |
| } |
| for (int i= 0; i < exceptionDescriptions.length; i++) { |
| CharSequence description= exceptionDescriptions[i]; |
| String name= exceptionNames.get(i); |
| if (name != null) { |
| fBuf.append(BlOCK_TAG_ENTRY_START); |
| handleLink(Collections.singletonList(fJavadoc.getAST().newSimpleName(name))); |
| if (description != null) { |
| fBuf.append(JavaElementLabels.CONCAT_STRING); |
| fBuf.append(description); |
| } |
| fBuf.append(BlOCK_TAG_ENTRY_END); |
| } |
| } |
| } |
| |
| private void handleThrowsTag(TagElement tag) { |
| List<? extends ASTNode> fragments= tag.fragments(); |
| int size= fragments.size(); |
| if (size > 0) { |
| handleLink(fragments.subList(0, 1)); |
| if (size > 1) { |
| fBuf.append(JavaElementLabels.CONCAT_STRING); |
| handleContentElements(fragments.subList(1, size)); |
| } |
| } |
| } |
| |
| private void handleParameterTags(List<TagElement> tags, List<String> parameterNames, CharSequence[] parameterDescriptions, boolean isTypeParameters) { |
| if (tags.isEmpty() && containsOnlyNull(parameterNames)) |
| return; |
| |
| String tagTitle= isTypeParameters ? JavaDocMessages.JavaDoc2HTMLTextReader_type_parameters_section : JavaDocMessages.JavaDoc2HTMLTextReader_parameters_section; |
| handleBlockTagTitle(tagTitle); |
| |
| for (TagElement tag : tags) { |
| fBuf.append(BlOCK_TAG_ENTRY_START); |
| handleParamTag(tag); |
| fBuf.append(BlOCK_TAG_ENTRY_END); |
| } |
| for (int i= 0; i < parameterDescriptions.length; i++) { |
| CharSequence description= parameterDescriptions[i]; |
| String name= parameterNames.get(i); |
| if (name != null) { |
| fBuf.append(BlOCK_TAG_ENTRY_START); |
| fBuf.append(PARAM_NAME_START); |
| if (isTypeParameters) { |
| fBuf.append("<"); //$NON-NLS-1$ |
| } |
| fBuf.append(name); |
| if (isTypeParameters) { |
| fBuf.append(">"); //$NON-NLS-1$ |
| } |
| fBuf.append(PARAM_NAME_END); |
| if (description != null) |
| fBuf.append(description); |
| fBuf.append(BlOCK_TAG_ENTRY_END); |
| } |
| } |
| } |
| |
| private void handleParamTag(TagElement tag) { |
| List<? extends ASTNode> fragments= tag.fragments(); |
| int i= 0; |
| int size= fragments.size(); |
| if (size > 0) { |
| Object first= fragments.get(0); |
| fBuf.append(PARAM_NAME_START); |
| if (first instanceof SimpleName) { |
| String name= ((SimpleName) first).getIdentifier(); |
| fBuf.append(name); |
| i++; |
| } else if (first instanceof TextElement) { |
| String firstText= ((TextElement) first).getText(); |
| if ("<".equals(firstText)) { //$NON-NLS-1$ |
| fBuf.append("<"); //$NON-NLS-1$ |
| i++; |
| if (size > 1) { |
| Object second= fragments.get(1); |
| if (second instanceof SimpleName) { |
| String name= ((SimpleName) second).getIdentifier(); |
| fBuf.append(name); |
| i++; |
| if (size > 2) { |
| Object third= fragments.get(2); |
| String thirdText= ((TextElement) third).getText(); |
| if (">".equals(thirdText)) { //$NON-NLS-1$ |
| fBuf.append(">"); //$NON-NLS-1$ |
| i++; |
| } |
| } |
| } |
| } |
| } |
| } |
| fBuf.append(PARAM_NAME_END); |
| |
| handleContentElements(fragments.subList(i, fragments.size())); |
| } |
| } |
| |
| private void handleSummary(List<? extends ASTNode> fragments) { |
| int fs= fragments.size(); |
| if (fs > 0) { |
| Object first= fragments.get(0); |
| if (first instanceof TextElement) { |
| TextElement memberRef= (TextElement) first; |
| fBuf.append(BlOCK_TAG_TITLE_START + "Summary: " + memberRef.getText() + BlOCK_TAG_TITLE_END); //$NON-NLS-1$ |
| return; |
| } |
| } |
| } |
| |
| private void handleSnippet(List<? extends ASTNode> fragments) { |
| int fs= fragments.size(); |
| if (fs > 0) { |
| fBuf.append("<pre><code>"); //$NON-NLS-1$ |
| fBuf.append(BlOCK_TAG_ENTRY_START); |
| for(ASTNode fragment : fragments) { |
| if (fragment instanceof TextElement) { |
| TextElement memberRef= (TextElement) fragment; |
| fBuf.append(memberRef.getText()); |
| } else if (fragment instanceof TagElement) { |
| TagElement tagElem= (TagElement) fragment; |
| String name= tagElem.getTagName(); |
| if (TagElement.TAG_HIGHLIGHT.equals(name)) { |
| handleSnippetHighlight(tagElem.fragments(), tagElem.tagProperties()); |
| } else if (TagElement.TAG_REPLACE.equals(name)) { |
| handleSnippetReplace(tagElem.fragments(), tagElem.tagProperties()); |
| } |
| } |
| } |
| fBuf.append(BlOCK_TAG_ENTRY_END); |
| } |
| } |
| |
| private void handleSnippetReplace(List<? extends ASTNode> fragments, List<? extends ASTNode> tagProperties) { |
| try { |
| int fs= fragments.size(); |
| boolean process = arePropertyValuesStringLiterals(tagProperties) ? true : false; |
| String regExValue = getPropertyValue("regex", tagProperties); //$NON-NLS-1$ |
| String subStringValue = getPropertyValue("substring", tagProperties); //$NON-NLS-1$ |
| String substitution = getPropertyValue("replacement", tagProperties); //$NON-NLS-1$ |
| Pattern regexPattern = null; |
| if (regExValue != null) { |
| regexPattern = Pattern.compile(regExValue); |
| } |
| if (fs > 0) { |
| for(ASTNode fragment : fragments) { |
| if (fragment instanceof TextElement) { |
| TextElement memberRef= (TextElement) fragment; |
| String modifiedText = memberRef.getText(); |
| if (process) { |
| if (regexPattern != null && process) { |
| Matcher matcher = regexPattern.matcher(modifiedText); |
| StringBuilder strBuild= new StringBuilder(); |
| int finalMatchIndex = 0; |
| while (matcher.find()) { |
| finalMatchIndex = matcher.end(); |
| matcher.appendReplacement(strBuild, substitution); |
| } |
| modifiedText = strBuild.toString() + modifiedText.substring(finalMatchIndex); |
| } else if (subStringValue != null) { |
| int startIndex = 0; |
| while (true) { |
| startIndex = modifiedText.indexOf(subStringValue, startIndex); |
| if (startIndex == -1) { |
| break; |
| } else { |
| modifiedText = modifiedText.substring(0, startIndex) + substitution + modifiedText.substring(startIndex + subStringValue.length()); |
| startIndex = startIndex + substitution.length() ; |
| } |
| } |
| } else { |
| modifiedText = substitution; |
| } |
| } |
| fBuf.append(modifiedText); |
| } |
| } |
| } |
| } catch (PatternSyntaxException e) { |
| // do nothing |
| } |
| } |
| |
| private void handleSnippetHighlight(List<? extends ASTNode> fragments, List<? extends ASTNode> tagProperties) { |
| try { |
| int fs= fragments.size(); |
| String defHighlight= getHighlightHtmlTag(tagProperties); |
| String startDefHighlight = '<' + defHighlight + '>'; |
| String endDefHighlight = "</" + defHighlight + '>'; //$NON-NLS-1$ |
| boolean process = true; |
| if (defHighlight.length() == 0 || !arePropertyValuesStringLiterals(tagProperties)) { |
| process = false; |
| } |
| String regExValue = getPropertyValue("regex", tagProperties); //$NON-NLS-1$ |
| String subStringValue = getPropertyValue("substring", tagProperties); //$NON-NLS-1$ |
| int additionalLength = startDefHighlight.length() + endDefHighlight.length(); |
| Pattern regexPattern = null; |
| if (regExValue != null) { |
| regexPattern = Pattern.compile(regExValue); |
| } |
| if (fs > 0) { |
| for(ASTNode fragment : fragments) { |
| if (fragment instanceof TextElement) { |
| TextElement memberRef= (TextElement) fragment; |
| String modifiedText = memberRef.getText(); |
| if (process) { |
| if (regexPattern != null && process) { |
| Matcher matcher = regexPattern.matcher(modifiedText); |
| StringBuilder strBuild= new StringBuilder(); |
| int finalMatchIndex = 0; |
| while (matcher.find()) { |
| finalMatchIndex = matcher.end(); |
| String replacementStr= startDefHighlight + modifiedText.substring(matcher.start(), matcher.end()) + endDefHighlight; |
| matcher.appendReplacement(strBuild, replacementStr); |
| } |
| modifiedText = strBuild.toString() + modifiedText.substring(finalMatchIndex); |
| } else if (subStringValue != null) { |
| int startIndex = 0; |
| while (true) { |
| startIndex = modifiedText.indexOf(subStringValue, startIndex); |
| if (startIndex == -1) { |
| break; |
| } else { |
| modifiedText = modifiedText.substring(0, startIndex) + startDefHighlight + subStringValue + endDefHighlight + modifiedText.substring(startIndex + subStringValue.length()); |
| startIndex = startIndex + subStringValue.length() + additionalLength; |
| } |
| } |
| } else { |
| modifiedText = startDefHighlight + modifiedText + endDefHighlight; |
| } |
| } |
| fBuf.append(modifiedText); |
| } |
| } |
| } |
| } catch (PatternSyntaxException e) { |
| // do nothing |
| } |
| } |
| |
| private String getHighlightHtmlTag(List<? extends ASTNode> tagProperties) { |
| String defaultTag= "b"; //$NON-NLS-1$ |
| if (tagProperties != null) { |
| for (ASTNode node : tagProperties) { |
| if (node instanceof TagProperty) { |
| TagProperty tagProp = (TagProperty) node; |
| if ("type".equals(tagProp.getName())) { //$NON-NLS-1$ |
| String tagValue = stripQuotes(tagProp.getStringValue()); |
| switch (tagValue) { |
| case "bold" : //$NON-NLS-1$ |
| defaultTag= "b"; //$NON-NLS-1$ |
| break; |
| case "italic" : //$NON-NLS-1$ |
| defaultTag= "i"; //$NON-NLS-1$ |
| break; |
| case "highlighted" : //$NON-NLS-1$ |
| defaultTag= "mark"; //$NON-NLS-1$ |
| break; |
| default : |
| defaultTag= ""; //$NON-NLS-1$ |
| break; |
| } |
| break; |
| } |
| } |
| } |
| } |
| return defaultTag; |
| } |
| |
| private boolean arePropertyValuesStringLiterals(List<? extends ASTNode> tagProperties) { |
| boolean val= true; |
| if (tagProperties != null) { |
| final String SUBSTRING = "substring"; //$NON-NLS-1$ |
| final String REGEX = "regex"; //$NON-NLS-1$ |
| final String TYPE = "type"; //$NON-NLS-1$ |
| for (ASTNode node : tagProperties) { |
| if (node instanceof TagProperty) { |
| TagProperty tagProp = (TagProperty) node; |
| String propName = tagProp.getName(); |
| if (SUBSTRING.equals(propName) |
| || REGEX.equals(propName) |
| || TYPE.equals(propName)) { |
| String value= tagProp.getStringValue(); |
| String changed= stripQuotes(value); |
| if (changed.equals(value)) { |
| val= false; |
| break; |
| } |
| } |
| } |
| } |
| } |
| return val; |
| } |
| private String getPropertyValue(String property, List<? extends ASTNode> tagProperties) { |
| String defaultTag= null; |
| if (tagProperties != null && property!= null) { |
| for (ASTNode node : tagProperties) { |
| if (node instanceof TagProperty) { |
| TagProperty tagProp = (TagProperty) node; |
| if (property.equals(tagProp.getName())) { |
| defaultTag= stripQuotes(tagProp.getStringValue()); |
| break; |
| } |
| } |
| } |
| } |
| return defaultTag; |
| } |
| |
| private String stripQuotes (String str) { |
| String newStr = str; |
| if (str != null && str.length() >= 2) { |
| int length = str.length(); |
| if ((str.charAt(0) == '"' && str.charAt(length-1) == '"') |
| || (str.charAt(0) == '\'' && str.charAt(length-1) == '\'')) { |
| newStr = str.substring(1, length-1); |
| } |
| } |
| return newStr; |
| } |
| |
| private void handleIndex(List<? extends ASTNode> fragments) { |
| int fs= fragments.size(); |
| if (fs > 0) { |
| Object first= fragments.get(0); |
| if (first instanceof TextElement) { |
| TextElement memberRef= (TextElement) first; |
| fBuf.append( memberRef.getText() ); |
| return; |
| } |
| } |
| } |
| |
| private void handleLink(List<? extends ASTNode> fragments) { |
| //TODO: Javadoc shortens type names to minimal length according to context |
| int fs= fragments.size(); |
| if (fs > 0) { |
| Object first= fragments.get(0); |
| String refTypeName= null; |
| String refMemberName= null; |
| String[] refMethodParamTypes= null; |
| String[] refMethodParamNames= null; |
| if (first instanceof Name) { |
| Name name = (Name) first; |
| refTypeName= name.getFullyQualifiedName(); |
| } else if (first instanceof MemberRef) { |
| MemberRef memberRef= (MemberRef) first; |
| Name qualifier= memberRef.getQualifier(); |
| refTypeName= qualifier == null ? "" : qualifier.getFullyQualifiedName(); //$NON-NLS-1$ |
| refMemberName= memberRef.getName().getIdentifier(); |
| } else if (first instanceof MethodRef) { |
| MethodRef methodRef= (MethodRef) first; |
| Name qualifier= methodRef.getQualifier(); |
| refTypeName= qualifier == null ? "" : qualifier.getFullyQualifiedName(); //$NON-NLS-1$ |
| refMemberName= methodRef.getName().getIdentifier(); |
| List<MethodRefParameter> params= methodRef.parameters(); |
| int ps= params.size(); |
| refMethodParamTypes= new String[ps]; |
| refMethodParamNames= new String[ps]; |
| for (int i= 0; i < ps; i++) { |
| MethodRefParameter param= params.get(i); |
| refMethodParamTypes[i]= ASTNodes.asString(param.getType()); |
| SimpleName paramName= param.getName(); |
| if (paramName != null) |
| refMethodParamNames[i]= paramName.getIdentifier(); |
| } |
| } |
| |
| if (refTypeName != null) { |
| fBuf.append("<a href='"); //$NON-NLS-1$ |
| try { |
| String scheme= JavaElementLinks.JAVADOC_SCHEME; |
| String uri= JavaElementLinks.createURI(scheme, fElement, refTypeName, refMemberName, refMethodParamTypes); |
| fBuf.append(uri); |
| } catch (URISyntaxException e) { |
| JavaPlugin.log(e); |
| } |
| fBuf.append("'>"); //$NON-NLS-1$ |
| if (fs > 1 && ((fs != 2) || !isWhitespaceTextElement(fragments.get(1)))) { |
| handleContentElements(fragments.subList(1, fs), true); |
| } else { |
| fBuf.append(refTypeName); |
| if (refMemberName != null) { |
| if (refTypeName.length() > 0) { |
| fBuf.append('.'); |
| } |
| fBuf.append(refMemberName); |
| if (refMethodParamTypes != null) { |
| fBuf.append('('); |
| for (int i= 0; i < refMethodParamTypes.length; i++) { |
| String pType= refMethodParamTypes[i]; |
| fBuf.append(pType); |
| String pName= refMethodParamNames[i]; |
| if (pName != null) { |
| fBuf.append(' ').append(pName); |
| } |
| if (i < refMethodParamTypes.length - 1) { |
| fBuf.append(", "); //$NON-NLS-1$ |
| } |
| } |
| fBuf.append(')'); |
| } |
| } |
| } |
| fBuf.append("</a>"); //$NON-NLS-1$ |
| } else { |
| handleContentElements(fragments); |
| } |
| } |
| } |
| |
| private static boolean isWhitespaceTextElement(Object fragment) { |
| if (!(fragment instanceof TextElement)) |
| return false; |
| |
| TextElement textElement= (TextElement) fragment; |
| return textElement.getText().trim().length() == 0; |
| } |
| |
| private boolean containsOnlyNull(List<String> parameterNames) { |
| for (String string : parameterNames) { |
| if (string != null) |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * @param content HTML content produced by <code>getHTMLContent(...)</code> |
| * @return the baseURL to use for the given content, or <code>null</code> if none |
| * @since 3.10 |
| */ |
| public static String extractBaseURL(String content) { |
| int introStart= content.indexOf(BASE_URL_COMMENT_INTRO); |
| if (introStart != -1) { |
| int introLength= BASE_URL_COMMENT_INTRO.length(); |
| int endIndex= content.indexOf('"', introStart + introLength); |
| if (endIndex != -1) { |
| return content.substring(introStart + introLength, endIndex); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the Javadoc for a PackageDeclaration. |
| * |
| * @param packageDeclaration the Java element whose Javadoc has to be retrieved |
| * @return the package documentation in HTML format or <code>null</code> if there is no |
| * associated Javadoc |
| * @throws CoreException if the Java element does not exists or an exception occurs while |
| * accessing the file containing the package Javadoc |
| * @since 3.9 |
| */ |
| public static String getHTMLContent(IPackageDeclaration packageDeclaration) throws CoreException { |
| IJavaElement element= packageDeclaration.getAncestor(IJavaElement.PACKAGE_FRAGMENT); |
| if (element instanceof IPackageFragment) { |
| return getHTMLContent((IPackageFragment) element); |
| } |
| return null; |
| } |
| |
| |
| /** |
| * Returns the Javadoc for a package which could be present in package.html, package-info.java |
| * or from an attached Javadoc. |
| * |
| * @param packageFragment the package which is requesting for the document |
| * @return the document content in HTML format or <code>null</code> if there is no associated |
| * Javadoc |
| * @throws CoreException if the Java element does not exists or an exception occurs while |
| * accessing the file containing the package Javadoc |
| * @since 3.9 |
| */ |
| public static String getHTMLContent(IPackageFragment packageFragment) throws CoreException { |
| IPackageFragmentRoot root= (IPackageFragmentRoot) packageFragment.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); |
| |
| //1==> Handle the case when the documentation is present in package-info.java or package-info.class file |
| ITypeRoot packageInfo; |
| boolean isBinary= root.getKind() == IPackageFragmentRoot.K_BINARY; |
| if (isBinary) { |
| packageInfo= packageFragment.getClassFile(JavaModelUtil.PACKAGE_INFO_CLASS); |
| } else { |
| packageInfo= packageFragment.getCompilationUnit(JavaModelUtil.PACKAGE_INFO_JAVA); |
| } |
| if (packageInfo != null && packageInfo.exists()) { |
| String cuSource= packageInfo.getSource(); |
| //the source can be null for some of the class files |
| if (cuSource != null) { |
| Javadoc packageJavadocNode= getPackageJavadocNode(packageFragment, cuSource); |
| if (packageJavadocNode != null) { |
| IJavaElement element; |
| if (isBinary) { |
| element= ((IOrdinaryClassFile) packageInfo).getType(); |
| } else { |
| element= packageInfo.getParent(); // parent is the IPackageFragment |
| } |
| return new JavadocContentAccess2(element, packageJavadocNode, cuSource).toHTML(); |
| } |
| } |
| } |
| |
| // 2==> Handle the case when the documentation is done in package.html file. The file can be either in normal source folder or coming from a jar file |
| else { |
| Object[] nonJavaResources= packageFragment.getNonJavaResources(); |
| // 2.1 ==>If the package.html file is present in the source or directly in the binary jar |
| for (Object nonJavaResource : nonJavaResources) { |
| if (nonJavaResource instanceof IFile) { |
| IFile iFile= (IFile) nonJavaResource; |
| if (iFile.exists() && JavaModelUtil.PACKAGE_HTML.equals(iFile.getName())) { |
| return getIFileContent(iFile); |
| } |
| } |
| } |
| |
| // 2.2==>The file is present in a binary container |
| if (isBinary) { |
| for (Object nonJavaResource : nonJavaResources) { |
| // The content is from an external binary class folder |
| if (nonJavaResource instanceof IJarEntryResource) { |
| IJarEntryResource jarEntryResource= (IJarEntryResource) nonJavaResource; |
| String encoding= getSourceAttachmentEncoding(root); |
| if (JavaModelUtil.PACKAGE_HTML.equals(jarEntryResource.getName()) && jarEntryResource.isFile()) { |
| return getHTMLContent(jarEntryResource, encoding); |
| } |
| } |
| } |
| //2.3 ==>The file is present in the source attachment path. |
| String contents= getHTMLContentFromAttachedSource(root, packageFragment); |
| if (contents != null) |
| return contents; |
| } |
| } |
| |
| //3==> Handle the case when the documentation is coming from the attached Javadoc |
| if ((root.isArchive() || root.isExternal())) { |
| return packageFragment.getAttachedJavadoc(null); |
| |
| } |
| |
| return null; |
| } |
| |
| private static String getHTMLContent(IJarEntryResource jarEntryResource, String encoding) throws CoreException { |
| InputStream in= jarEntryResource.getContents(); |
| try { |
| return getContentsFromInputStream(in, encoding); |
| } finally { |
| if (in != null) { |
| try { |
| in.close(); |
| } catch (IOException e) { |
| //ignore |
| } |
| } |
| } |
| } |
| |
| private static String getHTMLContentFromAttachedSource(IPackageFragmentRoot root, IPackageFragment packageFragment) throws CoreException { |
| String filePath= packageFragment.getElementName().replace('.', '/') + '/' + JavaModelUtil.PACKAGE_INFO_JAVA; |
| String contents= getFileContentFromAttachedSource(root, filePath); |
| if (contents != null) { |
| Javadoc packageJavadocNode= getPackageJavadocNode(packageFragment, contents); |
| if (packageJavadocNode != null) |
| return new JavadocContentAccess2(packageFragment, packageJavadocNode, contents).toHTML(); |
| |
| } |
| filePath= packageFragment.getElementName().replace('.', '/') + '/' + JavaModelUtil.PACKAGE_HTML; |
| return getFileContentFromAttachedSource(root, filePath); |
| } |
| |
| @SuppressWarnings("resource") |
| private static String getFileContentFromAttachedSource(IPackageFragmentRoot root, String filePath) throws CoreException { |
| IPath sourceAttachmentPath= root.getSourceAttachmentPath(); |
| if (sourceAttachmentPath != null) { |
| File file= null ; |
| String encoding= null; |
| |
| if (sourceAttachmentPath.getDevice() == null) { |
| //the path could be a workspace relative path to a zip or to the source folder |
| IWorkspaceRoot wsRoot= ResourcesPlugin.getWorkspace().getRoot(); |
| IResource res= wsRoot.findMember(sourceAttachmentPath); |
| |
| if (res instanceof IFile) { |
| // zip in the workspace |
| IPath location= res.getLocation(); |
| if (location == null) |
| return null; |
| file= location.toFile(); |
| encoding= ((IFile) res).getCharset(false); |
| |
| } else if (res instanceof IContainer) { |
| // folder in the workspace |
| res= ((IContainer) res).findMember(filePath); |
| if (!(res instanceof IFile)) |
| return null; |
| encoding= ((IFile) res).getCharset(false); |
| if (encoding == null) |
| encoding= getSourceAttachmentEncoding(root); |
| return getContentsFromInputStream(((IFile) res).getContents(), encoding); |
| } |
| } |
| |
| if (file == null || !file.exists()) |
| file= sourceAttachmentPath.toFile(); |
| |
| if (file.isDirectory()) { |
| //the path is an absolute filesystem path to the source folder |
| IPath packagedocPath= sourceAttachmentPath.append(filePath); |
| if (packagedocPath.toFile().exists()) |
| return getFileContent(packagedocPath.toFile()); |
| |
| } else if (file.exists()) { |
| //the package documentation is in a Jar/Zip |
| IPath sourceAttachmentRootPath= root.getSourceAttachmentRootPath(); |
| String packagedocPath; |
| //consider the root path also in the search path if it exists |
| if (sourceAttachmentRootPath != null) { |
| packagedocPath= sourceAttachmentRootPath.append(filePath).toString(); |
| } else { |
| packagedocPath= filePath; |
| } |
| ZipFile zipFile= null; |
| InputStream in= null; |
| try { |
| zipFile= new ZipFile(file, ZipFile.OPEN_READ); |
| ZipEntry packagedocFile= zipFile.getEntry(packagedocPath); |
| if (packagedocFile != null) { |
| in= zipFile.getInputStream(packagedocFile); |
| if (encoding == null) |
| encoding= getSourceAttachmentEncoding(root); |
| return getContentsFromInputStream(in, encoding); |
| } |
| } catch (IOException e) { |
| throw new CoreException(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), e.getMessage(), e)); |
| } finally { |
| try { |
| if (in != null) { |
| in.close(); |
| } |
| } catch (IOException e) { |
| //ignore |
| } |
| try { |
| if (zipFile != null) { |
| zipFile.close();//this will close the InputStream also |
| } |
| } catch (IOException e) { |
| //ignore |
| } |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| |
| private static String getContentsFromInputStream(InputStream in, String encoding) throws CoreException { |
| final int defaultFileSize= 15 * 1024; |
| StringBuilder buffer= new StringBuilder(defaultFileSize); |
| Reader reader= null; |
| |
| try { |
| reader= new BufferedReader(new InputStreamReader(in, encoding), defaultFileSize); |
| |
| char[] readBuffer= new char[2048]; |
| int charCount= reader.read(readBuffer); |
| |
| while (charCount > 0) { |
| buffer.append(readBuffer, 0, charCount); |
| charCount= reader.read(readBuffer); |
| } |
| |
| } catch (IOException e) { |
| throw new CoreException(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), e.getMessage(), e)); |
| } finally { |
| try { |
| if (reader != null) { |
| reader.close();//this will also close the InputStream wrapped in the reader |
| } |
| } catch (IOException e) { |
| //ignore |
| } |
| } |
| return buffer.toString(); |
| } |
| |
| |
| private static String getSourceAttachmentEncoding(IPackageFragmentRoot root) throws JavaModelException { |
| String encoding= ResourcesPlugin.getEncoding(); |
| IClasspathEntry entry= root.getRawClasspathEntry(); |
| |
| if (entry != null) { |
| int kind= entry.getEntryKind(); |
| if (kind == IClasspathEntry.CPE_LIBRARY || kind == IClasspathEntry.CPE_VARIABLE) { |
| for (IClasspathAttribute attrib : entry.getExtraAttributes()) { |
| if (IClasspathAttribute.SOURCE_ATTACHMENT_ENCODING.equals(attrib.getName())) { |
| return attrib.getValue(); |
| } |
| } |
| } |
| } |
| |
| return encoding; |
| } |
| |
| /** |
| * Reads the content of the IFile. |
| * |
| * @param file the file whose content has to be read |
| * @return the content of the file |
| * @throws CoreException if the file could not be successfully connected or disconnected |
| */ |
| private static String getIFileContent(IFile file) throws CoreException { |
| String content= null; |
| ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager(); |
| IPath fullPath= file.getFullPath(); |
| manager.connect(fullPath, LocationKind.IFILE, null); |
| try { |
| ITextFileBuffer buffer= manager.getTextFileBuffer(fullPath, LocationKind.IFILE); |
| if (buffer != null) { |
| content= buffer.getDocument().get(); |
| } |
| } finally { |
| manager.disconnect(fullPath, LocationKind.IFILE, null); |
| } |
| |
| return content; |
| } |
| |
| |
| /** |
| * Reads the content of the java.io.File. |
| * |
| * @param file the file whose content has to be read |
| * @return the content of the file |
| * @throws CoreException if the file could not be successfully connected or disconnected |
| */ |
| private static String getFileContent(File file) throws CoreException { |
| String content= null; |
| ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager(); |
| |
| IPath fullPath= new Path(file.getAbsolutePath()); |
| manager.connect(fullPath, LocationKind.LOCATION, null); |
| try { |
| ITextFileBuffer buffer= manager.getTextFileBuffer(fullPath, LocationKind.LOCATION); |
| if (buffer != null) { |
| content= buffer.getDocument().get(); |
| } |
| } finally { |
| manager.disconnect(fullPath, LocationKind.LOCATION, null); |
| } |
| return content; |
| } |
| |
| } |