| /******************************************************************************* |
| * Copyright (c) 2000, 2011 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.wst.jsdt.ui; |
| |
| import java.io.IOException; |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExtension; |
| import org.eclipse.core.runtime.IExtensionPoint; |
| import org.eclipse.core.runtime.IExtensionRegistry; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.wst.jsdt.core.IBuffer; |
| import org.eclipse.wst.jsdt.core.IFunction; |
| import org.eclipse.wst.jsdt.core.IJavaScriptElement; |
| import org.eclipse.wst.jsdt.core.ILocalVariable; |
| import org.eclipse.wst.jsdt.core.IMember; |
| import org.eclipse.wst.jsdt.core.IOpenable; |
| import org.eclipse.wst.jsdt.core.ISourceRange; |
| import org.eclipse.wst.jsdt.core.IType; |
| import org.eclipse.wst.jsdt.core.ITypeHierarchy; |
| import org.eclipse.wst.jsdt.core.JavaScriptModelException; |
| import org.eclipse.wst.jsdt.core.util.SequenceReader; |
| import org.eclipse.wst.jsdt.internal.core.MetadataFile; |
| import org.eclipse.wst.jsdt.internal.corext.javadoc.JavaDocCommentReader; |
| import org.eclipse.wst.jsdt.internal.corext.util.MethodOverrideTester; |
| import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin; |
| import org.eclipse.wst.jsdt.internal.ui.Logger; |
| import org.eclipse.wst.jsdt.internal.ui.text.javadoc.JavaDoc2HTMLTextReader; |
| import org.eclipse.wst.jsdt.internal.ui.text.javadoc.OAADocReader; |
| |
| /** |
| * Helper needed to get the content of a Javadoc comment. |
| * |
| * <p> |
| * This class is not intended to be subclassed or instantiated by clients. |
| * </p> |
| * |
| * Provisional API: This class/interface is part of an interim API that is still under development and expected to |
| * change significantly before reaching stability. It is being made available at this early stage to solicit feedback |
| * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken |
| * (repeatedly) as the API evolves. |
| */ |
| public class JSdocContentAccess { |
| |
| public static final String EXTENSION_POINT= "documentationProvider"; //$NON-NLS-1$ |
| |
| protected static final String TAG_DOCUMENTATIONPROVIDER = "documentationProvider"; //$NON-NLS-1$ |
| protected static final String ATTR_DOCUMENTATIONPROVIDER_CLASS = "class"; //$NON-NLS-1$ |
| |
| private static IDocumentationReader[] docReaders; |
| |
| private JSdocContentAccess() { |
| // do not instantiate |
| } |
| |
| /** |
| * <p>Gets the content reader for either an {@link IMember} or {@link ILocalVariable}.</p> |
| * |
| * @param element {@link IJavaScriptElement} to get the content reader for |
| * @param allowInherited For methods with no (Javadoc) comment, the comment of the overridden class |
| * is returned if <code>allowInherited</code> is <code>true</code>. |
| * |
| * @return a reader for the Javadoc comment content or <code>null</code> if the element |
| * does not contain a Javadoc comment or if no source is available |
| * |
| * @throws JavaScriptModelException is thrown when the elements javadoc can not be accessed |
| * |
| * @see #getContentReader(ILocalVariable, boolean) |
| * @see #getContentReader(IMember, boolean) |
| */ |
| public static Reader getContentReader(IJavaScriptElement element, boolean allowInherited) throws JavaScriptModelException { |
| Reader reader = null; |
| |
| if(element instanceof IMember) { |
| reader = getContentReader((IMember)element, allowInherited); |
| } else if(element instanceof ILocalVariable) { |
| reader = getContentReader((ILocalVariable)element, allowInherited); |
| } |
| |
| return reader; |
| } |
| |
| /** |
| * Gets a reader for an IMember's Javadoc comment content from the source attachment. |
| * The content does contain only the text from the comment without the Javadoc leading star characters. |
| * Returns <code>null</code> if the member does not contain a Javadoc comment or if no source is available. |
| * @param member The member to get the Javadoc of. |
| * @param allowInherited For methods with no (Javadoc) comment, the comment of the overridden class |
| * is returned if <code>allowInherited</code> is <code>true</code>. |
| * @return Returns a reader for the Javadoc comment content or <code>null</code> if the member |
| * does not contain a Javadoc comment or if no source is available |
| * @throws JavaScriptModelException is thrown when the elements javadoc can not be accessed |
| */ |
| public static Reader getContentReader(IMember member, boolean allowInherited) throws JavaScriptModelException { |
| List readers = new ArrayList(2); |
| IDocumentationReader[] docReaders = getDocReaders(member); |
| for (int i = 0; i < docReaders.length; i++) { |
| Reader contentReader = docReaders[i].getContentReader(member, allowInherited); |
| if(contentReader != null) { |
| readers.add(contentReader); |
| } |
| } |
| |
| IOpenable openable = member.getOpenable(); |
| if (openable instanceof MetadataFile) |
| { |
| return new OAADocReader((MetadataFile)openable, member); |
| } |
| |
| IBuffer buf= openable.getBuffer(); |
| if (buf != null) { |
| try { |
| // source or attachment found |
| ISourceRange jsDocRange = member.getJSdocRange(); |
| if (jsDocRange == null && member.getElementType() == IJavaScriptElement.TYPE) { |
| IFunction constructor = ((IType) member).getFunction(member.getElementName(), null); |
| if (constructor.exists()) { |
| jsDocRange = constructor.getJSdocRange(); |
| } |
| } |
| if (jsDocRange != null) { |
| JavaDocCommentReader reader = new JavaDocCommentReader(buf, jsDocRange.getOffset(), jsDocRange.getOffset() + jsDocRange.getLength() - 1); |
| if (!containsOnlyInheritDoc(reader, jsDocRange.getLength())) { |
| reader.reset(); |
| readers.add(reader); |
| } |
| else if (allowInherited && (member.getElementType() == IJavaScriptElement.METHOD)) { |
| Reader hierarchyDocReader = findDocInHierarchy((IFunction) member); |
| if (hierarchyDocReader != null) |
| readers.add(hierarchyDocReader); |
| } |
| } |
| } |
| catch (JavaScriptModelException e) { |
| Logger.logException(Logger.ERROR_DEBUG, e); |
| } |
| } |
| |
| if (!readers.isEmpty()) { |
| if (readers.size() == 1) |
| return (Reader) readers.get(0); |
| return new SequenceReader((Reader[]) readers.toArray(new Reader[readers.size()])); |
| } |
| return null; |
| } |
| |
| /** |
| * Gets a reader for an ILocalDeclaration's doc comment content from the |
| * source attachment. Returns <code>null</code> if the declaration does |
| * not have a doc comment or if no source is available. |
| * |
| * @param declaration |
| * The declaration to get the doc of. |
| * @param allowInherited |
| * For methods with no doc comment, the comment of the |
| * overridden class is returned if <code>allowInherited</code> |
| * is <code>true</code> and this is an argument. |
| * @return Returns a reader for the doc comment content or |
| * <code>null</code> if the declaration does not contain a doc |
| * comment or if no source is available |
| * @throws JavaScriptModelException |
| * is thrown when the declaration's doc can not be accessed |
| */ |
| public static Reader getContentReader(ILocalVariable declaration, boolean allowInherited) throws JavaScriptModelException { |
| List readers = new ArrayList(2); |
| IDocumentationReader[] docReaders = getDocReaders(declaration); |
| for (int i = 0; i < docReaders.length; i++) { |
| Reader contentReader = docReaders[i].getContentReader(declaration, allowInherited); |
| if (contentReader != null) { |
| readers.add(contentReader); |
| } |
| } |
| |
| if (!readers.isEmpty()) { |
| if (readers.size() == 1) |
| return (Reader) readers.get(0); |
| return new SequenceReader((Reader[]) readers.toArray(new Reader[readers.size()])); |
| } |
| IOpenable openable = declaration.getOpenable(); |
| if (!(openable instanceof MetadataFile)) { |
| IBuffer buf = openable.getBuffer(); |
| JavaDocCommentReader r = new JavaDocCommentReader(buf, declaration.getNameRange().getOffset() - 1); |
| if(r.getOffset() != declaration.getNameRange().getOffset() - 1) { |
| return r; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Checks whether the given reader only returns |
| * the inheritDoc tag. |
| * |
| * @param reader the reader |
| * @param length the length of the underlying content |
| * @return <code>true</code> if the reader only returns the inheritDoc tag |
| * |
| */ |
| private static boolean containsOnlyInheritDoc(Reader reader, int length) { |
| char[] content= new char[length]; |
| try { |
| reader.read(content, 0, length); |
| } catch (IOException e) { |
| return false; |
| } |
| return new String(content).trim().equals("{@inheritDoc}"); //$NON-NLS-1$ |
| |
| } |
| |
| /** |
| * <p>Gets the HTML content reader for either an {@link IMember} or {@link ILocalVariable}.</p> |
| * |
| * @param element {@link IJavaScriptElement} to get the Javadoc of |
| * @param allowInherited for methods with no (Javadoc) comment, the comment of the overridden |
| * class is returned if <code>allowInherited</code> is <code>true</code> |
| * @param useAttachedJavadoc if <code>true</code> Javadoc will be extracted from attached Javadoc |
| * if there's no source |
| * |
| * @return a reader for the Javadoc comment content in HTML or <code>null</code> if the element |
| * does not contain a Javadoc comment or if no source is available |
| * |
| * @throws JavaScriptModelException is thrown when the elements Javadoc can not be accessed |
| * |
| * @see #getHTMLContentReader(ILocalVariable, boolean, boolean) |
| * @see #getHTMLContentReader(IMember, boolean, boolean) |
| */ |
| public static Reader getHTMLContentReader(IJavaScriptElement element, boolean allowInherited, boolean useAttachedJavadoc) throws JavaScriptModelException { |
| Reader reader = null; |
| |
| if(element instanceof IMember) { |
| reader = getHTMLContentReader((IMember)element, allowInherited, useAttachedJavadoc); |
| } else if(element instanceof ILocalVariable) { |
| reader = getHTMLContentReader((ILocalVariable)element, allowInherited, useAttachedJavadoc); |
| } |
| |
| return reader; |
| } |
| |
| /** |
| * Gets a reader for an IMember's Javadoc comment content from the source attachment. |
| * and renders the tags in HTML. |
| * Returns <code>null</code> if the member does not contain a Javadoc comment or if no source is available. |
| * |
| * @param member the member to get the Javadoc of. |
| * @param allowInherited for methods with no (Javadoc) comment, the comment of the overridden |
| * class is returned if <code>allowInherited</code> is <code>true</code> |
| * @param useAttachedJavadoc if <code>true</code> Javadoc will be extracted from attached Javadoc |
| * if there's no source |
| * @return a reader for the Javadoc comment content in HTML or <code>null</code> if the member |
| * does not contain a Javadoc comment or if no source is available |
| * @throws JavaScriptModelException is thrown when the elements Javadoc can not be accessed |
| * |
| */ |
| public static Reader getHTMLContentReader(IMember member, boolean allowInherited, boolean useAttachedJavadoc) throws JavaScriptModelException { |
| Reader contentReader= getContentReader(member, allowInherited); |
| if (contentReader != null) |
| { |
| IDocumentationReader[] docReaders = getDocReaders(member); |
| if (docReaders.length > 0) { |
| List htmlReaders = new ArrayList(docReaders.length); |
| for (int i = 0; i < docReaders.length; i++) { |
| Reader htmlReader = docReaders[i].getDocumentation2HTMLReader(contentReader); |
| if (htmlReader != null) { |
| htmlReaders.add(htmlReader); |
| } |
| } |
| /* return any and all HTML readers in sequence */ |
| if (!htmlReaders.isEmpty()) { |
| // htmlReaders.add(/*0, */new JavaDoc2HTMLTextReader(contentReader)); |
| return new SequenceReader((Reader[]) htmlReaders.toArray(new Reader[htmlReaders.size()])); |
| } |
| } |
| return new JavaDoc2HTMLTextReader(contentReader); |
| } |
| |
| if (useAttachedJavadoc && member.getOpenable().getBuffer() == null) { // only if no source available |
| String s= member.getAttachedJavadoc(null); |
| if (s != null) |
| return new StringReader(s); |
| } |
| return null; |
| } |
| |
| /** |
| * Gets a reader for an ILocalDeclaration documentation comment content. |
| * and renders the tags in HTML. |
| * Returns <code>null</code> if the declaration does not contain a doc comment or if no source is available. |
| * |
| * @param variable the variable declaration to get the doc of. |
| * @param allowInherited for methods with no (JSDoc) comment, the comment of the overridden |
| * class is returned if <code>allowInherited</code> is <code>true</code> |
| * @param useAttachedDoc if <code>true</code> JSDoc will be extracted from attached JSDoc |
| * if there's no source |
| * @return a reader for the JSDoc comment content in HTML or <code>null</code> if the member |
| * does not contain a JSDoc comment or if no source is available |
| * @throws JavaScriptModelException is thrown when the elements JSDoc can not be accessed |
| * |
| */ |
| public static Reader getHTMLContentReader(ILocalVariable variable, boolean allowInherited, boolean useAttachedDoc) throws JavaScriptModelException { |
| Reader contentReader= getContentReader(variable, allowInherited); |
| if (contentReader != null) { |
| IDocumentationReader[] docReaders = getDocReaders(variable); |
| if (docReaders.length > 0) { |
| List htmlReaders = new ArrayList(docReaders.length); |
| for (int i = 0; i < docReaders.length; i++) { |
| Reader documentation2htmlReader = docReaders[i].getDocumentation2HTMLReader(contentReader); |
| if (documentation2htmlReader != null) { |
| htmlReaders.add(documentation2htmlReader); |
| } |
| } |
| if (!htmlReaders.isEmpty()) { |
| htmlReaders.add(/*0, */new JavaDoc2HTMLTextReader(contentReader)); |
| return new SequenceReader((Reader[]) htmlReaders.toArray(new Reader[htmlReaders.size()])); |
| } |
| } |
| return new JavaDoc2HTMLTextReader(contentReader); |
| } |
| |
| return null; |
| } |
| |
| private static Reader findDocInHierarchy(IFunction method) throws JavaScriptModelException { |
| IType type= method.getDeclaringType(); |
| if (type==null) |
| return null; |
| ITypeHierarchy hierarchy= type.newSupertypeHierarchy(null); |
| |
| MethodOverrideTester tester= new MethodOverrideTester(type, hierarchy); |
| |
| IType[] superTypes= hierarchy.getAllSuperclasses(type); |
| for (int i= 0; i < superTypes.length; i++) { |
| IType curr= superTypes[i]; |
| IFunction overridden= tester.findOverriddenMethodInType(curr, method); |
| if (overridden != null) { |
| Reader reader= getContentReader(overridden, false); |
| if (reader != null) { |
| return reader; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private static IDocumentationReader[] getDocReaders(IMember member) |
| { |
| if (docReaders==null) |
| loadExtensions(); |
| List readers = new ArrayList(docReaders.length); |
| for (int i = 0; i < docReaders.length; i++) { |
| if (docReaders[i].appliesTo(member)) { |
| readers.add(docReaders[i]); |
| } |
| } |
| return (IDocumentationReader[]) readers.toArray(new IDocumentationReader[readers.size()]); |
| } |
| |
| private static IDocumentationReader[] getDocReaders(ILocalVariable declaration) |
| { |
| if (docReaders==null) |
| loadExtensions(); |
| List readers = new ArrayList(docReaders.length); |
| for (int i = 0; i < docReaders.length; i++) { |
| if (docReaders[i].appliesTo(declaration)) { |
| readers.add(docReaders[i]); |
| } |
| } |
| return (IDocumentationReader[]) readers.toArray(new IDocumentationReader[readers.size()]); |
| } |
| |
| |
| private static void loadExtensions() { |
| IExtensionRegistry registry = Platform.getExtensionRegistry(); |
| ArrayList extList = new ArrayList(); |
| if (registry != null) { |
| IExtensionPoint point = registry.getExtensionPoint( |
| JavaScriptPlugin.getPluginId(), EXTENSION_POINT); |
| |
| if (point != null) { |
| IExtension[] extensions = point.getExtensions(); |
| for (int i = 0; i < extensions.length; i++) { |
| IConfigurationElement[] elements = extensions[i] |
| .getConfigurationElements(); |
| for (int j = 0; j < elements.length; j++) { |
| try { |
| IDocumentationReader docProvider = null; |
| if (elements[j].getName().equals(TAG_DOCUMENTATIONPROVIDER)) { |
| docProvider = (IDocumentationReader) elements[j] |
| .createExecutableExtension(ATTR_DOCUMENTATIONPROVIDER_CLASS); |
| } |
| |
| extList.add(docProvider); |
| } catch (CoreException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| } |
| } |
| |
| docReaders = (IDocumentationReader[]) extList |
| .toArray(new IDocumentationReader[extList.size()]); |
| } |
| |
| |
| |
| } |