blob: 2371c5f449ec73c42106d4417148117f832d90e1 [file] [log] [blame]
/*******************************************************************************
* 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()]);
}
}