Bug 560686: [Text] Improve TextParserInput
  - Add method .getRegionInSource
  - Unify method signatures (use endOffset instead of length)

Change-Id: I41ef7e5f0a5d571ee8a57209eabfca70066f49ce
diff --git a/jcommons/org.eclipse.statet.jcommons.text.core-tests/src/org/eclipse/statet/jcommons/text/core/input/HtmlStripParserInputTest.java b/jcommons/org.eclipse.statet.jcommons.text.core-tests/src/org/eclipse/statet/jcommons/text/core/input/HtmlStripParserInputTest.java
index 955ad84..56080e8 100644
--- a/jcommons/org.eclipse.statet.jcommons.text.core-tests/src/org/eclipse/statet/jcommons/text/core/input/HtmlStripParserInputTest.java
+++ b/jcommons/org.eclipse.statet.jcommons.text.core-tests/src/org/eclipse/statet/jcommons/text/core/input/HtmlStripParserInputTest.java
@@ -22,6 +22,7 @@
 import org.junit.jupiter.api.Test;
 
 import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.text.core.BasicTextRegion;
 import org.eclipse.statet.jcommons.text.core.util.HtmlStripParserInput;
 import org.eclipse.statet.jcommons.text.core.util.HtmlUtils;
 import org.eclipse.statet.jcommons.text.core.util.HtmlUtils.Entity;
@@ -52,6 +53,8 @@
 		assertEquals(106, this.input.getIndex());
 		assertChars(s, 100, s.length());
 		assertEquals(sb.length() - 106, this.input.getLengthInSource(s.length() - 100));
+		assertEquals(new BasicTextRegion(106 + 50, 106 + 150 + 7),
+				this.input.getRegionInSource(50, 150) );
 		this.input.consume(s.length() - 100);
 		
 		assertEquals(TextParserInput.EOF, this.input.get(0));
@@ -69,6 +72,8 @@
 		
 		assertEquals(13, this.input.getIndex());
 		assertChars(s);
+		assertEquals(new BasicTextRegion(50 + 13, 150 + 13),
+				this.input.getRegionInSource(50, 150) );
 		
 		assertEquals(TextParserInput.EOF, this.input.get(s.length()));
 	}
@@ -77,13 +82,16 @@
 	public void skipTagWithAttr() {
 		final String s= Utils.COUNTER_STRING;
 		final StringBuilder sb= new StringBuilder(s);
-		sb.insert(100, "<html attr=\"abc\">");
+		final String tag1;
+		sb.insert(100, tag1= "<html attr=\"abc\">");
 		sb.insert(200, "<html attr=\'abc\'>");
 		
 		this.input= new HtmlStripParserInput(sb.toString());
 		this.input.init();
 		
 		assertChars(s);
+		assertEquals(new BasicTextRegion(50, 150 + tag1.length()),
+				this.input.getRegionInSource(50, 150) );
 		
 		assertEquals(TextParserInput.EOF, this.input.get(s.length()));
 	}
@@ -92,14 +100,19 @@
 	public void skipTagWithAttrSpecial() {
 		final String s= Utils.COUNTER_STRING;
 		final StringBuilder sb= new StringBuilder(s);
-		sb.insert(100, "<html attr=\"ab'c\">");
-		sb.insert(200, "<html attr=\'ab\"c\'>");
-		sb.insert(300, "<html attr=\"<ignore\">");
+		final String tag1, tag2, tag3;
+		sb.insert(100, tag1= "<html attr=\"ab'c\">");
+		sb.insert(200, tag2= "<html attr=\'ab\"c\'>");
+		sb.insert(300, tag3= "<html attr=\"<ignore\">");
 		
 		this.input= new HtmlStripParserInput(sb.toString());
 		this.input.init();
 		
 		assertChars(s);
+		assertEquals(new BasicTextRegion(50, 150 + tag1.length()),
+				this.input.getRegionInSource(50, 150) );
+		assertEquals(new BasicTextRegion(150 + tag1.length(), 500 + tag1.length() + tag2.length() + tag3.length()),
+				this.input.getRegionInSource(150, 500) );
 		
 		assertEquals(TextParserInput.EOF, this.input.get(s.length()));
 	}
