Bug 578948 - [18][javadoc] incorrect handling of overlapping multiple
annotations within @snippet
Change-Id: I50ff70abdae5e5d9edafc17d3137cf3fc32ecc1d
Signed-off-by: Kalyan Prasad Tatavarthi <kalyan_prasad@in.ibm.com>
Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.ui/+/191692
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/javadoc/JavaDocSnippetStringEvaluator.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/javadoc/JavaDocSnippetStringEvaluator.java
new file mode 100644
index 0000000..26def92
--- /dev/null
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/javadoc/JavaDocSnippetStringEvaluator.java
@@ -0,0 +1,665 @@
+package org.eclipse.jdt.internal.ui.text.javadoc;
+
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.JavaDocRegion;
+import org.eclipse.jdt.core.dom.MemberRef;
+import org.eclipse.jdt.core.dom.MethodRef;
+import org.eclipse.jdt.core.dom.MethodRefParameter;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.TagElement;
+import org.eclipse.jdt.core.dom.TagProperty;
+import org.eclipse.jdt.core.dom.TextElement;
+
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+
+import org.eclipse.jdt.internal.ui.JavaPlugin;
+import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks;
+
+public class JavaDocSnippetStringEvaluator {
+
+ /**
+ * Either an IMember or an IPackageFragment.
+ */
+ private final IJavaElement fElement;
+
+ public enum ReplacementStringIntervalStatus {
+ /**
+ * replacement interval occurs before the given ActionElement interval
+ */
+ BEFORE,
+ /**
+ * replacement interval occurs after the given ActionElement interval
+ */
+ AFTER,
+ /**
+ * replacement interval occurs within the given ActionElement interval
+ */
+ WITHIN,
+ /**
+ * replacement interval contains the given ActionElement interval
+ */
+ ENCOMPASS,
+ /**
+ * replacement interval starts before the given ActionElement interval and ends within the ActionElement interval
+ */
+ PREV_OVERLAP,
+ /**
+ * replacement interval starts within the given ActionElement interval and ends after the ActionElement interval
+ */
+ POST_OVERLAP,
+ /**
+ * Default Value, do nothing
+ */
+ DEFAULT
+ }
+
+ public class ActionElement{
+ public int start;
+ public int end;
+ public String startTag;
+ public String endTag;
+
+ public ActionElement(int start, int end, String startTag, String endTag) {
+ this.start= start;
+ this.end= end;
+ this.startTag= startTag;
+ this.endTag= endTag;
+ }
+
+ public ReplacementStringIntervalStatus getIntervalStatus(int replacementIntervalStart, int replacementIntervalEnd) {
+ ReplacementStringIntervalStatus intervalStatus= ReplacementStringIntervalStatus.DEFAULT;
+ int startDiff = replacementIntervalStart - this.start;
+ int endDiff = replacementIntervalEnd - this.end;
+
+ if (this.end <= replacementIntervalStart) {
+ intervalStatus= ReplacementStringIntervalStatus.AFTER;
+ } else if (this.start >= replacementIntervalEnd) {
+ intervalStatus= ReplacementStringIntervalStatus.BEFORE;
+ } else if (startDiff <= 0 && endDiff >= 0) {
+ intervalStatus= ReplacementStringIntervalStatus.ENCOMPASS;
+ } else if (startDiff > 0 && endDiff < 0) {
+ intervalStatus= ReplacementStringIntervalStatus.WITHIN;
+ } else if (startDiff > 0) {
+ intervalStatus= ReplacementStringIntervalStatus.POST_OVERLAP;
+ } else if (endDiff < 0) {
+ intervalStatus= ReplacementStringIntervalStatus.PREV_OVERLAP;
+ }
+
+ return intervalStatus;
+ }
+
+ }
+
+ public class StringItem{
+ public int index;
+ public String tag;
+ public StringItem(int index, String tag) {
+ this.index= index;
+ this.tag= tag;
+ }
+ }
+
+ public JavaDocSnippetStringEvaluator(IJavaElement element) {
+ this.fElement= element;
+ }
+
+ public void AddTagElementString(TagElement snippetTag, StringBuffer buffer) {
+ if (snippetTag != null) {
+ List<Object> fragments = snippetTag.fragments();
+ for( Object fragment : fragments) {
+ String str= ""; //$NON-NLS-1$
+ if (fragment instanceof TextElement) {
+ TextElement textElement= (TextElement) fragment;
+ List<TagElement> tagElements= getTagElementsForTextElement(snippetTag, textElement);
+ str = getModifiedString(textElement, tagElements);
+ } else if (fragment instanceof JavaDocRegion) {
+ JavaDocRegion region = (JavaDocRegion) fragment;
+ if (region.isDummyRegion()) {
+ List<TagElement> tagElements= getTagElementsForDummyJavaDocRegion(snippetTag, region);
+ str = getModifiedString(region, tagElements);
+ }
+ } else if (fragment instanceof TagElement) {
+ TagElement tagElement= (TagElement) fragment;
+ List<TagElement> tagElements= getTagElementsForTagElement(snippetTag, tagElement);
+ str = getModifiedString(tagElement, tagElements);
+ }
+ buffer.append(str);
+ }
+ }
+ }
+
+ private String getModifiedString(TextElement textElement, List<TagElement> tags) {
+ return getModifiedString(textElement.getText(), tags);
+ }
+
+ private String getModifiedString(TagElement tagElement, List<TagElement> tags) {
+ return getModifiedString(((TextElement)tagElement.fragments().get(0)).getText(), tags);
+ }
+
+ private String getModifiedString(JavaDocRegion region, List<TagElement> tags) {
+ return getModifiedString(((TextElement)region.fragments().get(0)).getText(), tags);
+ }
+
+ private String getModifiedString(String str, List<TagElement> tags) {
+ List<ActionElement> actionElements= new ArrayList<>();
+ String modifiedStr= str;
+ for (TagElement tag : tags) {
+ String name= tag.getTagName();
+ if (TagElement.TAG_HIGHLIGHT.equals(name)) {
+ handleSnippetHighlight(modifiedStr, tag, actionElements);
+ } else if (TagElement.TAG_REPLACE.equals(name)) {
+ modifiedStr= handleSnippetReplace(modifiedStr, tag, actionElements);
+ } else if (TagElement.TAG_LINK.equals(name)) {
+ handleSnippetLink(modifiedStr, tag, actionElements);
+ }
+ }
+ return getString( modifiedStr, actionElements);
+ }
+
+ private String getString(String str, List<ActionElement> actionElements) {
+ String modifiedStr = str;
+ List<StringItem> items= new ArrayList<>();
+ for (ActionElement actElem : actionElements) {
+ StringItem startItem = new StringItem(actElem.start, actElem.startTag);
+ StringItem endItem = new StringItem(actElem.end, actElem.endTag);
+ ListIterator<StringItem> iterator = items.listIterator();
+ boolean endIndexAdded = false;
+ boolean startIndexAdded = false;
+ while (iterator.hasNext()) {
+ StringItem elem= iterator.next();
+ if (!endIndexAdded && elem.index < endItem.index) {
+ iterator.previous();
+ iterator.add(endItem);
+ endIndexAdded = true;
+ iterator.next();
+ }
+ if (!startIndexAdded && elem.index < startItem.index) {
+ iterator.previous();
+ iterator.add(startItem);
+ startIndexAdded = true;
+ iterator.next();
+ }
+ if (startIndexAdded && endIndexAdded) {
+ break;
+ }
+ }
+ if (!endIndexAdded) {
+ items.add(endItem);
+ }
+ if (!startIndexAdded) {
+ items.add(startItem);
+ }
+ }
+ for (StringItem item : items) {
+ modifiedStr = modifiedStr.substring(0, item.index) + item.tag + modifiedStr.substring(item.index);
+ }
+ return modifiedStr;
+ }
+
+ private void modifyPrevActionItemsReplacement(int replacementIntervalStart, int relacementIntervalEnd, int replacementStrNewLength, List<ActionElement> actionElements) {
+ ListIterator<ActionElement> iterator = actionElements.listIterator();
+ int oldLength = relacementIntervalEnd - replacementIntervalStart;
+ int diff = replacementStrNewLength - oldLength;
+ while (iterator.hasNext()) {
+ ActionElement elem= iterator.next();
+ ReplacementStringIntervalStatus intervalStatus= elem.getIntervalStatus(replacementIntervalStart, relacementIntervalEnd);
+ int endDiff = relacementIntervalEnd - elem.end;
+ switch(intervalStatus) {
+ case AFTER:
+ //do nothing., nothing needs to be done here
+ break;
+ case BEFORE:
+ if (diff != 0) {
+ elem.start+= diff;
+ elem.end+= diff;
+ }
+ break;
+ case ENCOMPASS:
+ iterator.remove();
+ break;
+ case POST_OVERLAP:
+ elem.end= replacementIntervalStart;
+ break;
+ case PREV_OVERLAP:
+ elem.end += diff;
+ elem.start = relacementIntervalEnd + diff;
+ break;
+ case WITHIN:
+ // modify the old ActionElement to end before the replacement string start
+ // Create a new Element element to start from the replacement string end
+ int oldEnd = elem.end;
+ elem.end= replacementIntervalStart;
+ int newStart = oldEnd + endDiff + diff;
+ ActionElement newElem= new ActionElement(newStart, oldEnd, elem.startTag, elem.endTag);
+ iterator.add(newElem);
+ break;
+ case DEFAULT:
+ default:
+ break;
+ }
+ }
+ }
+
+ private List<TagElement> getTagElementsForTextElement(TagElement snippetTag, TextElement textElement) {
+ List<TagElement> tagElements= new ArrayList<>();
+ List<JavaDocRegion> regions= snippetTag.tagRegionsStartingAtTextElement(textElement);
+ List<JavaDocRegion> masterList= snippetTag.tagRegionsContainingTextElement(textElement);
+ masterList.removeAll(regions);
+ for (JavaDocRegion region : masterList) {
+ for (Object tagObj : region.tags()) {
+ tagElements.add((TagElement) tagObj);
+ }
+ }
+ for (JavaDocRegion region : regions) {
+ for (Object tagObj : region.tags()) {
+ if (tagObj instanceof TagElement) {
+ TagElement tagElem= (TagElement) tagObj;
+ Object prop= tagElem.getProperty(TagProperty.TAG_PROPERTY_SNIPPET_INLINE_TAG_COUNT);
+ if (prop instanceof Integer) {
+ int val = ((Integer)prop).intValue();
+ ListIterator<TagElement> listIterator= tagElements.listIterator();
+ TagElement addBefore= null;
+ while (listIterator.hasNext()) {
+ TagElement tElem= listIterator.next();
+ Object prop2= tElem.getProperty(TagProperty.TAG_PROPERTY_SNIPPET_INLINE_TAG_COUNT);
+ if (prop2 instanceof Integer) {
+ int val2 = ((Integer)prop2).intValue();
+ if (val2 > val) {
+ addBefore= tElem;
+ break;
+ }
+ }
+ }
+ if (addBefore == null) {
+ tagElements.add(tagElem);
+ } else {
+ tagElements.add(tagElements.indexOf(addBefore), tagElem);
+ }
+ }
+ }
+ }
+ }
+ return tagElements;
+ }
+
+ private List<TagElement> getTagElementsForDummyJavaDocRegion(TagElement snippetTag, JavaDocRegion javaDocRegion) {
+ List<TagElement> tagElements= new ArrayList<>();
+ TextElement textElement= (TextElement) javaDocRegion.fragments().get(0);
+ List<JavaDocRegion> regions= snippetTag.tagRegionsStartingAtTextElement(textElement);
+ List<JavaDocRegion> masterList= snippetTag.tagRegionsContainingTextElement(textElement);
+ masterList.removeAll(regions);
+ for (JavaDocRegion region : masterList) {
+ for (Object tagObj : region.tags()) {
+ tagElements.add((TagElement) tagObj);
+ }
+ }
+ regions.add(javaDocRegion);
+ for (JavaDocRegion region : regions) {
+ for (Object tagObj : region.tags()) {
+ if (tagObj instanceof TagElement) {
+ TagElement tagElem= (TagElement) tagObj;
+ Object prop= tagElem.getProperty(TagProperty.TAG_PROPERTY_SNIPPET_INLINE_TAG_COUNT);
+ if (prop instanceof Integer) {
+ int val = ((Integer)prop).intValue();
+ ListIterator<TagElement> listIterator= tagElements.listIterator();
+ TagElement addBefore= null;
+ while (listIterator.hasNext()) {
+ TagElement tElem= listIterator.next();
+ Object prop2= tElem.getProperty(TagProperty.TAG_PROPERTY_SNIPPET_INLINE_TAG_COUNT);
+ if (prop2 instanceof Integer) {
+ int val2 = ((Integer)prop2).intValue();
+ if (val2 > val) {
+ addBefore= tElem;
+ break;
+ }
+ }
+ }
+ if (addBefore == null) {
+ tagElements.add(tagElem);
+ } else {
+ tagElements.add(tagElements.indexOf(addBefore), tagElem);
+ }
+ }
+ }
+ }
+ }
+
+ return tagElements;
+ }
+
+ private List<TagElement> getTagElementsForTagElement(TagElement snippetTag, TagElement tag) {
+ List<TagElement> tagElements= new ArrayList<>();
+ TextElement textElement= (TextElement) tag.fragments().get(0);
+ List<JavaDocRegion> regions= snippetTag.tagRegionsStartingAtTextElement(textElement);
+ List<JavaDocRegion> masterList= snippetTag.tagRegionsContainingTextElement(textElement);
+ masterList.removeAll(regions);
+ for (JavaDocRegion region : masterList) {
+ for (Object tagObj : region.tags()) {
+ tagElements.add((TagElement) tagObj);
+ }
+ }
+ for (JavaDocRegion region : regions) {
+ for (Object tagObj : region.tags()) {
+ if (tagObj instanceof TagElement) {
+ TagElement tagElem= (TagElement) tagObj;
+ Object prop= tagElem.getProperty(TagProperty.TAG_PROPERTY_SNIPPET_INLINE_TAG_COUNT);
+ if (prop instanceof Integer) {
+ int val = ((Integer)prop).intValue();
+ ListIterator<TagElement> listIterator= tagElements.listIterator();
+ TagElement addBefore= null;
+ while (listIterator.hasNext()) {
+ TagElement tElem= listIterator.next();
+ Object prop2= tElem.getProperty(TagProperty.TAG_PROPERTY_SNIPPET_INLINE_TAG_COUNT);
+ if (prop2 instanceof Integer) {
+ int val2 = ((Integer)prop).intValue();
+ if (val2 > val) {
+ addBefore= tElem;
+ break;
+ }
+ }
+ }
+ if (addBefore == null) {
+ tagElements.add(tagElem);
+ } else {
+ tagElements.add(tagElements.indexOf(addBefore), tagElem);
+ }
+ }
+ }
+ }
+ }
+ Object prop3= tag.getProperty(TagProperty.TAG_PROPERTY_SNIPPET_INLINE_TAG_COUNT);
+ if (prop3 instanceof Integer) {
+ int val = ((Integer)prop3).intValue();
+ ListIterator<TagElement> listIterator= tagElements.listIterator();
+ TagElement addBefore= null;
+ while (listIterator.hasNext()) {
+ TagElement tElem= listIterator.next();
+ Object prop2= tElem.getProperty(TagProperty.TAG_PROPERTY_SNIPPET_INLINE_TAG_COUNT);
+ if (prop2 instanceof Integer) {
+ int val2 = ((Integer)prop2).intValue();
+ if (val2 > val) {
+ addBefore= tElem;
+ break;
+ }
+ }
+ }
+ if (addBefore == null) {
+ tagElements.add(tag);
+ } else {
+ tagElements.add(tagElements.indexOf(addBefore), tag);
+ }
+ }
+ return tagElements;
+ }
+
+ private void handleSnippetHighlight(String text, TagElement tagElement, List<ActionElement> actionElements) {
+ try {
+ List<? extends TagProperty> tagProperties= tagElement.tagProperties();
+ String defHighlight= getHighlightHtmlTag(tagProperties);
+ String startDefHighlight = '<' + defHighlight + '>';
+ String endDefHighlight = "</" + defHighlight + '>'; //$NON-NLS-1$
+ String regExValue = getPropertyValue("regex", tagProperties); //$NON-NLS-1$
+ String subStringValue = getPropertyValue("substring", tagProperties); //$NON-NLS-1$
+ Pattern regexPattern = null;
+ if (regExValue != null) {
+ regexPattern = Pattern.compile(regExValue);
+ }
+ if (regexPattern != null) {
+ Matcher matcher = regexPattern.matcher(text);
+ while (matcher.find()) {
+ actionElements.add(new ActionElement(matcher.start(), matcher.end(), startDefHighlight, endDefHighlight));
+ }
+ } else if (subStringValue != null) {
+ int startIndex = 0;
+ while ((startIndex = text.indexOf(subStringValue, startIndex)) != -1) {
+ actionElements.add(new ActionElement(startIndex, startIndex + subStringValue.length(), startDefHighlight, endDefHighlight));
+ startIndex += subStringValue.length();
+ }
+ } else {
+ actionElements.add(new ActionElement(0, text.length(), startDefHighlight, endDefHighlight));
+ }
+ return;
+ } catch (PatternSyntaxException e) {
+ // do nothing
+ }
+ return;
+ }
+
+ private String handleSnippetReplace(String text, TagElement tagElement, List<ActionElement> actionElements) {
+ try {
+ List<? extends TagProperty> tagProperties= tagElement.tagProperties();
+ String regExValue = getPropertyValue("regex", tagProperties); //$NON-NLS-1$
+ String subStringValue = getPropertyValue("substring", tagProperties); //$NON-NLS-1$
+ String substitution = getPropertyValue("replacement", tagProperties); //$NON-NLS-1$
+ Pattern regexPattern = null;
+ if (regExValue != null) {
+ regexPattern = Pattern.compile(regExValue);
+ }
+ String modifiedText = text;
+ if (regexPattern != null) {
+ Matcher matcher = regexPattern.matcher(modifiedText);
+ StringBuilder strBuild= new StringBuilder();
+ int finalMatchIndex = 0;
+ while (matcher.find()) {
+ finalMatchIndex = matcher.end();
+ modifyPrevActionItemsReplacement(matcher.start(), matcher.end(), substitution.length(), actionElements);
+ matcher.appendReplacement(strBuild, substitution);
+ }
+ modifiedText = strBuild.toString() + modifiedText.substring(finalMatchIndex);
+ } else if (subStringValue != null) {
+ int startIndex = 0;
+ while ((startIndex = modifiedText.indexOf(subStringValue, startIndex)) != -1) {
+ modifyPrevActionItemsReplacement(startIndex, startIndex+ subStringValue.length(), substitution.length(), actionElements);
+ modifiedText = modifiedText.substring(0, startIndex) + substitution + modifiedText.substring(startIndex + subStringValue.length());
+ startIndex += substitution.length() ;
+ }
+ } else {
+ actionElements.clear();
+ modifiedText = substitution;
+ }
+ return modifiedText;
+ } catch (PatternSyntaxException e) {
+ // do nothing
+ }
+ return text;
+ }
+
+
+ private void handleSnippetLink(String text, TagElement tagElement, List<ActionElement> actionElements) {
+ try {
+ List<? extends TagProperty> tagProperties= tagElement.tagProperties();
+ String regExValue = getPropertyValue("regex", tagProperties); //$NON-NLS-1$
+ String subStringValue = getPropertyValue("substring", tagProperties); //$NON-NLS-1$
+ String additionalStartTag= getLinkHtmlTag(tagProperties);
+ String additionalEndTag= ""; //$NON-NLS-1$
+ if (additionalStartTag.length() > 0) {
+ additionalEndTag= "</" + additionalStartTag + '>'; //$NON-NLS-1$
+ additionalStartTag= '<' + additionalStartTag + '>';
+ }
+ ASTNode target = getPropertyNodeValue("target", tagProperties); //$NON-NLS-1$
+ String linkRefTxt = getLinkRef(target);
+ String startDefLink = linkRefTxt + additionalStartTag;
+ String endDefLink = additionalEndTag+"</a>"; //$NON-NLS-1$
+ Pattern regexPattern = null;
+ if (regExValue != null) {
+ regexPattern = Pattern.compile(regExValue);
+ }
+ String modifiedText = text;
+ if (regexPattern != null) {
+ Matcher matcher = regexPattern.matcher(modifiedText);
+ while (matcher.find()) {
+ actionElements.add(new ActionElement(matcher.start(), matcher.end(), startDefLink, endDefLink));
+ }
+ } else if (subStringValue != null) {
+ int startIndex = 0;
+ while ((startIndex = modifiedText.indexOf(subStringValue, startIndex)) != -1) {
+ actionElements.add(new ActionElement(startIndex, startIndex+ subStringValue.length(), startDefLink, endDefLink));
+ startIndex = startIndex+ subStringValue.length() ;
+ }
+ } else {
+ String subText = modifiedText.trim();
+ if (subText.length() < text.length()) {
+ int startIndex = text.indexOf(subText);
+ actionElements.add(new ActionElement(startIndex, startIndex + subText.length(), startDefLink, endDefLink));
+ }
+ }
+ return;
+ } catch (PatternSyntaxException e) {
+ // do nothing
+ }
+ return;
+ }
+
+ private String getLinkRef(ASTNode node) {
+ String str= ""; //$NON-NLS-1$
+ String refTypeName= null;
+ String refMemberName= null;
+ String[] refMethodParamTypes= null;
+ String[] refMethodParamNames= null;
+ if (node instanceof Name) {
+ Name name = (Name) node;
+ refTypeName= name.getFullyQualifiedName();
+ } else if (node instanceof MemberRef) {
+ MemberRef memberRef= (MemberRef) node;
+ Name qualifier= memberRef.getQualifier();
+ refTypeName= qualifier == null ? "" : qualifier.getFullyQualifiedName(); //$NON-NLS-1$
+ refMemberName= memberRef.getName().getIdentifier();
+ } else if (node instanceof MethodRef) {
+ MethodRef methodRef= (MethodRef) node;
+ Name qualifier= methodRef.getQualifier();
+ refTypeName= qualifier == null ? "" : qualifier.getFullyQualifiedName(); //$NON-NLS-1$
+ refMemberName= methodRef.getName().getIdentifier();
+ List<MethodRefParameter> params= methodRef.parameters();
+ int ps= params.size();
+ refMethodParamTypes= new String[ps];
+ refMethodParamNames= new String[ps];
+ for (int i= 0; i < ps; i++) {
+ MethodRefParameter param= params.get(i);
+ refMethodParamTypes[i]= ASTNodes.asString(param.getType());
+ SimpleName paramName= param.getName();
+ if (paramName != null)
+ refMethodParamNames[i]= paramName.getIdentifier();
+ }
+ }
+
+ if (refTypeName != null) {
+ str +="<a href='"; //$NON-NLS-1$
+ try {
+ String scheme= JavaElementLinks.JAVADOC_SCHEME;
+ String uri= JavaElementLinks.createURI(scheme, fElement, refTypeName, refMemberName, refMethodParamTypes);
+ str += uri;
+ } catch (URISyntaxException e) {
+ JavaPlugin.log(e);
+ }
+ str += "'>"; //$NON-NLS-1$
+ }
+ return str;
+ }
+
+ private String getLinkHtmlTag(List<? extends ASTNode> tagProperties) {
+ String defaultTag= "code"; //$NON-NLS-1$
+ if (tagProperties != null) {
+ for (ASTNode node : tagProperties) {
+ if (node instanceof TagProperty) {
+ TagProperty tagProp = (TagProperty) node;
+ if ("type".equals(tagProp.getName())) { //$NON-NLS-1$
+ String tagValue = stripQuotes(tagProp.getStringValue());
+ switch (tagValue) {
+ case "linkplain" : //$NON-NLS-1$
+ defaultTag= ""; //$NON-NLS-1$
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ }
+ }
+ return defaultTag;
+ }
+
+ private String getPropertyValue(String property, List<? extends ASTNode> tagProperties) {
+ String defaultTag= null;
+ if (tagProperties != null && property!= null) {
+ for (ASTNode node : tagProperties) {
+ if (node instanceof TagProperty) {
+ TagProperty tagProp = (TagProperty) node;
+ if (property.equals(tagProp.getName())) {
+ defaultTag= stripQuotes(tagProp.getStringValue());
+ break;
+ }
+ }
+ }
+ }
+ return defaultTag;
+ }
+ private ASTNode getPropertyNodeValue(String property, List<? extends ASTNode> tagProperties) {
+ ASTNode defaultTag= null;
+ if (tagProperties != null && property!= null) {
+ for (ASTNode node : tagProperties) {
+ if (node instanceof TagProperty) {
+ TagProperty tagProp = (TagProperty) node;
+ if (property.equals(tagProp.getName())) {
+ defaultTag= tagProp.getNodeValue();
+ break;
+ }
+ }
+ }
+ }
+ return defaultTag;
+ }
+
+ private String stripQuotes (String str) {
+ String newStr = str;
+ if (str != null && str.length() >= 2) {
+ int length = str.length();
+ if ((str.charAt(0) == '"' && str.charAt(length-1) == '"')
+ || (str.charAt(0) == '\'' && str.charAt(length-1) == '\'')) {
+ newStr = str.substring(1, length-1);
+ }
+ }
+ return newStr;
+ }
+
+ private String getHighlightHtmlTag(List<? extends ASTNode> tagProperties) {
+ String defaultTag= "b"; //$NON-NLS-1$
+ if (tagProperties != null) {
+ for (ASTNode node : tagProperties) {
+ if (node instanceof TagProperty) {
+ TagProperty tagProp = (TagProperty) node;
+ if ("type".equals(tagProp.getName())) { //$NON-NLS-1$
+ String tagValue = stripQuotes(tagProp.getStringValue());
+ switch (tagValue) {
+ case "bold" : //$NON-NLS-1$
+ defaultTag= "b"; //$NON-NLS-1$
+ break;
+ case "italic" : //$NON-NLS-1$
+ defaultTag= "i"; //$NON-NLS-1$
+ break;
+ case "highlighted" : //$NON-NLS-1$
+ defaultTag= "mark"; //$NON-NLS-1$
+ break;
+ default :
+ defaultTag= ""; //$NON-NLS-1$
+ break;
+ }
+ break;
+ }
+ }
+ }
+ }
+ return defaultTag;
+ }
+
+}
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/javadoc/JavadocContentAccess2.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/javadoc/JavadocContentAccess2.java
index 369411b..a8bbc67 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/javadoc/JavadocContentAccess2.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/javadoc/JavadocContentAccess2.java
@@ -34,9 +34,6 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -90,7 +87,6 @@
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
-import org.eclipse.jdt.core.dom.JavaDocRegion;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.MemberRef;
import org.eclipse.jdt.core.dom.MethodRef;
@@ -461,6 +457,8 @@
*/
private final IJavaElement fElement;
+ private final JavaDocSnippetStringEvaluator fSnippetStringEvaluator;
+
/**
* The method, or <code>null</code> if {@link #fElement} is not a method where @inheritDoc could
* work.
@@ -482,6 +480,7 @@
Assert.isNotNull(element);
Assert.isTrue(element instanceof IMethod || element instanceof ILocalVariable || element instanceof ITypeParameter);
fElement= element;
+ fSnippetStringEvaluator= new JavaDocSnippetStringEvaluator(fElement);
fMethod= (IMethod) ((element instanceof ILocalVariable || element instanceof ITypeParameter) ? element.getParent() : element);
fJavadoc= javadoc;
fSource= source;
@@ -491,6 +490,7 @@
private JavadocContentAccess2(IJavaElement element, Javadoc javadoc, String source) {
Assert.isTrue(element instanceof IMember || element instanceof IPackageFragment || element instanceof ILocalVariable || element instanceof ITypeParameter);
fElement= element;
+ fSnippetStringEvaluator= new JavaDocSnippetStringEvaluator(fElement);
fMethod= null;
fJavadoc= javadoc;
fSource= source;
@@ -1931,22 +1931,7 @@
if (fs > 0) {
fBuf.append("<pre><code>"); //$NON-NLS-1$
fBuf.append(BlOCK_TAG_ENTRY_START);
- for(Object fragment : node.fragments()) {
- if (fragment instanceof TextElement) {
- TextElement memberRef= (TextElement) fragment;
- fBuf.append(getTextElementString(node, memberRef));
- } else if (fragment instanceof TagElement) {
- TagElement tagElem= (TagElement) fragment;
- String str= getSnippetTagElementString(node, tagElem);
- fBuf.append(str);
- } else if (fragment instanceof JavaDocRegion) {
- JavaDocRegion region= (JavaDocRegion) fragment;
- if (region.isDummyRegion()) {
- String str = getDummyJavadocRegionString(region, node);
- fBuf.append(str);
- }
- }
- }
+ fSnippetStringEvaluator.AddTagElementString(node, fBuf);
fBuf.append(BlOCK_TAG_ENTRY_END);
}
} else {
@@ -1955,474 +1940,11 @@
}
}
- private String getTextElementString(TagElement snippetTag, TextElement textElem) {
- String text = textElem.getText();
- String changedText = text;
- List<JavaDocRegion> regions= snippetTag.tagRegionsContainingTextElement(textElem);
- for (JavaDocRegion region : regions) {
- changedText = getJavaDocRegionString(region, changedText);
- }
- return changedText;
- }
-
- private String getJavaDocRegionString(JavaDocRegion region, String str) {
- String changedText = ""; //$NON-NLS-1$
- if (region != null && str != null) {
- changedText = str;
- List<Object> tags= region.tags();
- for (Object tag : tags) {
- if (tag instanceof TagElement) {
- TagElement tagElem = (TagElement) tag;
- String name= tagElem.getTagName();
- if (TagElement.TAG_HIGHLIGHT.equals(name)) {
- changedText= handleSnippetHighlight(changedText, tagElem.tagProperties());
- } else if (TagElement.TAG_REPLACE.equals(name)) {
- changedText= handleSnippetReplace(changedText, tagElem.tagProperties());
- } else if (TagElement.TAG_LINK.equals(name)) {
- changedText= handleSnippetLink(changedText, tagElem.tagProperties());
- }
- }
- }
- }
- return changedText;
- }
-
- private String getSnippetTagElementString(TagElement snippetTag, TagElement tagElem) {
- String str= ""; //$NON-NLS-1$
- if (tagElem != null) {
- List<Object> fragments = tagElem.fragments();
- for (Object fragment : fragments) {
- if (fragment instanceof TextElement) {
- str = ((TextElement)fragment).getText();
- List<JavaDocRegion> regions= getRegionsBeforeASTNode(snippetTag,(TextElement)fragment, tagElem);
- for (JavaDocRegion region : regions) {
- str = getJavaDocRegionString(region, str);
- }
- String name= tagElem.getTagName();
- if (TagElement.TAG_HIGHLIGHT.equals(name)) {
- str= handleSnippetHighlight(str, tagElem.tagProperties());
- } else if (TagElement.TAG_REPLACE.equals(name)) {
- str= handleSnippetReplace(str, tagElem.tagProperties());
- } else if (TagElement.TAG_LINK.equals(name)) {
- str= handleSnippetLink(str, tagElem.tagProperties());
- }
- regions= getRegionsAfterASTNode(snippetTag,(TextElement)fragment, tagElem);
- for (JavaDocRegion region : regions) {
- str = getJavaDocRegionString(region, str);
- }
- }
- }
-
- }
- return str;
- }
-
- private List<JavaDocRegion> getRegionsBeforeASTNode(TagElement snippetTag,TextElement textElement, ASTNode node) {
- List<JavaDocRegion> regions= snippetTag.tagRegionsContainingTextElement(textElement);
- List<JavaDocRegion> regionsBefore = new ArrayList<>();
- int nodeStart = node.getStartPosition();
- for (JavaDocRegion region : regions) {
- for (Object tagObj : region.tags()) {
- if (tagObj instanceof TagElement) {
- int start = ((TagElement)tagObj).getStartPosition();
- if (start < nodeStart) {
- regionsBefore.add(region);
- break;
- }
- }
- }
- }
- return regionsBefore;
- }
-
- private List<JavaDocRegion> getRegionsAfterASTNode(TagElement snippetTag,TextElement textElement, ASTNode node) {
- List<JavaDocRegion> regions= snippetTag.tagRegionsContainingTextElement(textElement);
- List<JavaDocRegion> regionsAfter = new ArrayList<>();
- int nodeStart = node.getStartPosition();
- for (JavaDocRegion region : regions) {
- for (Object tagObj : region.tags()) {
- if (tagObj instanceof TagElement) {
- int start = ((TagElement)tagObj).getStartPosition();
- if (start > nodeStart) {
- regionsAfter.add(region);
- break;
- }
- }
- }
- }
- return regionsAfter;
- }
-
- private String getDummyJavadocRegionString(JavaDocRegion jregion, TagElement snippetTag) {
- String str= ""; //$NON-NLS-1$
- if (jregion != null) {
- List<Object> tags = jregion.tags();
- List<Object> fragments = jregion.fragments();
- if (fragments.size() == 0) {
- return str;
- }
- for (Object fragment: fragments) {
- if (fragment instanceof TextElement) {
- String textStr = ((TextElement)fragment).getText();
- List<JavaDocRegion> regions= getRegionsBeforeASTNode(snippetTag,(TextElement)fragment, jregion);
- for (JavaDocRegion region : regions) {
- textStr = getJavaDocRegionString(region, textStr);
- }
- if (textStr.length() > 0 && tags.size() > 0) {
- for (Object tag : tags) {
- if (tag instanceof TagElement) {
- TagElement tagElem= (TagElement) tag;
- String name= tagElem.getTagName();
- if (TagElement.TAG_HIGHLIGHT.equals(name)) {
- textStr= handleSnippetHighlight(textStr, tagElem.tagProperties());
- } else if (TagElement.TAG_REPLACE.equals(name)) {
- textStr= handleSnippetReplace(textStr, tagElem.tagProperties());
- } else if (TagElement.TAG_LINK.equals(name)) {
- textStr= handleSnippetLink(textStr, tagElem.tagProperties());
- }
- }
- }
- }
- regions= getRegionsAfterASTNode(snippetTag,(TextElement)fragment, jregion);
- for (JavaDocRegion region : regions) {
- textStr = getJavaDocRegionString(region, textStr);
- }
- str += textStr;
- }
- if (fragment instanceof JavaDocRegion) {
- str += getDummyJavadocRegionString((JavaDocRegion)fragment, snippetTag);
- }
- }
- }
- return str;
- }
-
private void handleInvalidSnippet() {
fBuf.append("<pre><code>\n"); //$NON-NLS-1$
fBuf.append("<mark>invalid @Snippet</mark>"); //$NON-NLS-1$
}
- private String handleSnippetReplace(String text, List<? extends ASTNode> tagProperties) {
- try {
- boolean process = arePropertyValuesStringLiterals(tagProperties) ? true : false;
- String regExValue = getPropertyValue("regex", tagProperties); //$NON-NLS-1$
- String subStringValue = getPropertyValue("substring", tagProperties); //$NON-NLS-1$
- String substitution = getPropertyValue("replacement", tagProperties); //$NON-NLS-1$
- Pattern regexPattern = null;
- if (regExValue != null) {
- regexPattern = Pattern.compile(regExValue);
- }
- String modifiedText = text;
- if (process) {
- if (regexPattern != null && process) {
- Matcher matcher = regexPattern.matcher(modifiedText);
- StringBuilder strBuild= new StringBuilder();
- int finalMatchIndex = 0;
- while (matcher.find()) {
- finalMatchIndex = matcher.end();
- matcher.appendReplacement(strBuild, substitution);
- }
- modifiedText = strBuild.toString() + modifiedText.substring(finalMatchIndex);
- } else if (subStringValue != null) {
- int startIndex = 0;
- while (true) {
- startIndex = modifiedText.indexOf(subStringValue, startIndex);
- if (startIndex == -1) {
- break;
- } else {
- modifiedText = modifiedText.substring(0, startIndex) + substitution + modifiedText.substring(startIndex + subStringValue.length());
- startIndex = startIndex + substitution.length() ;
- }
- }
- } else {
- modifiedText = substitution;
- }
- }
- return modifiedText;
- } catch (PatternSyntaxException e) {
- // do nothing
- }
- return text;
- }
-
- private String handleSnippetHighlight(String text, List<? extends ASTNode> tagProperties) {
- try {
- String defHighlight= getHighlightHtmlTag(tagProperties);
- String startDefHighlight = '<' + defHighlight + '>';
- String endDefHighlight = "</" + defHighlight + '>'; //$NON-NLS-1$
- boolean process = true;
- if (defHighlight.length() == 0 || !arePropertyValuesStringLiterals(tagProperties)) {
- process = false;
- }
- String regExValue = getPropertyValue("regex", tagProperties); //$NON-NLS-1$
- String subStringValue = getPropertyValue("substring", tagProperties); //$NON-NLS-1$
- int additionalLength = startDefHighlight.length() + endDefHighlight.length();
- Pattern regexPattern = null;
- if (regExValue != null) {
- regexPattern = Pattern.compile(regExValue);
- }
- String modifiedText = text;
- if (process) {
- if (regexPattern != null && process) {
- Matcher matcher = regexPattern.matcher(modifiedText);
- StringBuilder strBuild= new StringBuilder();
- int finalMatchIndex = 0;
- while (matcher.find()) {
- finalMatchIndex = matcher.end();
- String replacementStr= startDefHighlight + modifiedText.substring(matcher.start(), matcher.end()) + endDefHighlight;
- matcher.appendReplacement(strBuild, replacementStr);
- }
- modifiedText = strBuild.toString() + modifiedText.substring(finalMatchIndex);
- } else if (subStringValue != null) {
- int startIndex = 0;
- while (true) {
- startIndex = modifiedText.indexOf(subStringValue, startIndex);
- if (startIndex == -1) {
- break;
- } else {
- modifiedText = modifiedText.substring(0, startIndex) + startDefHighlight + subStringValue + endDefHighlight + modifiedText.substring(startIndex + subStringValue.length());
- startIndex = startIndex + subStringValue.length() + additionalLength;
- }
- }
- } else {
- modifiedText = startDefHighlight + modifiedText + endDefHighlight;
- }
- }
- return modifiedText;
- } catch (PatternSyntaxException e) {
- // do nothing
- }
- return text;
- }
-
- private String handleSnippetLink(String text, List<? extends ASTNode> tagProperties) {
- try {
- String regExValue = getPropertyValue("regex", tagProperties); //$NON-NLS-1$
- String subStringValue = getPropertyValue("substring", tagProperties); //$NON-NLS-1$
- String additionalStartTag= getLinkHtmlTag(tagProperties);
- String additionalEndTag= ""; //$NON-NLS-1$
- if (additionalStartTag.length() > 0) {
- additionalEndTag= "</" + additionalStartTag + '>'; //$NON-NLS-1$
- additionalStartTag= '<' + additionalStartTag + '>';
- }
- ASTNode target = getPropertyNodeValue("target", tagProperties); //$NON-NLS-1$
- String linkRefTxt = getLinkRef(target);
- String startDefLink = linkRefTxt + additionalStartTag;
- String endDefLink = additionalEndTag+"</a>"; //$NON-NLS-1$
- int additionalLength = startDefLink.length() + endDefLink.length();
- Pattern regexPattern = null;
- if (regExValue != null) {
- regexPattern = Pattern.compile(regExValue);
- }
- String modifiedText = text;
- if (regexPattern != null) {
- Matcher matcher = regexPattern.matcher(modifiedText);
- StringBuilder strBuild= new StringBuilder();
- int finalMatchIndex = 0;
- while (matcher.find()) {
- finalMatchIndex = matcher.end();
- String replacementStr= startDefLink + modifiedText.substring(matcher.start(), matcher.end()) + endDefLink;
- matcher.appendReplacement(strBuild, replacementStr);
- }
- modifiedText = strBuild.toString() + modifiedText.substring(finalMatchIndex);
- } else if (subStringValue != null) {
- int startIndex = 0;
- while (true) {
- startIndex = modifiedText.indexOf(subStringValue, startIndex);
- if (startIndex == -1) {
- break;
- } else {
- modifiedText = modifiedText.substring(0, startIndex) + startDefLink + subStringValue + endDefLink + modifiedText.substring(startIndex + subStringValue.length());
- startIndex = startIndex + subStringValue.length() + additionalLength;
- }
- }
- } else {
- String subText = modifiedText.trim();
- String pre = ""; //$NON-NLS-1$
- String post = ""; //$NON-NLS-1$
- if (subText.length() < text.length()) {
- int index = text.indexOf(subText);
- if (index > 0) {
- pre = text.substring(0, index);
- post = text.substring(index + subText.length());
- }
- }
- modifiedText = pre + startDefLink + subText + endDefLink + post;
- }
- return modifiedText;
- } catch (PatternSyntaxException e) {
- // do nothing
- }
- return text;
- }
-
- private String getLinkRef(ASTNode node) {
- String str= ""; //$NON-NLS-1$
- String refTypeName= null;
- String refMemberName= null;
- String[] refMethodParamTypes= null;
- String[] refMethodParamNames= null;
- if (node instanceof Name) {
- Name name = (Name) node;
- refTypeName= name.getFullyQualifiedName();
- } else if (node instanceof MemberRef) {
- MemberRef memberRef= (MemberRef) node;
- Name qualifier= memberRef.getQualifier();
- refTypeName= qualifier == null ? "" : qualifier.getFullyQualifiedName(); //$NON-NLS-1$
- refMemberName= memberRef.getName().getIdentifier();
- } else if (node instanceof MethodRef) {
- MethodRef methodRef= (MethodRef) node;
- Name qualifier= methodRef.getQualifier();
- refTypeName= qualifier == null ? "" : qualifier.getFullyQualifiedName(); //$NON-NLS-1$
- refMemberName= methodRef.getName().getIdentifier();
- List<MethodRefParameter> params= methodRef.parameters();
- int ps= params.size();
- refMethodParamTypes= new String[ps];
- refMethodParamNames= new String[ps];
- for (int i= 0; i < ps; i++) {
- MethodRefParameter param= params.get(i);
- refMethodParamTypes[i]= ASTNodes.asString(param.getType());
- SimpleName paramName= param.getName();
- if (paramName != null)
- refMethodParamNames[i]= paramName.getIdentifier();
- }
- }
-
- if (refTypeName != null) {
- str +="<a href='"; //$NON-NLS-1$
- try {
- String scheme= JavaElementLinks.JAVADOC_SCHEME;
- String uri= JavaElementLinks.createURI(scheme, fElement, refTypeName, refMemberName, refMethodParamTypes);
- str += uri;
- } catch (URISyntaxException e) {
- JavaPlugin.log(e);
- }
- str += "'>"; //$NON-NLS-1$
- }
- return str;
- }
-
- private String getHighlightHtmlTag(List<? extends ASTNode> tagProperties) {
- String defaultTag= "b"; //$NON-NLS-1$
- if (tagProperties != null) {
- for (ASTNode node : tagProperties) {
- if (node instanceof TagProperty) {
- TagProperty tagProp = (TagProperty) node;
- if ("type".equals(tagProp.getName())) { //$NON-NLS-1$
- String tagValue = stripQuotes(tagProp.getStringValue());
- switch (tagValue) {
- case "bold" : //$NON-NLS-1$
- defaultTag= "b"; //$NON-NLS-1$
- break;
- case "italic" : //$NON-NLS-1$
- defaultTag= "i"; //$NON-NLS-1$
- break;
- case "highlighted" : //$NON-NLS-1$
- defaultTag= "mark"; //$NON-NLS-1$
- break;
- default :
- defaultTag= ""; //$NON-NLS-1$
- break;
- }
- break;
- }
- }
- }
- }
- return defaultTag;
- }
-
- private String getLinkHtmlTag(List<? extends ASTNode> tagProperties) {
- String defaultTag= "code"; //$NON-NLS-1$
- if (tagProperties != null) {
- for (ASTNode node : tagProperties) {
- if (node instanceof TagProperty) {
- TagProperty tagProp = (TagProperty) node;
- if ("type".equals(tagProp.getName())) { //$NON-NLS-1$
- String tagValue = stripQuotes(tagProp.getStringValue());
- switch (tagValue) {
- case "linkplain" : //$NON-NLS-1$
- defaultTag= ""; //$NON-NLS-1$
- break;
- default:
- break;
- }
- break;
- }
- }
- }
- }
- return defaultTag;
- }
-
- private boolean arePropertyValuesStringLiterals(List<? extends ASTNode> tagProperties) {
- boolean val= true;
- if (tagProperties != null) {
- final String SUBSTRING = "substring"; //$NON-NLS-1$
- final String REGEX = "regex"; //$NON-NLS-1$
- final String TYPE = "type"; //$NON-NLS-1$
- for (ASTNode node : tagProperties) {
- if (node instanceof TagProperty) {
- TagProperty tagProp = (TagProperty) node;
- String propName = tagProp.getName();
- if (SUBSTRING.equals(propName)
- || REGEX.equals(propName)
- || TYPE.equals(propName)) {
- String value= tagProp.getStringValue();
- String changed= stripQuotes(value);
- if (changed.equals(value)) {
- val= false;
- break;
- }
- }
- }
- }
- }
- return val;
- }
- private String getPropertyValue(String property, List<? extends ASTNode> tagProperties) {
- String defaultTag= null;
- if (tagProperties != null && property!= null) {
- for (ASTNode node : tagProperties) {
- if (node instanceof TagProperty) {
- TagProperty tagProp = (TagProperty) node;
- if (property.equals(tagProp.getName())) {
- defaultTag= stripQuotes(tagProp.getStringValue());
- break;
- }
- }
- }
- }
- return defaultTag;
- }
- private ASTNode getPropertyNodeValue(String property, List<? extends ASTNode> tagProperties) {
- ASTNode defaultTag= null;
- if (tagProperties != null && property!= null) {
- for (ASTNode node : tagProperties) {
- if (node instanceof TagProperty) {
- TagProperty tagProp = (TagProperty) node;
- if (property.equals(tagProp.getName())) {
- defaultTag= tagProp.getNodeValue();
- break;
- }
- }
- }
- }
- return defaultTag;
- }
-
- private String stripQuotes (String str) {
- String newStr = str;
- if (str != null && str.length() >= 2) {
- int length = str.length();
- if ((str.charAt(0) == '"' && str.charAt(length-1) == '"')
- || (str.charAt(0) == '\'' && str.charAt(length-1) == '\'')) {
- newStr = str.substring(1, length-1);
- }
- }
- return newStr;
- }
-
private void handleIndex(List<? extends ASTNode> fragments) {
int fs= fragments.size();
if (fs > 0) {