Java tutorial
/******************************************************************************* * Copyright (c) 2012 itemis AG (http://www.itemis.eu) 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 *******************************************************************************/ package org.summer.dsl.xbase.ui.hover; import static org.summer.dsl.xbase.ui.hover.HoverLinkHelper.*; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspace; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.dom.AST; 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.Javadoc; import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.TagElement; import org.eclipse.jdt.core.dom.TextElement; import org.eclipse.jdt.internal.corext.dom.ASTNodes; import org.eclipse.jdt.ui.JavaElementLabels; import org.eclipse.xtext.EcoreUtil2; import org.summer.dsl.model.types.JvmAnnotationReference; import org.summer.dsl.model.types.JvmAnnotationTarget; import org.summer.dsl.model.types.JvmAnnotationValue; import org.summer.dsl.model.types.JvmConstructor; import org.summer.dsl.model.types.JvmDeclaredType; import org.summer.dsl.model.types.JvmExecutable; import org.summer.dsl.model.types.JvmFeature; import org.summer.dsl.model.types.JvmField; import org.summer.dsl.model.types.JvmFormalParameter; import org.summer.dsl.model.types.JvmMember; import org.summer.dsl.model.types.JvmOperation; import org.summer.dsl.model.types.JvmTypeReference; import org.summer.dsl.model.types.TypesPackage; import org.eclipse.xtext.documentation.IEObjectDocumentationProvider; import org.eclipse.xtext.formatting.ILineSeparatorInformation; import org.eclipse.xtext.formatting.IWhitespaceInformationProvider; import org.eclipse.xtext.naming.IQualifiedNameConverter; import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.resource.XtextResourceSet; import org.eclipse.xtext.scoping.IScope; import org.eclipse.xtext.scoping.IScopeProvider; import org.eclipse.xtext.ui.editor.hover.html.IEObjectHoverDocumentationProvider; import org.eclipse.xtext.ui.editor.hover.html.XtextElementLinks; import org.eclipse.xtext.util.Strings; import org.summer.dsl.xbase.compiler.DocumentationAdapter; import org.summer.dsl.xbase.compiler.IGeneratorConfigProvider; import org.summer.dsl.xbase.compiler.JvmModelGenerator; import org.summer.dsl.xbase.compiler.output.FakeTreeAppendable; import org.summer.dsl.xbase.compiler.output.ITreeAppendable; import org.summer.dsl.xbase.jvmmodel.IJvmModelAssociations; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.inject.Inject; /** * @author Holger Schill - Initial contribution and API * @since 2.3 */ public class XbaseHoverDocumentationProvider implements IEObjectHoverDocumentationProvider { protected static final String BLOCK_TAG_START = "<dl>"; //$NON-NLS-1$ protected static final String BLOCK_TAG_END = "</dl>"; //$NON-NLS-1$ protected static final String BlOCK_TAG_ENTRY_START = "<dd>"; //$NON-NLS-1$ protected static final String BlOCK_TAG_ENTRY_END = "</dd>"; //$NON-NLS-1$ protected static final String PARAM_NAME_START = "<b>"; //$NON-NLS-1$ protected static final String PARAM_NAME_END = "</b> "; //$NON-NLS-1$ protected String rawJavaDoc = ""; protected EObject context = null; @Inject protected IScopeProvider scopeProvider; @Inject protected IQualifiedNameConverter qualifiedNameConverter; @Inject protected IWorkspace workspace; @Inject protected IJvmModelAssociations associations; @Inject protected IEObjectDocumentationProvider documentationProvider; @Inject protected JvmModelGenerator jvmModelGenerator; @Inject private XbaseDeclarativeHoverSignatureProvider hoverSignatureProvider; @Inject IGeneratorConfigProvider generatorConfigProvider; protected StringBuffer buffer; protected int fLiteralContent; @Inject private IWhitespaceInformationProvider whitespaceInformationProvider; public String getDocumentation(EObject object) { return computeDocumentation(object) + getDerivedOrOriginalDeclarationInformation(object); } public String computeDocumentation(EObject object) { buffer = new StringBuffer(); context = object; fLiteralContent = 0; List<String> parameterNames = initParameterNames(); Map<String, URI> exceptionNamesToURI = initExceptionNamesToURI(); addAnnotations(object); getDocumentationWithPrefix(object); Javadoc javadoc = getJavaDoc(); if (javadoc == null) return ""; TagElement deprecatedTag = null; TagElement start = null; List<TagElement> parameters = new ArrayList<TagElement>(); TagElement returnTag = null; List<TagElement> exceptions = new ArrayList<TagElement>(); List<TagElement> versions = new ArrayList<TagElement>(); List<TagElement> authors = new ArrayList<TagElement>(); List<TagElement> sees = new ArrayList<TagElement>(); List<TagElement> since = new ArrayList<TagElement>(); List<TagElement> rest = new ArrayList<TagElement>(); @SuppressWarnings("unchecked") List<TagElement> tags = javadoc.tags(); for (Iterator<TagElement> iter = tags.iterator(); iter.hasNext();) { TagElement tag = iter.next(); String tagName = tag.getTagName(); if (tagName == null) { start = tag; } else if (TagElement.TAG_PARAM.equals(tagName)) { parameters.add(tag); @SuppressWarnings("unchecked") List<? extends ASTNode> fragments = tag.fragments(); if (fragments.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); } } } } else if (TagElement.TAG_RETURN.equals(tagName)) { if (returnTag == null) returnTag = tag; // the Javadoc tool only shows the first return tag } else if (TagElement.TAG_EXCEPTION.equals(tagName) || TagElement.TAG_THROWS.equals(tagName)) { exceptions.add(tag); @SuppressWarnings("unchecked") 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 (exceptionNamesToURI.containsKey(name)) { exceptionNamesToURI.put(name, null); } } } } else if (TagElement.TAG_SINCE.equals(tagName)) { since.add(tag); } else if (TagElement.TAG_VERSION.equals(tagName)) { versions.add(tag); } else if (TagElement.TAG_AUTHOR.equals(tagName)) { authors.add(tag); } else if (TagElement.TAG_SEE.equals(tagName)) { sees.add(tag); } else if (TagElement.TAG_DEPRECATED.equals(tagName)) { if (deprecatedTag == null) deprecatedTag = tag; // the Javadoc tool only shows the first deprecated tag } else { rest.add(tag); } } boolean hasParameters = parameters.size() > 0; boolean hasReturnTag = returnTag != null; boolean hasExceptions = exceptions.size() > 0; if (deprecatedTag != null) handleDeprecatedTag(deprecatedTag); if (start != null) { @SuppressWarnings("unchecked") List<ASTNode> fragments = start.fragments(); handleContentElements(fragments); } if (hasParameters || hasReturnTag || hasExceptions || versions.size() > 0 || authors.size() > 0 || since.size() > 0 || sees.size() > 0 || rest.size() > 0 || (buffer.length() > 0) && (parameterNames.size() > 0 || exceptionNamesToURI.size() > 0)) { handleSuperMethodReferences(object); buffer.append(BLOCK_TAG_START); handleParameters(object, parameters, parameterNames); handleReturnTag(returnTag); handleExceptionTags(exceptions, exceptionNamesToURI); handleBlockTags("Since:", since); handleBlockTags("Version:", versions); handleBlockTags("Author:", authors); handleBlockTags("See Also:", sees); handleBlockTags(rest); buffer.append(BLOCK_TAG_END); } else if (buffer.length() > 0) { handleSuperMethodReferences(object); } String result = buffer.toString(); buffer = null; rawJavaDoc = null; context = null; return result; } public String getDerivedOrOriginalDeclarationInformation(EObject object) { String derivedInformation = getDerivedElementInformation(object); if (derivedInformation != null && derivedInformation.length() > 0) return getDerivedElementInformation(object); return getOriginalDeclarationInformation(object); } protected List<String> initParameterNames() { List<String> result = Lists.newArrayList(); if (context instanceof JvmExecutable) { for (JvmFormalParameter param : ((JvmExecutable) context).getParameters()) { result.add(param.getName()); } } return result; } protected Map<String, URI> initExceptionNamesToURI() { Map<String, URI> result = Maps.newHashMap(); if (context instanceof JvmExecutable) { for (JvmTypeReference exception : ((JvmExecutable) context).getExceptions()) { result.put(exception.getSimpleName(), EcoreUtil.getURI(exception.getType())); } } return result; } protected void addAnnotations(EObject object) { Set<EObject> jvmElements = associations.getJvmElements(object); if (jvmElements.size() > 0) { EObject associatedElement = Lists.newArrayList(jvmElements).get(0); if (associatedElement instanceof JvmAnnotationTarget) { EList<JvmAnnotationReference> annotations = ((JvmAnnotationTarget) associatedElement) .getAnnotations(); for (JvmAnnotationReference annotationReference : annotations) { buffer.append("@"); buffer.append(createLinkWithLabel(XtextElementLinks.XTEXTDOC_SCHEME, EcoreUtil.getURI(annotationReference.getAnnotation()), annotationReference.getAnnotation().getSimpleName())); if (annotationReference.getValues().size() > 0) { buffer.append("("); for (JvmAnnotationValue value : annotationReference.getValues()) { ITreeAppendable appendable = new FakeTreeAppendable(); jvmModelGenerator.toJava(value, appendable, generatorConfigProvider.get(object)); String java = appendable.getContent(); buffer.append(java); } buffer.append(")"); } buffer.append("<br>"); } } } } protected void getDocumentationWithPrefix(EObject object) { String startTag = "/**"; String endTag = "*/"; ILineSeparatorInformation lineSeparatorInformation = whitespaceInformationProvider .getLineSeparatorInformation(EcoreUtil.getURI(object)); String lineBreak = lineSeparatorInformation.getLineSeparator(); String documentation = resolveDocumentation(object); if (documentation != null && documentation.length() > 0) { BufferedReader reader = new BufferedReader(new StringReader(documentation)); StringBuilder builder = new StringBuilder(startTag + lineBreak); try { String line = ""; while ((line = reader.readLine()) != null) { if (line.length() > 0) builder.append(line + lineBreak); } builder.append(endTag); } catch (IOException e) { } rawJavaDoc = builder.toString(); } } protected String resolveDocumentation(EObject object) { String documentation = documentationProvider.getDocumentation(object); if (documentation != null) { return documentation; } DocumentationAdapter adapter = getDocumentationAdapter(object); if (adapter != null) { documentation = adapter.getDocumentation(); } if (documentation != null) { return documentation; } EObject primarySourceElement = associations.getPrimarySourceElement(object); if (primarySourceElement == null) { return null; } return documentationProvider.getDocumentation(primarySourceElement); } protected DocumentationAdapter getDocumentationAdapter(EObject object) { return (DocumentationAdapter) EcoreUtil.getAdapter(object.eAdapters(), DocumentationAdapter.class); } protected void handleSuperMethodReferences(EObject context) { // Not handled for Xbase } protected String createMethodInTypeLinks(JvmOperation overridden) { String methodLink = createSimpleMemberLink(overridden); String typeLink = createSimpleMemberLink(overridden.getDeclaringType()); String methodInType = MessageFormat.format("{0} in {1}", new Object[] { methodLink, typeLink }); return methodInType; } protected String createSimpleMemberLink(EObject type) { String label = ""; if (type instanceof JvmDeclaredType) label = ((JvmDeclaredType) type).getSimpleName(); else if (type instanceof JvmOperation) { JvmOperation operation = (JvmOperation) type; label = operation.getSimpleName(); if (operation.getParameters().size() > 0) { label += "(...)"; } } return createLinkWithLabel(XtextElementLinks.XTEXTDOC_SCHEME, EcoreUtil.getURI(type), label); } protected boolean handleValueTag(TagElement node) { // Not handled explicit for Xbase for now handleText(node.toString()); return true; } protected boolean handleInheritDoc(TagElement node) { if (!TagElement.TAG_INHERITDOC.equals(node.getTagName())) return false; // Not handled explicit for Xbase for now handleText(node.toString()); return true; } protected boolean handleDocRoot(TagElement node) { if (!TagElement.TAG_DOCROOT.equals(node.getTagName())) return false; URI uri = EcoreUtil.getURI(context); String projectName = uri.segment(1); String sourceFolderName = uri.segment(2); IProject project = workspace.getRoot().getProject(projectName); if (project.exists() && project.isOpen()) { IFolder sourceFolder = project.getFolder(sourceFolderName); if (sourceFolder.exists()) { buffer.append(sourceFolder.getLocationURI().toASCIIString()); } } return true; } protected void handleLink(List<?> fragments) { if (fragments == null || fragments.isEmpty()) { return; } URI elementURI = null; String firstFragment = fragments.get(0).toString(); int hashIndex = firstFragment.indexOf("#"); if (hashIndex != -1) { JvmDeclaredType resolvedDeclarator = getResolvedDeclarator(firstFragment.substring(0, hashIndex)); if (resolvedDeclarator != null && !resolvedDeclarator.eIsProxy()) { String signature = firstFragment.substring(hashIndex + 1); int indexOfOpenBracket = signature.indexOf("("); int indexOfClosingBracket = signature.indexOf(")"); String simpleNameOfFeature = indexOfOpenBracket != -1 ? signature.substring(0, indexOfOpenBracket) : signature; Iterable<JvmFeature> possibleCandidates = resolvedDeclarator .findAllFeaturesByName(simpleNameOfFeature); List<String> parameterNames = null; if (indexOfOpenBracket != -1 && indexOfClosingBracket != -1) { parameterNames = Strings .split(signature.substring(indexOfOpenBracket + 1, indexOfClosingBracket), ","); } Iterator<JvmFeature> featureIterator = possibleCandidates.iterator(); while (elementURI == null && featureIterator.hasNext()) { JvmFeature feature = featureIterator.next(); boolean valid = false; if (feature instanceof JvmField) { valid = true; } else if (feature instanceof JvmExecutable) { JvmExecutable executable = (JvmExecutable) feature; EList<JvmFormalParameter> parameters = executable.getParameters(); if (parameterNames == null) { valid = true; } else if (parameters.size() == parameterNames.size()) { valid = true; for (int i = 0; (i < parameterNames.size() && valid); i++) { URI parameterTypeURI = EcoreUtil .getURI(parameters.get(i).getParameterType().getType()); IEObjectDescription expectedParameter = scopeProvider .getScope(context, new HoverReference(TypesPackage.Literals.JVM_TYPE)) .getSingleElement( qualifiedNameConverter.toQualifiedName(parameterNames.get(i))); if (expectedParameter == null || !expectedParameter.getEObjectURI().equals(parameterTypeURI)) { valid = false; } } } } if (valid) elementURI = EcoreUtil.getURI(feature); } } } else { IScope scope = scopeProvider.getScope(context, new HoverReference(TypesPackage.Literals.JVM_TYPE)); IEObjectDescription singleElement = scope .getSingleElement(qualifiedNameConverter.toQualifiedName(firstFragment)); if (singleElement != null) elementURI = singleElement.getEObjectURI(); } String label = ""; if (fragments.size() > 1) { for (int i = 1; i < fragments.size(); i++) { String portentialLabel = fragments.get(i).toString(); if (portentialLabel.trim().length() > 0) label += portentialLabel; } } if (label.length() == 0) label = firstFragment; if (elementURI == null) buffer.append(label); else { buffer.append(createLinkWithLabel(XtextElementLinks.XTEXTDOC_SCHEME, elementURI, label)); } } protected JvmDeclaredType getResolvedDeclarator(String name) { JvmDeclaredType jvmDeclaredType = null; if (Strings.isEmpty(name) || name.trim().length() == 0) { jvmDeclaredType = getDeclaringType(context); } else { HoverReference reference = new HoverReference(TypesPackage.Literals.JVM_TYPE); IScope scope = scopeProvider.getScope(context, reference); IEObjectDescription declarator = scope.getSingleElement(qualifiedNameConverter.toQualifiedName(name)); if (declarator != null && EcoreUtil2.isAssignableFrom(TypesPackage.eINSTANCE.getJvmGenericType(), declarator.getEClass())) { jvmDeclaredType = (JvmDeclaredType) context.eResource().getResourceSet() .getEObject(declarator.getEObjectURI(), true); } } return jvmDeclaredType; } protected JvmDeclaredType getDeclaringType(EObject eObject) { if (eObject instanceof JvmMember) { return ((JvmMember) eObject).getDeclaringType(); } return null; } protected void handleBlockTags(List<TagElement> tags) { for (Iterator<TagElement> iter = tags.iterator(); iter.hasNext();) { TagElement tag = iter.next(); handleBlockTagTitle(tag.getTagName()); buffer.append(BlOCK_TAG_ENTRY_START); @SuppressWarnings("unchecked") List<ASTNode> fragments = tag.fragments(); handleContentElements(fragments); buffer.append(BlOCK_TAG_ENTRY_END); } } protected void handleBlockTags(String title, List<TagElement> tags) { if (tags.isEmpty()) return; handleBlockTagTitle(title); for (Iterator<TagElement> iter = tags.iterator(); iter.hasNext();) { TagElement tag = iter.next(); buffer.append(BlOCK_TAG_ENTRY_START); if (TagElement.TAG_SEE.equals(tag.getTagName())) { handleSeeTag(tag); } else { @SuppressWarnings("unchecked") List<ASTNode> fragments = tag.fragments(); handleContentElements(fragments); } buffer.append(BlOCK_TAG_ENTRY_END); } } protected void handleSeeTag(TagElement tag) { handleLink(tag.fragments()); } protected void handleExceptionTags(List<TagElement> tags, Map<String, URI> exceptionNamesToURI) { if (tags.size() == 0 && containsOnlyNull(exceptionNamesToURI.values())) return; handleBlockTagTitle("Throws:"); for (Iterator<TagElement> iter = tags.iterator(); iter.hasNext();) { TagElement tag = iter.next(); buffer.append(BlOCK_TAG_ENTRY_START); handleThrowsTag(tag); buffer.append(BlOCK_TAG_ENTRY_END); } for (int i = 0; i < exceptionNamesToURI.size(); i++) { String name = Lists.newArrayList(exceptionNamesToURI.keySet()).get(i); if (name != null && exceptionNamesToURI.get(name) != null) { buffer.append(BlOCK_TAG_ENTRY_START); buffer.append(createLinkWithLabel(XtextElementLinks.XTEXTDOC_SCHEME, exceptionNamesToURI.get(name), name)); buffer.append(BlOCK_TAG_ENTRY_END); } } } private boolean containsOnlyNull(Collection<?> list) { for (Iterator<?> iter = list.iterator(); iter.hasNext();) { if (iter.next() != null) return false; } return true; } protected void handleThrowsTag(TagElement tag) { @SuppressWarnings("unchecked") List<? extends ASTNode> fragments = tag.fragments(); int size = fragments.size(); if (size > 0) { handleLink(fragments.subList(0, 1)); if (size > 1) { buffer.append(JavaElementLabels.CONCAT_STRING); handleContentElements(fragments.subList(1, size)); } } } protected void handleDeprecatedTag(TagElement tag) { buffer.append("<p><b>"); //$NON-NLS-1$ //TODO: Messages out of properties like in JDT? buffer.append("Deprecated."); buffer.append("</b> <i>"); //$NON-NLS-1$ @SuppressWarnings("unchecked") List<ASTNode> fragments = tag.fragments(); handleContentElements(fragments); buffer.append("</i></p>"); //$NON-NLS-1$ } protected void handleContentElements(List<? extends ASTNode> nodes) { handleContentElements(nodes, false); } protected void handleContentElements(List<? extends ASTNode> nodes, boolean skipLeadingWhitespace) { ASTNode previousNode = null; for (Iterator<? extends ASTNode> iter = nodes.iterator(); iter.hasNext();) { ASTNode child = iter.next(); 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 } 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 = rawJavaDoc.substring(previousEnd, childStart); String text = removeDocLineIntros(textWithStars); buffer.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$ } 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 = rawJavaDoc.substring(start, start + child.getLength()); buffer.append(removeDocLineIntros(text)); } } } protected 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); if (isLiteral || isCode) fLiteralContent++; if (isLink || isCode) buffer.append("<code>"); //$NON-NLS-1$ if (isLink || isLinkplain) handleLink(node.fragments()); else if (isCode || isLiteral) { @SuppressWarnings("unchecked") List<ASTNode> fragments = node.fragments(); handleContentElements(fragments, true); } else if (handleInheritDoc(node)) { // handled } else if (handleDocRoot(node)) { // handled } else { //print uninterpreted source {@tagname ...} for unknown tags int start = node.getStartPosition(); String text = rawJavaDoc.substring(start, start + node.getLength()); buffer.append(removeDocLineIntros(text)); } if (isLink || isCode) buffer.append("</code>"); //$NON-NLS-1$ if (isLiteral || isCode) fLiteralContent--; } protected void handleText(String text) { if (fLiteralContent == 0) { buffer.append(text); } else { appendEscaped(buffer, text); } } protected 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)); } protected 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$ } protected void handleParameters(EObject object, List<TagElement> parameters, List<String> parameterNames) { if (parameters.size() == 0 && containsOnlyNull(parameterNames)) return; handleBlockTagTitle("Parameters:"); for (Iterator<TagElement> iter = parameters.iterator(); iter.hasNext();) { TagElement tag = iter.next(); buffer.append(BlOCK_TAG_ENTRY_START); handleParamTag(tag); buffer.append(BlOCK_TAG_ENTRY_END); } for (int i = 0; i < parameterNames.size(); i++) { String name = parameterNames.get(i); if (name != null) { buffer.append(BlOCK_TAG_ENTRY_START); buffer.append(PARAM_NAME_START); buffer.append(name); buffer.append(PARAM_NAME_END); buffer.append(BlOCK_TAG_ENTRY_END); } } } protected void handleParamTag(TagElement tag) { @SuppressWarnings("unchecked") List<? extends ASTNode> fragments = tag.fragments(); int i = 0; int size = fragments.size(); if (size > 0) { Object first = fragments.get(0); buffer.append(PARAM_NAME_START); if (first instanceof SimpleName) { String name = ((SimpleName) first).getIdentifier(); buffer.append(name); i++; } else if (first instanceof TextElement) { String firstText = ((TextElement) first).getText(); if ("<".equals(firstText)) { //$NON-NLS-1$ buffer.append("<"); //$NON-NLS-1$ i++; if (size > 1) { Object second = fragments.get(1); if (second instanceof SimpleName) { String name = ((SimpleName) second).getIdentifier(); buffer.append(name); i++; if (size > 2) { Object third = fragments.get(2); String thirdText = ((TextElement) third).getText(); if (">".equals(thirdText)) { //$NON-NLS-1$ buffer.append(">"); //$NON-NLS-1$ i++; } } } } } } buffer.append(PARAM_NAME_END); handleContentElements(fragments.subList(i, fragments.size())); } } protected void handleReturnTag(TagElement tag) { if (tag == null) return; handleBlockTagTitle("Returns:"); buffer.append(BlOCK_TAG_ENTRY_START); @SuppressWarnings("unchecked") List<ASTNode> fragments = tag.fragments(); handleContentElements(fragments); buffer.append(BlOCK_TAG_ENTRY_END); } protected void handleBlockTagTitle(String title) { buffer.append("<dt>"); //$NON-NLS-1$ buffer.append(title); buffer.append("</dt>"); //$NON-NLS-1$ } public Javadoc getJavaDoc() { if (context == null) return null; Object classpathURIContext = ((XtextResourceSet) context.eResource().getResourceSet()) .getClasspathURIContext(); if (classpathURIContext instanceof IJavaProject) { IJavaProject javaProject = (IJavaProject) classpathURIContext; ASTParser parser = ASTParser.newParser(AST.JLS3); parser.setProject(javaProject); @SuppressWarnings("unchecked") 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); String source = rawJavaDoc + "class C{}"; //$NON-NLS-1$ parser.setSource(source.toCharArray()); CompilationUnit root = (CompilationUnit) parser.createAST(null); if (root == null) return null; @SuppressWarnings("unchecked") List<AbstractTypeDeclaration> types = root.types(); if (types.size() != 1) return null; AbstractTypeDeclaration type = types.get(0); return type.getJavadoc(); } return null; } protected String getDerivedElementInformation(EObject o) { StringBuffer buf = new StringBuffer(); List<EObject> jvmElements = getFilteredDerivedElements(o, TypesPackage.Literals.JVM_MEMBER); if (jvmElements.size() > 0) { buf.append("<dt>Derived element:</dt>"); for (EObject jvmElement : jvmElements) { buf.append("<dd>"); buf.append(computeLinkToElement(jvmElement)); buf.append("</dd>"); } } return buf.toString(); } protected String getOriginalDeclarationInformation(EObject o) { StringBuffer buf = new StringBuffer(); List<EObject> sourceElements = getFilteredSourceElements(o, null); if (sourceElements.size() > 0) { buf.append("<dt>Original declaration:</dt>"); for (EObject sourceElement : sourceElements) { buf.append("<dd>"); buf.append(computeLinkToElement(sourceElement)); buf.append("</dd>"); } } return buf.toString(); } protected List<EObject> getFilteredDerivedElements(EObject o, final EClass type) { List<EObject> jvmElements = Lists .newArrayList(Iterables.filter(associations.getJvmElements(o), new Predicate<EObject>() { public boolean apply(EObject input) { if (input instanceof JvmConstructor && ((JvmConstructor) input).getParameters().size() == 0) return false; if (type == null) return true; return EcoreUtil2.isAssignableFrom(type, input.eClass()); } })); return jvmElements; } protected List<EObject> getFilteredSourceElements(EObject o, final EClass type) { List<EObject> sourceElements = Lists .newArrayList(Iterables.filter(associations.getSourceElements(o), new Predicate<EObject>() { public boolean apply(EObject input) { if (type == null) return true; return EcoreUtil2.isAssignableFrom(type, input.eClass()); } })); return sourceElements; } private String computeLinkToElement(EObject jvmElement) { String imageURL = hoverSignatureProvider.getImageTag(jvmElement); String signature = hoverSignatureProvider.getDerivedOrSourceSignature(jvmElement); return imageURL + createLinkWithLabel(XtextElementLinks.XTEXTDOC_SCHEME, EcoreUtil.getURI(jvmElement), signature); } }