diff --git a/jcommons/org.eclipse.statet.jcommons.text.core-tests/src/org/eclipse/statet/jcommons/text/core/input/TextParserInputMatchTest.java b/jcommons/org.eclipse.statet.jcommons.text.core-tests/src/org/eclipse/statet/jcommons/text/core/input/TextParserInputMatchTest.java
index 26a2194..eb72720 100644
--- a/jcommons/org.eclipse.statet.jcommons.text.core-tests/src/org/eclipse/statet/jcommons/text/core/input/TextParserInputMatchTest.java
+++ b/jcommons/org.eclipse.statet.jcommons.text.core-tests/src/org/eclipse/statet/jcommons/text/core/input/TextParserInputMatchTest.java
@@ -14,7 +14,8 @@
 
 package org.eclipse.statet.jcommons.text.core.input;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import org.junit.jupiter.api.Test;
 
@@ -28,6 +29,7 @@
 	private StringParserInput input;
 	
 	
+	@SuppressWarnings("null")
 	public TextParserInputMatchTest() {
 	}
 	
@@ -39,7 +41,7 @@
 		this.input.init();
 		
 		this.input.consume('a');
-		assertEquals(true, this.input.get(0) == 'a');
+		assertTrue(this.input.get(0) == 'a');
 	}
 	
 	@Test
@@ -49,11 +51,11 @@
 		this.input.init();
 		
 		this.input.consume('a');
-		assertEquals(true, this.input.matches(0, 'a'));
-		assertEquals(true, this.input.matches(1, 'b'));
-		assertEquals(true, this.input.matches(2, 'c'));
-		assertEquals(false, this.input.matches(0, 'x'));
-		assertEquals(false, this.input.matches(3, 'c'));
+		assertTrue(this.input.matches(0, 'a'));
+		assertTrue(this.input.matches(1, 'b'));
+		assertTrue(this.input.matches(2, 'c'));
+		assertFalse(this.input.matches(0, 'x'));
+		assertFalse(this.input.matches(3, 'c'));
 	}
 	
 	@Test
@@ -63,12 +65,12 @@
 		this.input.init();
 		
 		this.input.consume('a');
-		assertEquals(true, this.input.matches(0, 'a', 'b'));
-		assertEquals(true, this.input.matches(1, 'b', 'c'));
-		assertEquals(true, this.input.matches(2, 'c', 'd'));
-		assertEquals(false, this.input.matches(0, 'x', 'b'));
-		assertEquals(false, this.input.matches(3, 'c', 'e'));
-		assertEquals(false, this.input.matches(3, 'd', 'd'));
+		assertTrue(this.input.matches(0, 'a', 'b'));
+		assertTrue(this.input.matches(1, 'b', 'c'));
+		assertTrue(this.input.matches(2, 'c', 'd'));
+		assertFalse(this.input.matches(0, 'x', 'b'));
+		assertFalse(this.input.matches(3, 'c', 'e'));
+		assertFalse(this.input.matches(3, 'd', 'd'));
 	}
 	
 	@Test
@@ -78,12 +80,12 @@
 		this.input.init();
 		
 		this.input.consume('a');
-		assertEquals(true, this.input.matches(0, 'a', 'b', 'c'));
-		assertEquals(true, this.input.matches(1, 'b', 'c', 'd'));
-		assertEquals(true, this.input.matches(2, 'c', 'd', 'e'));
-		assertEquals(false, this.input.matches(0, 'x', 'b', 'c'));
-		assertEquals(false, this.input.matches(3, 'c', 'e', 'f'));
-		assertEquals(false, this.input.matches(3, 'd', 'e', 'e'));
+		assertTrue(this.input.matches(0, 'a', 'b', 'c'));
+		assertTrue(this.input.matches(1, 'b', 'c', 'd'));
+		assertTrue(this.input.matches(2, 'c', 'd', 'e'));
+		assertFalse(this.input.matches(0, 'x', 'b', 'c'));
+		assertFalse(this.input.matches(3, 'c', 'e', 'f'));
+		assertFalse(this.input.matches(3, 'd', 'e', 'e'));
 	}
 	
 	@Test
@@ -93,15 +95,15 @@
 		this.input.init();
 		
 		this.input.consume('a');
