519262: copy org.eclipse.ui.internal.misc.StringMatcher

The find(String, int, int) was removed in Photon. In order to retain
compatibility with previous versions and also work on Photon, we'll keep
a copy of the old version.

Change-Id: I1682f7e94e2b730fbec408809b5fc99ae8db1e9d
Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=519262
diff --git a/org.eclipse.mylyn.commons.workbench/src/org/eclipse/mylyn/commons/workbench/DecoratingPatternStyledCellLabelProvider.java b/org.eclipse.mylyn.commons.workbench/src/org/eclipse/mylyn/commons/workbench/DecoratingPatternStyledCellLabelProvider.java
index c766793..28b2d05 100644
--- a/org.eclipse.mylyn.commons.workbench/src/org/eclipse/mylyn/commons/workbench/DecoratingPatternStyledCellLabelProvider.java
+++ b/org.eclipse.mylyn.commons.workbench/src/org/eclipse/mylyn/commons/workbench/DecoratingPatternStyledCellLabelProvider.java
@@ -26,6 +26,8 @@
 import org.eclipse.jface.viewers.StyledString;
 import org.eclipse.jface.viewers.StyledString.Styler;
 import org.eclipse.jface.viewers.ViewerColumn;
+import org.eclipse.mylyn.internal.commons.workbench.StringMatcher;
+import org.eclipse.mylyn.internal.commons.workbench.StringMatcher.Position;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.StyleRange;
 import org.eclipse.swt.graphics.Color;
@@ -33,19 +35,17 @@
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.ui.IWorkbenchPreferenceConstants;
 import org.eclipse.ui.PlatformUI;
-import org.eclipse.ui.internal.misc.StringMatcher;
-import org.eclipse.ui.internal.misc.StringMatcher.Position;
 
 /**
  * A decorating styled label provider that supports highlighting of substrings that match a pattern. Based on
  * {@link DecoratingFileSearchLabelProvider}.
- * 
+ *
  * @author Kevin Sawicki
  * @see DecoratingFileSearchLabelProvider
  * @since 3.7
  */
