/*******************************************************************************
* Copyright (c) 2009, 2013, 2014 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Zend Technologies
* Dawid Pakuła [418890]
*******************************************************************************/
package org.eclipse.php.internal.ui.documentation;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.dltk.ast.references.SimpleReference;
import org.eclipse.dltk.ast.references.TypeReference;
import org.eclipse.dltk.ast.references.VariableReference;
import org.eclipse.dltk.core.*;
import org.eclipse.dltk.internal.core.util.MethodOverrideTester;
import org.eclipse.dltk.ui.ScriptElementLabels;
import org.eclipse.php.core.compiler.PHPFlags;
import org.eclipse.php.internal.core.ast.nodes.Identifier;
import org.eclipse.php.internal.core.compiler.ast.nodes.PHPDocBlock;
import org.eclipse.php.internal.core.compiler.ast.nodes.PHPDocTag;
import org.eclipse.php.internal.core.typeinference.FakeConstructor;
import org.eclipse.php.internal.core.typeinference.PHPModelUtils;
import org.eclipse.php.internal.core.util.MagicMemberUtil;
import org.eclipse.php.internal.core.util.MagicMemberUtil.MagicField;
import org.eclipse.php.internal.core.util.MagicMemberUtil.MagicMember;
import org.eclipse.php.internal.core.util.MagicMemberUtil.MagicMethod;
import org.eclipse.php.internal.ui.PHPUiPlugin;
import org.eclipse.php.internal.ui.corext.util.SuperTypeHierarchyCache;
import org.eclipse.text.edits.ReplaceEdit;
/**
* 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
*/
@SuppressWarnings({ "restriction", "unchecked", "rawtypes" })
public class PHPDocumentationContentAccess {
private static final Pattern INLINE_LINK_PATTERN = Pattern
.compile("\\{@link[\\s]+[^\\}]*\\}"); //$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$
private static final String BlOCK_TAG_ENTRY_START = "<dd>"; //$NON-NLS-1$
private 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$
private static final int PARAMETER_TYPE_TYPE = 1;
private static final int PARAMETER_NAME_TYPE = 2;
private static final int PARAMETER_DESCRIPTION_TYPE = 3;
/**
* Implements the "Algorithm for Inheriting Method Comments" as specified
* for <a href=
* "http://java.sun.com/j2se/1.4.2/docs/tooldocs/solaris/javadoc.html#inheritingcomments"
* >1.4.2</a>, <a href=
* "http://java.sun.com/j2se/1.5.0/docs/tooldocs/windows/javadoc.html#inheritingcomments"
* >1.5</a>, and <a href=
* "http://java.sun.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() {
public String toString() {
return "STOP_BRANCH";} //$NON-NLS-1$
};
public static final Object CONTINUE = new Object() {
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 ModelException
* unexpected problem
* @see #visitInheritDoc(IType, ITypeHierarchy)
*/
public abstract Object visit(IType currType) throws ModelException;
/**
* 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 ModelException
* unexpected problem
*/
public Object visitInheritDoc(IType currentType,
ITypeHierarchy typeHierarchy) throws ModelException {
ArrayList visited = new ArrayList();
visited.add(currentType);
// Object result = visitInheritDocInterfaces(visited, currentType,
// typeHierarchy);
// if (result != InheritDocVisitor.CONTINUE)
// return result;
Object result;
IType[] superClasses = typeHierarchy.getSuperclass(currentType);
for (IType superClass : superClasses) {
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
superClasses = 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 ModelException
* unexpected problem
*/
private Object visitInheritDocInterfaces(ArrayList visited,
IType currentType, ITypeHierarchy typeHierarchy)
throws ModelException {
ArrayList toVisitChildren = new ArrayList();
IType[] superInterfaces = typeHierarchy.getSuperclass(currentType);
for (int i = 0; i < superInterfaces.length; i++) {
IType superInterface = superInterfaces[i];
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 (Iterator iter = toVisitChildren.iterator(); iter.hasNext();) {
IType child = (IType) iter.next();
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) {
public CharSequence getInheritedMainDescription(IMethod method) {
return null;
}
public CharSequence getInheritedParamDescription(IMethod method,
int i) {
return null;
}
public CharSequence getInheritedReturnDescription(IMethod method) {
return null;
}
public CharSequence getInheritedExceptionDescription(
IMethod method, String name) {
return null;
}
};
private static 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 ModelException
* unexpected problem
*/
CharSequence getDescription(
PHPDocumentationContentAccess contentAccess)
throws ModelException;
}
private final IType fStartingType;
private final HashMap 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, new DescriptionGetter() {
public CharSequence getDescription(
PHPDocumentationContentAccess contentAccess) {
return contentAccess.getMainDescription();
}
});
}
/**
* 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, new DescriptionGetter() {
public CharSequence getDescription(
PHPDocumentationContentAccess contentAccess)
throws ModelException {
return contentAccess
.getInheritedParamDescription(paramIndex);
}
});
}
/**
* 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 getInheritedParamType(IMethod method,
final int paramIndex) {
return getInheritedDescription(method, new DescriptionGetter() {
public CharSequence getDescription(
PHPDocumentationContentAccess contentAccess)
throws ModelException {
return contentAccess.getInheritedParamType(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, new DescriptionGetter() {
public CharSequence getDescription(
PHPDocumentationContentAccess contentAccess) {
return contentAccess.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, new DescriptionGetter() {
public CharSequence getDescription(
PHPDocumentationContentAccess contentAccess) {
return contentAccess.getExceptionDescription(simpleName);
}
});
}
private CharSequence getInheritedDescription(final IMethod method,
final DescriptionGetter descriptionGetter) {
try {
return (CharSequence) new InheritDocVisitor() {
public Object visit(IType currType) throws ModelException {
IMethod overridden = getOverrideTester()
.findOverriddenMethodInType(currType, method);
if (overridden == null)
return InheritDocVisitor.CONTINUE;
PHPDocumentationContentAccess contentAccess = getJavadocContentAccess(overridden);
if (contentAccess == null) {
// if (overridden.getOpenable().getBuffer() == null)
// {
// return InheritDocVisitor.CONTINUE;
// } else {
// return InheritDocVisitor.CONTINUE;
// }
return InheritDocVisitor.CONTINUE;
}
CharSequence overriddenDescription = descriptionGetter
.getDescription(contentAccess);
if (overriddenDescription != null)
return overriddenDescription;
else
return InheritDocVisitor.CONTINUE;
}
}.visitInheritDoc(method.getDeclaringType(), getTypeHierarchy());
} catch (ModelException e) {
PHPUiPlugin.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 ModelException
* unexpected problem
*/
private PHPDocumentationContentAccess getJavadocContentAccess(
IMethod method) throws ModelException {
Object cached = fContentAccesses.get(method);
if (cached != null)
return (PHPDocumentationContentAccess) 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;
// }
PHPDocBlock javadoc = PHPModelUtils.getDocBlock(method);
if (javadoc == null) {
fContentAccesses.put(method, null);
return null;
}
PHPDocumentationContentAccess contentAccess = new PHPDocumentationContentAccess(
method, javadoc, this);
fContentAccesses.put(method, contentAccess);
return contentAccess;
}
private ITypeHierarchy getTypeHierarchy() throws ModelException {
if (fTypeHierarchy == null)
fTypeHierarchy = SuperTypeHierarchyCache
.getTypeHierarchy(fStartingType);
return fTypeHierarchy;
}
private MethodOverrideTester getOverrideTester() throws ModelException {
if (fOverrideTester == null)
fOverrideTester = SuperTypeHierarchyCache
.getMethodOverrideTester(fStartingType);
return fOverrideTester;
}
}
private final IMember fMember;
/**
* The method, or <code>null</code> if {@link #fMember} is not a method
* where {@inheritDoc} could work.
*/
private final IMethod fMethod;
private final PHPDocBlock fJavadoc;
private final JavadocLookup fJavadocLookup;
private StringBuffer fBuf;
private StringBuffer fMainDescription;
private StringBuffer fReturnDescription;
private StringBuffer[] fParamDescriptions;
private StringBuffer[] fParamTypes;
private HashMap<String, StringBuffer> fExceptionDescriptions;
private PHPDocumentationContentAccess(IMethod method, PHPDocBlock javadoc,
JavadocLookup lookup) {
fMember = method;
fMethod = method;
fJavadoc = javadoc;
fJavadocLookup = lookup;
}
private PHPDocumentationContentAccess(IMember member, PHPDocBlock javadoc) {
fMember = member;
fMethod = null;
fJavadoc = javadoc;
fJavadocLookup = JavadocLookup.NONE;
}
/**
* Gets an IMember's Javadoc comment content from the source or Javadoc
* attachment and renders the tags and links 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 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
* member does not have a Javadoc comment or if no source is
* available
* @throws ModelException
* is thrown when the element's Javadoc can not be accessed
*/
public static String getHTMLContent(IMember member) throws ModelException {
return getHTMLContentFromSource(member);
}
private static StringBuffer createSuperMethodReferences(final IMethod method)
throws ModelException {
IType type = method.getDeclaringType();
ITypeHierarchy hierarchy = SuperTypeHierarchyCache
.getTypeHierarchy(type);
final MethodOverrideTester tester = SuperTypeHierarchyCache
.getMethodOverrideTester(type);
final ArrayList<IMethod> superInterfaceMethods = new ArrayList<IMethod>();
final IMethod[] superClassMethod = { null };
new InheritDocVisitor() {
public Object visit(IType currType) throws ModelException {
IMethod overridden = tester.findOverriddenMethodInType(
currType, method);
if (overridden == null)
return InheritDocVisitor.CONTINUE;
if (PHPFlags.isInterface(currType.getFlags()))
superInterfaceMethods.add(overridden);
else
superClassMethod[0] = overridden;
return STOP_BRANCH;
}
}.visitInheritDoc(type, hierarchy);
boolean hasSuperInterfaceMethods = superInterfaceMethods.size() != 0;
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(PHPDocumentationMessages.JavaDoc2HTMLTextReader_specified_by_section);
buf.append("</b> "); //$NON-NLS-1$
for (Iterator<IMethod> iter = superInterfaceMethods.iterator(); iter
.hasNext();) {
IMethod overridden = (IMethod) iter.next();
buf.append(createMethodInTypeLinks(overridden));
if (iter.hasNext())
buf.append(ScriptElementLabels.COMMA_STRING);
}
}
if (superClassMethod[0] != null) {
if (hasSuperInterfaceMethods)
buf.append(ScriptElementLabels.COMMA_STRING);
buf.append("<b>"); //$NON-NLS-1$
buf.append(PHPDocumentationMessages.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(
PHPDocumentationMessages.JavaDoc2HTMLTextReader_method_in_type,
new Object[] { methodLink, typeLink });
return methodInType;
}
private static CharSequence createSimpleMemberLink(IMember member) {
StringBuffer buf = new StringBuffer();
buf.append("<a href='"); //$NON-NLS-1$
try {
String uri = PHPElementLinks.createURI(
PHPElementLinks.PHPDOC_SCHEME, member);
buf.append(uri);
} catch (URISyntaxException e) {
PHPUiPlugin.log(e);
}
buf.append("'>"); //$NON-NLS-1$
ScriptElementLabels.getDefault().getElementLabel(member, 0, buf);
buf.append("</a>"); //$NON-NLS-1$
return buf;
}
private static String getHTMLContentFromSource(IMember member)
throws ModelException {
return javadoc2HTML(member);
}
private static PHPDocBlock getJavadocNode(IMember member) {
if (member instanceof IType) {
return PHPModelUtils.getDocBlock((IType) member);
}
if (member instanceof IMethod) {
PHPDocBlock result = PHPModelUtils.getDocBlock((IMethod) member);
if (result == null && member instanceof FakeConstructor) {
FakeConstructor fc = (FakeConstructor) member;
result = PHPModelUtils.getDocBlock((IType) fc.getParent());
}
return result;
}
if (member instanceof IField) {
return PHPModelUtils.getDocBlock((IField) member);
}
return null;
}
private static String javadoc2HTML(IMember member) {
PHPDocBlock javadoc = getJavadocNode(member);
if (javadoc == null) {
MagicMember magicMember = getMagicMember(member);
if (magicMember != null) {
return getHTMLForMagicMember(member, magicMember);
}
javadoc = new PHPDocBlock(0, 0, null, null, new PHPDocTag[0]);
}
if (canInheritJavadoc(member)) {
IMethod method = (IMethod) member;
IType declaringType = method.getDeclaringType();
JavadocLookup lookup;
if (declaringType == null) {
lookup = JavadocLookup.NONE;
} else {
lookup = new JavadocLookup(method.getDeclaringType());
}
return new PHPDocumentationContentAccess(method, javadoc, lookup)
.toHTML();
}
return new PHPDocumentationContentAccess(member, javadoc).toHTML();
}
private static String getHTMLForMagicMember(IMember member,
MagicMember magicMember) {
StringBuffer fBuf = new StringBuffer();
if (appendBuiltinDoc(member, fBuf)) {
return fBuf.toString();
}
if (magicMember instanceof MagicField) {
MagicField magicField = (MagicField) magicMember;
fBuf.append(magicField.desc);
fBuf.append(BLOCK_TAG_START);
fBuf.append("<dt>"); //$NON-NLS-1$
fBuf.append("Type"); //$NON-NLS-1$
fBuf.append("</dt>"); //$NON-NLS-1$
fBuf.append(BlOCK_TAG_ENTRY_START);
fBuf.append(" "); //$NON-NLS-1$
fBuf.append(magicField.type);
fBuf.append(BlOCK_TAG_ENTRY_END);
fBuf.append(BLOCK_TAG_END);
} else {
MagicMethod magicMethod = (MagicMethod) magicMember;
fBuf.append(magicMethod.desc);
fBuf.append(BLOCK_TAG_START);
if (magicMethod.parameterNames != null
&& magicMethod.parameterNames.length > 0) {
fBuf.append("<dt>"); //$NON-NLS-1$
fBuf.append(PHPDocumentationMessages.JavaDoc2HTMLTextReader_parameters_section);
fBuf.append("</dt>"); //$NON-NLS-1$
for (int i = 0; i < magicMethod.parameterNames.length; i++) {
fBuf.append(BlOCK_TAG_ENTRY_START);
fBuf.append(" "); //$NON-NLS-1$
String parameterName = magicMethod.parameterNames[i];
String parameterType = magicMethod.parameterTypes[i];
if (parameterType != null) {
fBuf.append(PARAM_NAME_START);
fBuf.append(parameterType);
fBuf.append(PARAM_NAME_END);
}
fBuf.append(PARAM_NAME_START);
fBuf.append(parameterName);
fBuf.append(PARAM_NAME_END);
fBuf.append(BlOCK_TAG_ENTRY_END);
}
}
if (magicMethod.returnType != null) {
fBuf.append("<dt>"); //$NON-NLS-1$
fBuf.append(PHPDocumentationMessages.JavaDoc2HTMLTextReader_returns_section);
fBuf.append("</dt>"); //$NON-NLS-1$
fBuf.append(BlOCK_TAG_ENTRY_START);
fBuf.append(" "); //$NON-NLS-1$
fBuf.append(magicMethod.returnType);
fBuf.append(BlOCK_TAG_ENTRY_END);
}
fBuf.append(BLOCK_TAG_END);
}
return fBuf.toString();
}
private static MagicMember getMagicMember(IMember member) {
if (member instanceof IMethod || member instanceof IField) {
IType type = null;
if (member instanceof IMethod) {
type = ((IMethod) member).getDeclaringType();
PHPDocBlock doc = PHPModelUtils.getDocBlock(type);
if (doc != null) {
Pattern WHITESPACE_SEPERATOR = Pattern.compile("\\s+"); //$NON-NLS-1$
final PHPDocTag[] tags = doc.getTags();
for (PHPDocTag docTag : tags) {
final int tagKind = docTag.getTagKind();
if (tagKind == PHPDocTag.METHOD) {
// http://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_tags.method.pkg.html
final String[] split = WHITESPACE_SEPERATOR
.split(docTag.getValue().trim());
if (split.length < 2) {
continue;
}
if (MagicMemberUtil.removeParenthesis(split)
.equals(member.getElementName())) {
return MagicMemberUtil.getMagicMethod(docTag
.getValue());
} else if (MagicMemberUtil
.removeParenthesis2(split).equals(
member.getElementName())) {
return MagicMemberUtil.getMagicMethod2(docTag
.getValue());
}
}
}
}
} else {
type = ((IField) member).getDeclaringType();
PHPDocBlock doc = PHPModelUtils.getDocBlock(type);
if (doc != null) {
Pattern WHITESPACE_SEPERATOR = Pattern.compile("\\s+"); //$NON-NLS-1$
final PHPDocTag[] tags = doc.getTags();
for (PHPDocTag docTag : tags) {
final int tagKind = docTag.getTagKind();
if (tagKind == PHPDocTag.PROPERTY
|| tagKind == PHPDocTag.PROPERTY_READ
|| tagKind == PHPDocTag.PROPERTY_WRITE) {
// http://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_tags.property.pkg.html
final String[] split = WHITESPACE_SEPERATOR
.split(docTag.getValue().trim());
if (split.length < 2) {
continue;
}
if (split[1].equals(member.getElementName())) {
return MagicMemberUtil.getMagicField(docTag
.getValue());
}
}
}
}
}
}
return null;
}
private static boolean canInheritJavadoc(IMember member) {
if (member instanceof IMethod && member.getScriptProject().exists()) {
/*
* Exists test catches ExternalJavaProject, in which case no
* hierarchy can be built.
*/
try {
return !((IMethod) member).isConstructor();
} catch (ModelException e) {
PHPUiPlugin.log(e);
}
}
return false;
}
private String toHTML() {
fBuf = new StringBuffer();
if (appendBuiltinDoc(fMember, fBuf)) {
return fBuf.toString();
}
// After first loop, non-null entries in the following two lists are
// missing and need to be inherited:
List<String> parameterNames = initParameterNames();
List<String> exceptionNames = new ArrayList<String>();
PHPDocTag deprecatedTag = null;
PHPDocTag returnTag = null;
PHPDocTag namespaceTag = null;
List<PHPDocTag> parameters = new ArrayList<PHPDocTag>();
List<PHPDocTag> exceptions = new ArrayList<PHPDocTag>();
List<PHPDocTag> versions = new ArrayList<PHPDocTag>();
List<PHPDocTag> authors = new ArrayList<PHPDocTag>();
List<PHPDocTag> sees = new ArrayList<PHPDocTag>();
List<PHPDocTag> since = new ArrayList<PHPDocTag>();
List<PHPDocTag> rest = new ArrayList<PHPDocTag>();
String shortDescription = fJavadoc.getShortDescription();
String longDescription = fJavadoc.getLongDescription();
PHPDocTag[] tags = fJavadoc.getTags();
for (PHPDocTag tag : tags) {
if (PHPDocTag.PARAM == tag.getTagKind()) {
parameters.add(tag);
SimpleReference[] fragments = tag.getReferences();
if (fragments.length == 0) {
if (parameterNames.size() > parameters.indexOf(tag))
parameterNames.set(parameters.indexOf(tag), null);
}
for (SimpleReference reference : fragments) {
String name = reference.getName();
if (reference instanceof TypeReference) {
// parameterTypes.add(name);
} else if (reference instanceof VariableReference) {
int paramIndex = parameterNames.indexOf(name);
if (paramIndex != -1) {
parameterNames.set(paramIndex, null);
}
}
}
} else if (PHPDocTag.RETURN == tag.getTagKind()) {
if (returnTag == null)
returnTag = tag; // the Javadoc tool only shows the first
// return tag
} else if (PHPDocTag.NAMESPACE == tag.getTagKind()) {
if (namespaceTag == null)
namespaceTag = tag;
} else if (PHPDocTag.THROWS == tag.getTagKind()) {
exceptions.add(tag);
SimpleReference[] fragments = tag.getReferences();
if (fragments.length > 0) {
Object first = fragments[0];
if (first instanceof TypeReference) {
exceptionNames.add(((TypeReference) first).getName());
}
}
} else if (PHPDocTag.SINCE == tag.getTagKind()) {
since.add(tag);
} else if (PHPDocTag.VERSION == tag.getTagKind()) {
versions.add(tag);
} else if (PHPDocTag.AUTHOR == tag.getTagKind()) {
authors.add(tag);
} else if (PHPDocTag.SEE == tag.getTagKind()) {
sees.add(tag);
} else if (PHPDocTag.DEPRECATED == tag.getTagKind()) {
if (deprecatedTag == null)
deprecatedTag = tag; // the Javadoc tool only shows the
// first deprecated tag
} else {
rest.add(tag);
}
}
if (deprecatedTag != null)
handleDeprecatedTag(deprecatedTag);
if (shortDescription != null && shortDescription.length() > 0)
fBuf.append(shortDescription);
if (longDescription != null && longDescription.length() > 0) {
fBuf.append("<p>"); //$NON-NLS-1$
longDescription = longDescription.replaceAll(
"(\r\n|\n|\r){2,}", "</p><p>"); //$NON-NLS-1$ //$NON-NLS-2$
fBuf.append(longDescription);
fBuf.append("</p>"); //$NON-NLS-1$
} else if (fMethod != null) {
CharSequence inherited = fJavadocLookup
.getInheritedMainDescription(fMethod);
handleInherited(inherited);
}
handleInlineLinks();
CharSequence[] parameterDescriptions = new CharSequence[parameterNames
.size()];
CharSequence[] parameterTypes = new CharSequence[parameterNames.size()];
boolean hasInheritedParameters = inheritParameterDescriptions(
parameterNames, parameterDescriptions, parameterTypes);
boolean hasParameters = parameters.size() > 0 || hasInheritedParameters;
CharSequence returnDescription = null;
if (returnTag == null)
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;
boolean hasNamespace = namespaceTag != null;
if (hasParameters
|| hasReturnTag
|| hasExceptions
|| hasNamespace
|| versions.size() > 0
|| authors.size() > 0
|| since.size() > 0
|| sees.size() > 0
|| rest.size() > 0
|| (fBuf.length() > 0 && (parameterDescriptions.length > 0 || exceptionDescriptions.length > 0))) {
handleSuperMethodReferences();
fBuf.append(BLOCK_TAG_START);
handleParameterTags(parameters, parameterNames, parameterTypes,
parameterDescriptions);
handleReturnTag(returnTag, returnDescription);
handleNamespaceTag(namespaceTag);
handleExceptionTags(exceptions, exceptionNames,
exceptionDescriptions);
handleBlockTags(
PHPDocumentationMessages.JavaDoc2HTMLTextReader_since_section,
since);
handleBlockTags(
PHPDocumentationMessages.JavaDoc2HTMLTextReader_version_section,
versions);
handleBlockTags(
PHPDocumentationMessages.JavaDoc2HTMLTextReader_author_section,
authors);
handleBlockTags(
PHPDocumentationMessages.JavaDoc2HTMLTextReader_see_section,
sees);
handleBlockTags(rest);
fBuf.append(BLOCK_TAG_END);
} else if (fBuf.length() > 0) {
handleSuperMethodReferences();
}
String result = fBuf.toString();
fBuf = null;
return result;
}
private void handleInlineLinks() {
Matcher m = INLINE_LINK_PATTERN.matcher(fBuf);
List<ReplaceEdit> replaceLinks = new ArrayList<ReplaceEdit>();
while (m.find()) {
String[] strs = m.group().split("[\\s]+", 3); //$NON-NLS-1$
String url = removeLastRightCurlyBrace(strs[1]);
String link = "";//$NON-NLS-1$
if (url.toLowerCase().startsWith("http")) { //$NON-NLS-1$
String description = ""; //$NON-NLS-1$
if (strs.length == 3) {
description = removeLastRightCurlyBrace(strs[2]);
} else {
description = url;
}
link = String.format("<a href=\"%s\">%s</a>", url, description); //$NON-NLS-1$
} else {
link = handleLinks(Arrays.asList(new TypeReference(0, 0, url)))
.toString();
}
replaceLinks.add(new ReplaceEdit(m.start(), m.end() - m.start(),
link));
}
for (int i = replaceLinks.size() - 1; i >= 0; i--) {
ReplaceEdit replaceLink = replaceLinks.get(i);
fBuf.replace(replaceLink.getOffset(), replaceLink.getOffset()
+ replaceLink.getLength(), replaceLink.getText());
}
}
private String removeLastRightCurlyBrace(String str) {
if (str.endsWith("}")) { //$NON-NLS-1$
return str.substring(0, str.length() - 1);
}
return str;
}
private void handleDeprecatedTag(PHPDocTag tag) {
fBuf.append("<p><b>"); //$NON-NLS-1$
fBuf.append(PHPDocumentationMessages.JavaDoc2HTMLTextReader_deprecated_section);
fBuf.append("</b> <i>"); //$NON-NLS-1$
handleContentElements(tag);
fBuf.append("</i><p>"); //$NON-NLS-1$
}
private void handleSuperMethodReferences() {
if (fMethod != null && fMethod.getDeclaringType() != null) {
try {
StringBuffer superMethodReferences = createSuperMethodReferences(fMethod);
if (superMethodReferences != null)
fBuf.append(superMethodReferences);
} catch (ModelException e) {
PHPUiPlugin.log(e);
}
}
}
private List<String> initParameterNames() {
if (fMethod != null) {
try {
return new ArrayList<String>(Arrays.asList(fMethod
.getParameterNames()));
} catch (ModelException e) {
PHPUiPlugin.log(e);
}
}
return Collections.EMPTY_LIST;
}
private boolean inheritParameterDescriptions(List<String> parameterNames,
CharSequence[] parameterDescriptions, CharSequence[] parameterTypes) {
boolean hasInheritedParameters = false;
if (fMethod != null && fMethod.getDeclaringType() == null) {
return hasInheritedParameters;
}
for (int i = 0; i < parameterNames.size(); i++) {
String name = (String) parameterNames.get(i);
if (name != null) {
parameterDescriptions[i] = fJavadocLookup
.getInheritedParamDescription(fMethod, i);
parameterTypes[i] = fJavadocLookup.getInheritedParamType(
fMethod, i);
if (parameterDescriptions[i] != null)
hasInheritedParameters = true;
}
}
return hasInheritedParameters;
}
private boolean inheritExceptionDescriptions(List<String> exceptionNames,
CharSequence[] exceptionDescriptions) {
boolean hasInheritedExceptions = false;
if (fMethod != null && fMethod.getDeclaringType() == null) {
return hasInheritedExceptions;
}
for (int i = 0; i < exceptionNames.size(); i++) {
String name = (String) 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;
String shortDescription = fJavadoc.getShortDescription();
if (shortDescription != null && shortDescription.length() > 0) {
fMainDescription.append(shortDescription);
}
fBuf = null;
}
return fMainDescription.length() > 0 ? fMainDescription : null;
}
CharSequence getReturnDescription() {
if (fReturnDescription == null) {
fReturnDescription = new StringBuffer();
fBuf = fReturnDescription;
PHPDocTag[] tags = fJavadoc.getTags();
for (PHPDocTag tag : tags) {
if (PHPDocTag.RETURN == tag.getTagKind()) {
handleContentElements(tag);
break;
}
}
fBuf = null;
}
return fReturnDescription.length() > 0 ? fReturnDescription : null;
}
CharSequence getInheritedParamDescription(int paramIndex)
throws ModelException {
if (fMethod != null) {
String[] parameterNames = fMethod.getParameterNames();
if (paramIndex >= parameterNames.length) {
return null;
}
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;
String paramName = parameterNames[paramIndex];
String info = getParameterInfo(fJavadoc, paramName,
PARAMETER_DESCRIPTION_TYPE);
if (info != null)
description.append(info);
fBuf = null;
return description.length() > 0 ? description : null;
}
return null;
}
CharSequence getInheritedParamType(int paramIndex) throws ModelException {
if (fMethod != null) {
String[] parameterNames = fMethod.getParameterNames();
if (paramIndex >= parameterNames.length) {
return null;
}
if (fParamTypes == null) {
fParamTypes = new StringBuffer[parameterNames.length];
} else {
StringBuffer typeName = fParamTypes[paramIndex];
if (typeName != null) {
return typeName.length() > 0 ? typeName : null;
}
}
StringBuffer typeName = new StringBuffer();
fParamTypes[paramIndex] = typeName;
fBuf = typeName;
String paramName = parameterNames[paramIndex];
String info = getParameterInfo(fJavadoc, paramName,
PARAMETER_TYPE_TYPE);
if (info != null)
typeName.append(info);
fBuf = null;
return typeName.length() > 0 ? typeName : null;
}
return null;
}
CharSequence getExceptionDescription(String simpleName) {
if (fMethod != null) {
if (fExceptionDescriptions == null) {
fExceptionDescriptions = new HashMap();
} else {
StringBuffer description = (StringBuffer) fExceptionDescriptions
.get(simpleName);
if (description != null) {
return description.length() > 0 ? description : null;
}
}
StringBuffer description = new StringBuffer();
fExceptionDescriptions.put(simpleName, description);
fBuf = description;
List tags = Arrays.asList(fJavadoc.getTags());
for (Iterator iter = tags.iterator(); iter.hasNext();) {
PHPDocTag tag = (PHPDocTag) iter.next();
if (PHPDocTag.THROWS == tag.getTagKind()) {
List fragments = Arrays.asList(tag.getReferences());
if (fragments.size() > 0) {
Object first = fragments.get(0);
if (first instanceof Identifier) {
String name = ((Identifier) first).getName();
if (name.equals(simpleName)) {
if (fragments.size() > 1)
handleContentElements(tag);
break;
}
}
}
}
}
fBuf = null;
return description.length() > 0 ? description : null;
}
return null;
}
private void handleContentElements(PHPDocTag tag) {
fBuf.append(tag.getValue());
}
private boolean handleInherited(CharSequence inherited) {
if (inherited == null)
return false;
fBuf.append(inherited);
return true;
}
private void handleBlockTags(String title, List tags) {
if (tags.isEmpty())
return;
handleBlockTagTitle(title);
for (Iterator iter = tags.iterator(); iter.hasNext();) {
PHPDocTag tag = (PHPDocTag) iter.next();
fBuf.append(BlOCK_TAG_ENTRY_START);
if (PHPDocTag.SEE == tag.getTagKind()) {
handleSeeTag(tag);
} else {
handleContentElements(tag);
}
doWorkAround();
fBuf.append(BlOCK_TAG_ENTRY_END);
}
}
private void handleReturnTag(PHPDocTag tag, CharSequence returnDescription) {
if (tag == null && returnDescription == null)
return;
handleBlockTagTitle(PHPDocumentationMessages.JavaDoc2HTMLTextReader_returns_section);
fBuf.append(BlOCK_TAG_ENTRY_START);
if (tag != null) {
handleContentElements(tag);
} else {
fBuf.append(returnDescription);
}
doWorkAround();
fBuf.append(BlOCK_TAG_ENTRY_END);
}
private void handleNamespaceTag(PHPDocTag tag) {
if (tag == null)
return;
handleBlockTagTitle(PHPDocumentationMessages.JavaDoc2HTMLTextReader_namespace_section);
fBuf.append(BlOCK_TAG_ENTRY_START);
if (tag != null) {
handleContentElements(tag);
}
doWorkAround();
fBuf.append(BlOCK_TAG_ENTRY_END);
}
private void handleBlockTags(List tags) {
for (Iterator iter = tags.iterator(); iter.hasNext();) {
PHPDocTag tag = (PHPDocTag) iter.next();
if (tag.getTagKind() == PHPDocTag.INHERITDOC) {
continue;
} else if (tag.getTagKind() == PHPDocTag.VAR) {
handleBlockTagTitle("Type"); //$NON-NLS-1$
} else {
handleBlockTagTitle(PHPDocTag.getTagKind(tag.getTagKind()));
}
fBuf.append(BlOCK_TAG_ENTRY_START);
if (tag.getTagKind() == PHPDocTag.LINK) {
handleLinkTag(tag);
} else {
handleContentElements(tag);
}
doWorkAround();
fBuf.append(BlOCK_TAG_ENTRY_END);
}
}
private void handleBlockTagTitle(String title) {
fBuf.append("<dt>"); //$NON-NLS-1$
fBuf.append(title);
fBuf.append("</dt>"); //$NON-NLS-1$
}
private void handleExceptionTags(List tags, List exceptionNames,
CharSequence[] exceptionDescriptions) {
if (tags.size() == 0 && containsOnlyNull(exceptionNames))
return;
handleBlockTagTitle(PHPDocumentationMessages.JavaDoc2HTMLTextReader_throws_section);
for (Iterator iter = tags.iterator(); iter.hasNext();) {
PHPDocTag tag = (PHPDocTag) iter.next();
fBuf.append(BlOCK_TAG_ENTRY_START);
handleThrowsTag(tag);
doWorkAround();
fBuf.append(BlOCK_TAG_ENTRY_END);
}
for (int i = 0; i < exceptionDescriptions.length; i++) {
CharSequence description = exceptionDescriptions[i];
String name = (String) exceptionNames.get(i);
if (name != null) {
fBuf.append(BlOCK_TAG_ENTRY_START);
if (description != null) {
fBuf.append(ScriptElementLabels.CONCAT_STRING);
fBuf.append(description);
}
fBuf.append(BlOCK_TAG_ENTRY_END);
}
}
}
private void handleThrowsTag(PHPDocTag tag) {
List<SimpleReference> fragments = Arrays.asList(tag.getReferences());
int size = fragments.size();
if (size > 0) {
String exceptionName = ""; //$NON-NLS-1$
if (fragments.get(0) instanceof TypeReference) {
exceptionName = fragments.get(0).getName().trim();
fBuf.append(exceptionName);
}
String value = tag.getValue().trim();
String description = value.substring(exceptionName.length());
if (description.length() > 0) {
fBuf.append(ScriptElementLabels.CONCAT_STRING);
fBuf.append(description.trim());
}
}
}
private void handleParameterTags(List tags, List parameterNames,
CharSequence[] parameterTypes, CharSequence[] parameterDescriptions) {
if (tags.size() == 0 && containsOnlyNull(parameterNames))
return;
handleBlockTagTitle(PHPDocumentationMessages.JavaDoc2HTMLTextReader_parameters_section);
for (Iterator iter = tags.iterator(); iter.hasNext();) {
PHPDocTag tag = (PHPDocTag) iter.next();
handleParamTag(tag);
}
for (int i = 0; i < parameterDescriptions.length; i++) {
CharSequence description = parameterDescriptions[i];
String name = (String) parameterNames.get(i);
if (name != null) {
fBuf.append(BlOCK_TAG_ENTRY_START);
if (parameterTypes[i] != null) {
fBuf.append(PARAM_NAME_START);
fBuf.append(parameterTypes[i]);
fBuf.append(PARAM_NAME_END);
}
fBuf.append(PARAM_NAME_END);
fBuf.append(PARAM_NAME_START);
fBuf.append(name);
fBuf.append(PARAM_NAME_END);
if (description != null) {
fBuf.append(description);
}
doWorkAround();
fBuf.append(BlOCK_TAG_ENTRY_END);
}
}
}
private void handleParamTag(PHPDocTag tag) {
if (tag.getReferences().length == 0) {
fBuf.append(BlOCK_TAG_ENTRY_START);
fBuf.append(tag.getValue());
fBuf.append(BlOCK_TAG_ENTRY_END);
return;
}
String parameterName = getParameterInfo(tag, PARAMETER_NAME_TYPE);
String parameterType = getParameterInfo(tag, PARAMETER_TYPE_TYPE);
String description = getParameterInfo(tag, PARAMETER_DESCRIPTION_TYPE);
fBuf.append(BlOCK_TAG_ENTRY_START);
if (parameterType != null) {
fBuf.append(PARAM_NAME_START);
fBuf.append(parameterType);
fBuf.append(PARAM_NAME_END);
}
fBuf.append(PARAM_NAME_START);
fBuf.append(parameterName);
fBuf.append(PARAM_NAME_END);
if (description != null) {
fBuf.append(description);
}
doWorkAround();
fBuf.append(BlOCK_TAG_ENTRY_END);
}
private void handleLinkTag(PHPDocTag tag) {
fBuf.append("<a href="); //$NON-NLS-1$
fBuf.append(tag.getValue());
fBuf.append(">").append(tag.getValue()).append("</a>"); //$NON-NLS-1$ //$NON-NLS-2$
}
private void handleSeeTag(PHPDocTag tag) {
fBuf.append(handleLinks(Arrays.asList(tag.getReferences())));
}
private StringBuffer handleLinks(List<? extends SimpleReference> fragments) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < fragments.size(); i++) {
SimpleReference reference = fragments.get(i);
sb.append(handleLink(reference));
if (i < (fragments.size() - 1)) {
sb.append(", ");
}
}
return sb;
}
private StringBuffer handleLink(SimpleReference fragment) {
StringBuffer sb = new StringBuffer();
String refTypeName = null;
String refMemberName = null;
String[] refMethodParamTypes = null;
if (fragment instanceof TypeReference) {
TypeReference type = (TypeReference) fragment;
String name = type.getName();
int typeNameEnd = name.indexOf("::"); //$NON-NLS-1$
if (typeNameEnd == -1) {
refTypeName = name;
} else {
refTypeName = name.substring(0, typeNameEnd);
int argsListStart = name.indexOf('(', typeNameEnd);
if (argsListStart == -1) {
refMemberName = name.substring(typeNameEnd + "::".length()); //$NON-NLS-1$
} else {
refMemberName = name.substring(
typeNameEnd + "::".length(), argsListStart); //$NON-NLS-1$
int argsListEnd = name.indexOf(')', argsListStart);
if (argsListEnd == -1) {
refMethodParamTypes = new String[0];
} else {
String argsList = name.substring(
argsListStart + ")".length(), argsListEnd); //$NON-NLS-1$
List<String> args = new ArrayList<String>();
StringTokenizer tokenizer = new StringTokenizer(
argsList, ","); //$NON-NLS-1$
while (tokenizer.hasMoreElements()) {
args.add(tokenizer.nextToken().trim());
}
refMethodParamTypes = args.toArray(new String[0]);
}
}
}
}
if (refTypeName != null) {
sb.append("<a href='"); //$NON-NLS-1$
try {
String scheme = PHPElementLinks.PHPDOC_SCHEME;
String uri = PHPElementLinks.createURI(scheme, fMember,
refTypeName, refMemberName, refMethodParamTypes);
sb.append(uri);
} catch (URISyntaxException e) {
PHPUiPlugin.log(e);
}
sb.append("'>"); //$NON-NLS-1$
sb.append(refTypeName);
if (refMemberName != null) {
if (refTypeName.length() > 0) {
sb.append("::"); //$NON-NLS-1$
}
sb.append(refMemberName);
if (refMethodParamTypes != null) {
sb.append('(');
for (int i = 0; i < refMethodParamTypes.length; i++) {
String pType = refMethodParamTypes[i];
sb.append(pType);
if (i < refMethodParamTypes.length - 1) {
sb.append(", "); //$NON-NLS-1$
}
}
sb.append(')');
}
}
sb.append("</a>"); //$NON-NLS-1$
}
return sb;
}
private boolean containsOnlyNull(List parameterNames) {
for (Iterator iter = parameterNames.iterator(); iter.hasNext();) {
if (iter.next() != null)
return false;
}
return true;
}
private String getParameterInfo(PHPDocBlock phpDoc, String paramName,
int infoType) {
for (PHPDocTag tag : phpDoc.getTags(PHPDocTag.PARAM)) {
String name = getParameterInfo(tag, PARAMETER_NAME_TYPE);
if (name != null && name.equals(paramName)) {
return getParameterInfo(tag, infoType);
}
continue;
}
return null;
}
private String getParameterInfo(PHPDocTag tag, int infoType) {
if (tag.getTagKind() != PHPDocTag.PARAM) {
return null;
}
SimpleReference typeRef = null;
SimpleReference variableRef = null;
String value = tag.getValue();
if (tag.getReferences().length != 2) {
return null;
}
for (SimpleReference reference : tag.getReferences()) {
if (reference instanceof TypeReference) {
typeRef = reference;
} else if (reference instanceof VariableReference) {
variableRef = reference;
}
}
if (infoType == PARAMETER_DESCRIPTION_TYPE) {
int typeRefIndex = value.indexOf(typeRef.getName());
int variableRefIndex = value.indexOf(variableRef.getName());
int lastRefIndex = typeRefIndex > variableRefIndex ? typeRefIndex
+ typeRef.getName().length() : variableRefIndex
+ variableRef.getName().length();
return value.substring(lastRefIndex).trim();
} else if (infoType == PARAMETER_TYPE_TYPE) {
return typeRef.getName();
} else if (infoType == PARAMETER_NAME_TYPE) {
return variableRef.getName();
}
return null;
}
private static boolean appendBuiltinDoc(IMember element, StringBuffer buf) {
String builtinDoc = BuiltinDoc.getString(element.getElementName());
if (builtinDoc.length() > 0) {
buf.append(builtinDoc);
return true;
}
return false;
}
// Work aorund for Bug 320709
// PHPDoc tooltips are not sized according to their contents
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=320709
private void doWorkAround() {
fBuf.append(" "); //$NON-NLS-1$
}
}