-		assertEquals(true, this.input.matches(0, new char[] { }));
-		assertEquals(true, this.input.matches(0, new char[] { 'a' }));
-		assertEquals(true, this.input.matches(0, new char[] { 'a', 'b', 'c', 'd' }));
-		assertEquals(true, this.input.matches(1, new char[] { 'b' }));
-		assertEquals(true, this.input.matches(1, new char[] { 'b', 'c', 'd', 'e' }));
-		assertEquals(true, this.input.matches(2, new char[] { 'c', 'd', 'e', 'f' }));
-		assertEquals(false, this.input.matches(0, new char[] { 'x', 'b', 'c', 'd' }));
-		assertEquals(false, this.input.matches(3, new char[] { 'c', 'e', 'f', 'g' }));
-		assertEquals(false, this.input.matches(3, new char[] { 'd', 'e', 'f', 'f' }));
+		assertTrue(this.input.matches(0, new char[] { }));
+		assertTrue(this.input.matches(0, new char[] { 'a' }));
+		assertTrue(this.input.matches(0, new char[] { 'a', 'b', 'c', 'd' }));
+		assertTrue(this.input.matches(1, new char[] { 'b' }));
+		assertTrue(this.input.matches(1, new char[] { 'b', 'c', 'd', 'e' }));
+		assertTrue(this.input.matches(2, new char[] { 'c', 'd', 'e', 'f' }));
+		assertFalse(this.input.matches(0, new char[] { 'x', 'b', 'c', 'd' }));
+		assertFalse(this.input.matches(3, new char[] { 'c', 'e', 'f', 'g' }));
+		assertFalse(this.input.matches(3, new char[] { 'd', 'e', 'f', 'f' }));
 	}
 	
 	
diff --git a/jcommons/org.eclipse.statet.jcommons.text.core/src/org/eclipse/statet/jcommons/text/core/input/TextParserInput.java b/jcommons/org.eclipse.statet.jcommons.text.core/src/org/eclipse/statet/jcommons/text/core/input/TextParserInput.java
index 020e79e..ed72f00 100644
--- a/jcommons/org.eclipse.statet.jcommons.text.core/src/org/eclipse/statet/jcommons/text/core/input/TextParserInput.java
+++ b/jcommons/org.eclipse.statet.jcommons.text.core/src/org/eclipse/statet/jcommons/text/core/input/TextParserInput.java
@@ -18,6 +18,8 @@
 import org.eclipse.statet.jcommons.lang.Nullable;
 import org.eclipse.statet.jcommons.string.CharArrayString;
 import org.eclipse.statet.jcommons.string.StringFactory;
+import org.eclipse.statet.jcommons.text.core.BasicTextRegion;
+import org.eclipse.statet.jcommons.text.core.TextRegion;
 
 
 /**
@@ -194,6 +196,17 @@
 		return offset;
 	}
 	
+	/**
+	 * Returns the region in the source text of the context within the specified offsets.
+	 * @param startOffset the start offset in the content (inclusive)
+	 * @param endOffset the end offset in the content (exclusive)
+	 * @return the region
+	 */
+	public TextRegion getRegionInSource(final int startOffset, final int endOffset) {
+		final int startIndex= getIndex(startOffset);
+		return new BasicTextRegion(startIndex, getIndex() + getLengthInSource(endOffset));
+	}
+	
 	
 	/**
 	 * Returns the character at the specified offset in the content.
@@ -272,6 +285,24 @@
 		return true;
 	}
 	
+	public final boolean matchesN(int offset, final char c, final int n) {
+		int idx= this.currentIdx + offset;
+		if (idx + n > this.endIdx) {
+			if (updateBuffer(offset + n)) {
+				idx= this.currentIdx + offset;
+			}
+			else {
+				return false;
+			}
+		}
+		for (offset= 0; offset < n; offset++) {
+			if (this.buffer[idx++] != c) {
+				return false;
+			}
+		}
+		return true;
+	}
+	
 	
 	/**
 	 * Forwards the current index to the specified offset.
@@ -368,42 +399,73 @@
 	}
 	
 	
-	protected final CharArrayString getTmpString(final int offset, final int length) {
-		this.tmpCharString.set(this.buffer, this.currentIdx + offset, length);
+	protected final CharArrayString getTmpString(final int startOffset, final int endOffset) {
+		this.tmpCharString.set(this.buffer, this.currentIdx + startOffset, endOffset - startOffset);
 		return this.tmpCharString;
 	}
 	
-	public final String getString(final int offset, final int length) {
-		return new String(this.buffer, this.currentIdx + offset, length);
+	/**
+	 * Return the text as string.
+	 * 
+	 * @param startOffset the start offset in the content (inclusive)
+	 * @param endOffset the end offset in the content (exclusive)
+	 * @return the text
+	 */
+	public final String getString(final int startOffset, final int endOffset) {
+		return new String(this.buffer, this.currentIdx + startOffset, endOffset - startOffset);
 	}
 	