-public class DecoratingPatternStyledCellLabelProvider extends DecoratingStyledCellLabelProvider implements
-		IPropertyChangeListener, ILabelProvider {
+public class DecoratingPatternStyledCellLabelProvider extends DecoratingStyledCellLabelProvider
+		implements IPropertyChangeListener, ILabelProvider {
 
 	/**
 	 * Color to use to decorate matches.
@@ -54,8 +54,8 @@
 
 	static final Styler HIGHLIGHT_STYLE = StyledString.createColorRegistryStyler(null, HIGHLIGHT_BG_COLOR_NAME);
 
-	private static class PatternStyledLabelProvider extends StyledCellLabelProvider implements IStyledLabelProvider,
-			IColorProvider, IFontProvider {
+	private static class PatternStyledLabelProvider extends StyledCellLabelProvider
+			implements IStyledLabelProvider, IColorProvider, IFontProvider {
 
 		private final ILabelProvider labelProvider;
 
@@ -67,7 +67,7 @@
 
 		/**
 		 * Set the pattern to highlight
-		 * 
+		 *
 		 * @param pattern
 		 */
 		public void setPattern(String pattern) {
@@ -134,7 +134,7 @@
 
 	/**
 	 * Create a new repository search styled label provider that wraps an {@link ILabelProvider}
-	 * 
+	 *
 	 * @param labelProvider
 	 * @param decorator
 	 * @param decorationContext
@@ -154,15 +154,15 @@
 		PlatformUI.getPreferenceStore().addPropertyChangeListener(this);
 		JFaceResources.getColorRegistry().addListener(this);
 
-		setOwnerDrawEnabled(PlatformUI.getPreferenceStore()
-				.getBoolean(IWorkbenchPreferenceConstants.USE_COLORED_LABELS));
+		setOwnerDrawEnabled(
+				PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.USE_COLORED_LABELS));
 
 		super.initialize(viewer, column);
 	}
 
 	/**
 	 * Get underyling label provider
-	 * 
+	 *
 	 * @return label provider
 	 */
 	public ILabelProvider getLabelProvider() {
@@ -182,7 +182,7 @@
 
 	/**
 	 * Set the pattern to highlight
-	 * 
+	 *
 	 * @param pattern
 	 */
 	public void setPattern(String pattern) {
@@ -195,8 +195,8 @@
 	protected void refresh() {
 		ColumnViewer viewer = getViewer();
 		if (viewer != null) {
-			boolean coloredLabels = PlatformUI.getPreferenceStore().getBoolean(
-					IWorkbenchPreferenceConstants.USE_COLORED_LABELS);
+			boolean coloredLabels = PlatformUI.getPreferenceStore()
+					.getBoolean(IWorkbenchPreferenceConstants.USE_COLORED_LABELS);
 			if (coloredLabels || coloredLabels != isOwnerDrawEnabled()) {
 				setOwnerDrawEnabled(coloredLabels);
 				viewer.refresh();
@@ -228,7 +228,7 @@
 
 	/**
 	 * Get text of element from underyling label provider
-	 * 
+	 *
 	 * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
 	 */
 	public String getText(Object element) {
@@ -237,7 +237,7 @@
 
 	/**
 	 * Override preparation of style range to add border dot about highlight regions that don't have colors applied
-	 * 
+	 *
 	 * @see org.eclipse.jface.viewers.StyledCellLabelProvider#prepareStyleRange(org.eclipse.swt.custom.StyleRange,
 	 *      boolean)
 	 */
diff --git a/org.eclipse.mylyn.commons.workbench/src/org/eclipse/mylyn/internal/commons/workbench/StringMatcher.java b/org.eclipse.mylyn.commons.workbench/src/org/eclipse/mylyn/internal/commons/workbench/StringMatcher.java
new file mode 100644
index 0000000..6bcf2e2
--- /dev/null
+++ b/org.eclipse.mylyn.commons.workbench/src/org/eclipse/mylyn/internal/commons/workbench/StringMatcher.java
@@ -0,0 +1,454 @@
+package org.eclipse.mylyn.internal.commons.workbench;
+
+import java.util.Vector;
+
+/**
+ * A string pattern matcher, suppporting "*" and "?" wildcards. Copied from org.eclipse.ui.internal.misc.StringMatcher.
+ */
+public class StringMatcher {
+	protected String fPattern;
+
+	protected int fLength; // pattern length
+
+	protected boolean fIgnoreWildCards;
+
+	protected boolean fIgnoreCase;
+
+	protected boolean fHasLeadingStar;
+
+	protected boolean fHasTrailingStar;
+
+	protected String fSegments[]; //the given pattern is split into * separated segments
+
+	/* boundary value beyond which we don't need to search in the text */
+	protected int fBound = 0;
+
+	protected static final char fSingleWildCard = '\u0000';
+
+	public static class Position {
+		int start; //inclusive
+
+		int end; //exclusive
+
+		public Position(int start, int end) {
+			this.start = start;
+			this.end = end;
+		}
+
+		public int getStart() {
+			return start;
+		}
+
+		public int getEnd() {
+			return end;
+		}
+	}
+
+	/**
+	 * StringMatcher constructor takes in a String object that is a simple pattern which may contain '*' for 0 and many
+	 * characters and '?' for exactly one character. Literal '*' and '?' characters must be escaped in the pattern e.g.,
+	 * "\*" means literal "*", etc. Escaping any other character (including the escape character itself), just results
+	 * in that character in the pattern. e.g., "\a" means "a" and "\\" means "\" If invoking the StringMatcher with
+	 * string literals in Java, don't forget escape characters are represented by "\\".
+	 *
+	 * @param pattern
+	 *            the pattern to match text against
+	 * @param ignoreCase
+	 *            if true, case is ignored
+	 * @param ignoreWildCards
+	 *            if true, wild cards and their escape sequences are ignored (everything is taken literally).
+	 */
+	public StringMatcher(String pattern, boolean ignoreCase, boolean ignoreWildCards) {
+		if (pattern == null) {
+			throw new IllegalArgumentException();
+		}
+		fIgnoreCase = ignoreCase;
+		fIgnoreWildCards = ignoreWildCards;
+		fPattern = pattern;
+		fLength = pattern.length();
+
+		if (fIgnoreWildCards) {
+			parseNoWildCards();
+		} else {
+			parseWildCards();
+		}
+	}
+
+	/**
+	 * Find the first occurrence of the pattern between <code>start</code)(inclusive)
+	 * and <code>end</code>(exclusive).
+	 *
+	 * @param text
+	 *            the String object to search in
+	 * @param start
+	 *            the starting index of the search range, inclusive
+	 * @param end
+	 *            the ending index of the search range, exclusive
+	 * @return an <code>StringMatcher.Position</code> object that keeps the starting (inclusive) and ending positions
+	 *         (exclusive) of the first occurrence of the pattern in the specified range of the text; return null if not
+	 *         found or subtext is empty (start==end). A pair of zeros is returned if pattern is empty string Note that
+	 *         for pattern like "*abc*" with leading and trailing stars, position of "abc" is returned. For a pattern
+	 *         like"*??*" in text "abcdf", (1,3) is returned
+	 */
+	public StringMatcher.Position find(String text, int start, int end) {
+		if (text == null) {
+			throw new IllegalArgumentException();
+		}
+
+		int tlen = text.length();
+		if (start < 0) {
+			start = 0;
+		}
+		if (end > tlen) {
+			end = tlen;
+		}
+		if (end < 0 || start >= end) {
+			return null;
+		}
+		if (fLength == 0) {
+			return new Position(start, start);
+		}
+		if (fIgnoreWildCards) {
+			int x = posIn(text, start, end);
+			if (x < 0) {
+				return null;
+			}
+			return new Position(x, x + fLength);
+		}
+
+		int segCount = fSegments.length;
+		if (segCount == 0) {
+			return new Position(start, end);
+		}
+
+		int curPos = start;
+		int matchStart = -1;
+		int i;
+		for (i = 0; i < segCount && curPos < end; ++i) {
+			String current = fSegments[i];
+			int nextMatch = regExpPosIn(text, curPos, end, current);
+			if (nextMatch < 0) {
+				return null;
+			}
+			if (i == 0) {
+				matchStart = nextMatch;
+			}
+			curPos = nextMatch + current.length();
+		}
+		if (i < segCount) {
+			return null;
+		}
+		return new Position(matchStart, curPos);
+	}
+
+	/**
+	 * match the given <code>text</code> with the pattern
+	 *
+	 * @return true if matched otherwise false
+	 * @param text
+	 *            a String object
+	 */
+	public boolean match(String text) {
+		if (text == null) {
+			return false;
+		}
+		return match(text, 0, text.length());
+	}
+
+	/**
+	 * Given the starting (inclusive) and the ending (exclusive) positions in the <code>text</code>, determine if the
+	 * given substring matches with aPattern
+	 *
+	 * @return true if the specified portion of the text matches the pattern
+	 * @param text
+	 *            a String object that contains the substring to match
+	 * @param start
+	 *            marks the starting position (inclusive) of the substring
+	 * @param end
+	 *            marks the ending index (exclusive) of the substring
+	 */
+	public boolean match(String text, int start, int end) {
+		if (null == text) {
+			throw new IllegalArgumentException();
+		}
+
+		if (start > end) {
+			return false;
+		}
+
+		if (fIgnoreWildCards) {
+			return (end - start == fLength) && fPattern.regionMatches(fIgnoreCase, 0, text, start, fLength);
+		}
+		int segCount = fSegments.length;
+		if (segCount == 0 && (fHasLeadingStar || fHasTrailingStar)) {
+			return true;
+		}
+		if (start == end) {
+			return fLength == 0;
+		}
+		if (fLength == 0) {
+			return start == end;
+		}
+
+		int tlen = text.length();
+		if (start < 0) {
+			start = 0;
+		}
+		if (end > tlen) {
+			end = tlen;
+		}
+
+		int tCurPos = start;
+		int bound = end - fBound;
+		if (bound < 0) {
+			return false;
+		}
+		int i = 0;
+		String current = fSegments[i];
+		int segLength = current.length();
+
+		/* process first segment */
+		if (!fHasLeadingStar) {
+			if (!regExpRegionMatches(text, start, current, 0, segLength)) {
+				return false;
+			} else {
+				++i;
+				tCurPos = tCurPos + segLength;
+			}
+		}
+		if ((fSegments.length == 1) && (!fHasLeadingStar) && (!fHasTrailingStar)) {
+			// only one segment to match, no wildcards specified
+			return tCurPos == end;
+		}
+		/* process middle segments */
+		while (i < segCount) {
+			current = fSegments[i];
+			int currentMatch;
+			int k = current.indexOf(fSingleWildCard);
+			if (k < 0) {
+				currentMatch = textPosIn(text, tCurPos, end, current);
+				if (currentMatch < 0) {
+					return false;
+				}
+			} else {
+				currentMatch = regExpPosIn(text, tCurPos, end, current);
+				if (currentMatch < 0) {
+					return false;
+				}
+			}
+			tCurPos = currentMatch + current.length();
+			i++;
+		}
+
+		/* process final segment */
+		if (!fHasTrailingStar && tCurPos != end) {
+			int clen = current.length();
+			return regExpRegionMatches(text, end - clen, current, 0, clen);
+		}
+		return i == segCount;
+	}
+
+	/**
+	 * This method parses the given pattern into segments seperated by wildcard '*' characters. Since wildcards are not
+	 * being used in this case, the pattern consists of a single segment.
+	 */
+	private void parseNoWildCards() {
+		fSegments = new String[1];
+		fSegments[0] = fPattern;
+		fBound = fLength;
+	}
+
+	/**
+	 * Parses the given pattern into segments seperated by wildcard '*' characters.
+	 *
+	 * @param p,
+	 *            a String object that is a simple regular expression with '*' and/or '?'
+	 */
+	private void parseWildCards() {
+		if (fPattern.startsWith("*")) { //$NON-NLS-1$
+			fHasLeadingStar = true;
+		}
+		if (fPattern.endsWith("*")) {//$NON-NLS-1$
+			/* make sure it's not an escaped wildcard */
+			if (fLength > 1 && fPattern.charAt(fLength - 2) != '\\') {
+				fHasTrailingStar = true;
+			}
+		}
+
+		Vector temp = new Vector();
+
+		int pos = 0;
+		StringBuffer buf = new StringBuffer();
+		while (pos < fLength) {
+			char c = fPattern.charAt(pos++);
+			switch (c) {
+			case '\\':
+				if (pos >= fLength) {
+					buf.append(c);
+				} else {
+					char next = fPattern.charAt(pos++);
+					/* if it's an escape sequence */
+					if (next == '*' || next == '?' || next == '\\') {
+						buf.append(next);
+					} else {
+						/* not an escape sequence, just insert literally */
+						buf.append(c);
+						buf.append(next);
+					}
+				}
+				break;
+			case '*':
+				if (buf.length() > 0) {
+					/* new segment */
+					temp.addElement(buf.toString());
+					fBound += buf.length();
+					buf.setLength(0);
+				}
+				break;
+			case '?':
+				/* append special character representing single match wildcard */
+				buf.append(fSingleWildCard);
+				break;
+			default:
+				buf.append(c);
+			}
+		}
+
+		/* add last buffer to segment list */
+		if (buf.length() > 0) {
+			temp.addElement(buf.toString());
+			fBound += buf.length();
+		}
+
+		fSegments = new String[temp.size()];
+		temp.copyInto(fSegments);
+	}
+
+	/**
+	 * @param text
+	 *            a string which contains no wildcard
+	 * @param start
+	 *            the starting index in the text for search, inclusive
+	 * @param end
+	 *            the stopping point of search, exclusive
+	 * @return the starting index in the text of the pattern , or -1 if not found
+	 */
+	protected int posIn(String text, int start, int end) {//no wild card in pattern
+		int max = end - fLength;
+
+		if (!fIgnoreCase) {
+			int i = text.indexOf(fPattern, start);
+			if (i == -1 || i > max) {
+				return -1;
+			}
+			return i;
+		}
+
+		for (int i = start; i <= max; ++i) {
+			if (text.regionMatches(true, i, fPattern, 0, fLength)) {
+				return i;
+			}
+		}
+
+		return -1;
+	}
+
+	/**
+	 * @param text
+	 *            a simple regular expression that may only contain '?'(s)
+	 * @param start
+	 *            the starting index in the text for search, inclusive
+	 * @param end
+	 *            the stopping point of search, exclusive
+	 * @param p
+	 *            a simple regular expression that may contains '?'
+	 * @return the starting index in the text of the pattern , or -1 if not found
+	 */
+	protected int regExpPosIn(String text, int start, int end, String p) {
+		int plen = p.length();
+
+		int max = end - plen;
+		for (int i = start; i <= max; ++i) {
+			if (regExpRegionMatches(text, i, p, 0, plen)) {
+				return i;
+			}
+		}
+		return -1;
+	}
+
+	/**
+	 * @return boolean
+	 * @param text
+	 *            a String to match
+	 * @param start
+	 *            int that indicates the starting index of match, inclusive
+	 * @param end</code>
+	 *            int that indicates the ending index of match, exclusive
+	 * @param p
+	 *            String, String, a simple regular expression that may contain '?'
+	 * @param ignoreCase
+	 *            boolean indicating wether code>p</code> is case sensitive
+	 */
+	protected boolean regExpRegionMatches(String text, int tStart, String p, int pStart, int plen) {
+		while (plen-- > 0) {
+			char tchar = text.charAt(tStart++);
+			char pchar = p.charAt(pStart++);
+
+			/* process wild cards */
+			if (!fIgnoreWildCards) {
+				/* skip single wild cards */
+				if (pchar == fSingleWildCard) {
+					continue;
+				}
+			}
+			if (pchar == tchar) {
+				continue;
+			}
+			if (fIgnoreCase) {
+				if (Character.toUpperCase(tchar) == Character.toUpperCase(pchar)) {
+					continue;
+				}
+				// comparing after converting to upper case doesn't handle all cases;
+				// also compare after converting to lower case
+				if (Character.toLowerCase(tchar) == Character.toLowerCase(pchar)) {
+					continue;
+				}
+			}
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * @param text
+	 *            the string to match
+	 * @param start
+	 *            the starting index in the text for search, inclusive
+	 * @param end
+	 *            the stopping point of search, exclusive
+	 * @param p
+	 *            a pattern string that has no wildcard
+	 * @return the starting index in the text of the pattern , or -1 if not found
+	 */
+	protected int textPosIn(String text, int start, int end, String p) {
+
+		int plen = p.length();
+		int max = end - plen;
+
+		if (!fIgnoreCase) {
+			int i = text.indexOf(p, start);
+			if (i == -1 || i > max) {
+				return -1;
+			}
+			return i;
+		}
+
+		for (int i = start; i <= max; ++i) {
+			if (text.regionMatches(true, i, p, 0, plen)) {
+				return i;
+			}
+		}
+
+		return -1;
+	}
+}