-	public final @Nullable String getString(final int offset, final int length, final StringFactory factory) {
-		this.tmpCharString.set(this.buffer, this.currentIdx + offset, length);
+	/**
+	 * Return the text as string created by using the specified factory.
+	 * 
+	 * @param startOffset the start offset in the content (inclusive)
+	 * @param endOffset the end offset in the content (exclusive)
+	 * @return the text
+	 */
+	public final @Nullable String getString(final int startOffset, final int endOffset,
+			final StringFactory factory) {
+		this.tmpCharString.set(this.buffer, this.currentIdx + startOffset, endOffset - startOffset);
 		return factory.get(this.tmpCharString);
 	}
 	
-	public final void appendTo(final int offset, final int length, final StringBuilder dst) {
+	/**
+	 * Appends the text to the specified StringBuilder.
+	 * 
+	 * @param startOffset the start offset in the content (inclusive)
+	 * @param endOffset the end offset in the content (exclusive)
+	 */
+	public final void appendTo(final int startOffset, final int endOffset,
+			final StringBuilder dst) {
+		final int length= endOffset - startOffset;
 		switch (length) {
 		case 0:
 			return;
 		case 1:
-			dst.append(this.buffer[this.currentIdx + offset]);
+			dst.append(this.buffer[this.currentIdx + startOffset]);
 			return;
 		default:
-			dst.append(this.buffer, this.currentIdx + offset, length);
+			dst.append(this.buffer, this.currentIdx + startOffset, length);
 			return;
 		}
 	}
 	
-	public final void insertInto(final int offset, final int length, final StringBuilder dst, final int dstOffset) {
-		switch (length) {
+	/**
+	 * Inserts the text in the specified StringBuilder.
+	 * 
+	 * @param startOffset the start offset in the content (inclusive)
+	 * @param endOffset the end offset in the content (exclusive)
+	 */
+	public final void insertInto(final int startOffset, final int endOffset,
+			final StringBuilder dst, final int dstOffset) {
+		final int length= endOffset - startOffset;
+		switch (endOffset - startOffset) {
 		case 0:
 			return;
 		case 1:
-			dst.insert(dstOffset, this.buffer[this.currentIdx + offset]);
+			dst.insert(dstOffset, this.buffer[this.currentIdx + startOffset]);
 			return;
 		default:
-			dst.insert(dstOffset, this.buffer, this.currentIdx + offset, length);
+			dst.insert(dstOffset, this.buffer, this.currentIdx + startOffset, length);
 			return;
 		}
 	}
diff --git a/jcommons/org.eclipse.statet.jcommons.text.core/src/org/eclipse/statet/jcommons/text/core/util/HtmlStripParserInput.java b/jcommons/org.eclipse.statet.jcommons.text.core/src/org/eclipse/statet/jcommons/text/core/util/HtmlStripParserInput.java
index 9f95794..6e1dd45 100644
--- a/jcommons/org.eclipse.statet.jcommons.text.core/src/org/eclipse/statet/jcommons/text/core/util/HtmlStripParserInput.java
+++ b/jcommons/org.eclipse.statet.jcommons.text.core/src/org/eclipse/statet/jcommons/text/core/util/HtmlStripParserInput.java
@@ -320,7 +320,7 @@
 					case '9':
 						continue ITER_CN;
 					case ';':
-						return resolveEntity(in, Integer.parseInt(in.getString(2, n - 3), 10), n);
+						return resolveEntity(in, Integer.parseInt(in.getString(2, n - 1), 10), n);
 					default:
 						break ITER_CN;
 					}
@@ -358,7 +358,7 @@
 						continue ITER_CN;
 					case ';':
 						if (n > 4) {
-							return resolveEntity(in, Integer.parseInt(in.getString(3, n - 4), 16), n);
+							return resolveEntity(in, Integer.parseInt(in.getString(3, n - 1), 16), n);
 						}
 						break ITER_CN;
 					default:
@@ -481,7 +481,7 @@
 				case 'z':
 					continue ITER_CN;
 				case ';':
-					return resolveEntity(in, in.getString(1, n - 2), n);
+					return resolveEntity(in, in.getString(1, n - 1), n);
 				default:
 					break ITER_CN;
 				}