Bug 574871: [Yaml-Source] Fix initialization of YAML scanner (reset) for
start index other than 0

Change-Id: I3528473ca03042d7c3bad00abdef3d685f3cb798
diff --git a/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/yaml/core/ast/YamlParser.java b/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/yaml/core/ast/YamlParser.java
index 6438965..75daec5 100644
--- a/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/yaml/core/ast/YamlParser.java
+++ b/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/yaml/core/ast/YamlParser.java
@@ -34,7 +34,6 @@
 import java.util.List;
 
 import org.yaml.snakeyaml.error.Mark;
-import org.yaml.snakeyaml.reader.StreamReader;
 import org.yaml.snakeyaml.tokens.AliasToken;
 import org.yaml.snakeyaml.tokens.ScalarToken;
 import org.yaml.snakeyaml.tokens.TagToken;
@@ -46,6 +45,7 @@
 
 import org.eclipse.statet.internal.yaml.snakeyaml.scanner.ScannerConstants;
 import org.eclipse.statet.internal.yaml.snakeyaml.scanner.ScannerImpl;
+import org.eclipse.statet.internal.yaml.snakeyaml.scanner.StreamReader;
 import org.eclipse.statet.ltk.ast.core.AstNode;
 import org.eclipse.statet.ltk.ast.core.StatusDetail;
 
diff --git a/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/yaml/core/source/YamlPartitionNodeScanner.java b/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/yaml/core/source/YamlPartitionNodeScanner.java
index 55657dd..f2a8bb2 100644
--- a/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/yaml/core/source/YamlPartitionNodeScanner.java
+++ b/yaml/org.eclipse.statet.yaml.core/src/org/eclipse/statet/yaml/core/source/YamlPartitionNodeScanner.java
@@ -20,7 +20,6 @@
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.IDocument;
 
-import org.yaml.snakeyaml.reader.StreamReader;
 import org.yaml.snakeyaml.tokens.Token;
 
 import org.eclipse.statet.ecommons.text.core.treepartitioner.TreePartitionNode;
@@ -29,6 +28,7 @@
 import org.eclipse.statet.ecommons.text.core.treepartitioner.TreePartitionNodeType;
 
 import org.eclipse.statet.internal.yaml.snakeyaml.scanner.ScannerImpl;
+import org.eclipse.statet.internal.yaml.snakeyaml.scanner.StreamReader;
 
 
 public class YamlPartitionNodeScanner implements TreePartitionNodeScanner {
diff --git a/yaml/org.eclipse.statet.yaml.core/srcParser/org/eclipse/statet/internal/yaml/snakeyaml/scanner/ScannerImpl.java b/yaml/org.eclipse.statet.yaml.core/srcParser/org/eclipse/statet/internal/yaml/snakeyaml/scanner/ScannerImpl.java
index fcc3400..58cfcc7 100644
--- a/yaml/org.eclipse.statet.yaml.core/srcParser/org/eclipse/statet/internal/yaml/snakeyaml/scanner/ScannerImpl.java
+++ b/yaml/org.eclipse.statet.yaml.core/srcParser/org/eclipse/statet/internal/yaml/snakeyaml/scanner/ScannerImpl.java
@@ -53,7 +53,6 @@
 import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
 import org.yaml.snakeyaml.error.Mark;
 import org.yaml.snakeyaml.error.YAMLException;
-import org.yaml.snakeyaml.reader.StreamReader;
 import org.yaml.snakeyaml.scanner.Constant;
 import org.yaml.snakeyaml.scanner.ScannerException;
 import org.yaml.snakeyaml.tokens.AliasToken;
@@ -241,7 +240,7 @@
 	
 	
 	public void reset(final String s, final int index) {
-		this.reader= new StreamReader(s);
+		this.reader= new StreamReader(s, index);
 		this.done= false;
 		this.flowLevel= 0;
 		this.tokens.clear();
diff --git a/yaml/org.eclipse.statet.yaml.core/srcParser/org/eclipse/statet/internal/yaml/snakeyaml/scanner/StreamReader.java b/yaml/org.eclipse.statet.yaml.core/srcParser/org/eclipse/statet/internal/yaml/snakeyaml/scanner/StreamReader.java
new file mode 100644
index 0000000..21a9772
--- /dev/null
+++ b/yaml/org.eclipse.statet.yaml.core/srcParser/org/eclipse/statet/internal/yaml/snakeyaml/scanner/StreamReader.java
@@ -0,0 +1,251 @@
+/**
+ * Copyright (c) 2008, 2021 http://www.snakeyaml.org and others.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.eclipse.statet.internal.yaml.snakeyaml.scanner;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Arrays;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.reader.ReaderException;
+import org.yaml.snakeyaml.scanner.Constant;
+
+
+/**
+ * Reader: checks if code points are in allowed range. Returns '\0' when end of
+ * data has been reached.
+ */
+public class StreamReader {
+	
+	private String name;
+	private final Reader stream;
+	/**
+	 * Read data (as a moving window for input stream)
+	 */
+	private int[] dataWindow;
+	
+	/**
+	 * Real length of the data in dataWindow
+	 */
+	private int dataLength;
+	
+	/**
+	 * The variable points to the current position in the data array
+	 */
+	private int pointer = 0;
+	private boolean eof;
+	/**
+	 * index is only required to implement 1024 key length restriction
+	 * http://yaml.org/spec/1.1/#simple key/
+	 * It must count code points, but it counts characters (to be fixed)
+	 */
+	private int index = 0; // in code points
+	private int line = 0;
+	private int column = 0; //in code points
+	private char[] buffer; // temp buffer for one read operation (to avoid
+						   // creating the array in stack)
+	
+	private static final int BUFFER_SIZE = 1025;
+	
+	
+	public StreamReader(String stream, final int index) {
+		this(new StringReader(stream), index);
+		this.name = "'string'";
+	}
+	
+	public StreamReader(String stream) {
+		this(new StringReader(stream), 0);
+		this.name = "'string'";
+	}
+	
+	public StreamReader(Reader reader, final int index) {
+		this.name = "'reader'";
+		this.dataWindow = new int[0];
+		this.dataLength = 0;
+		this.stream = reader;
+		this.eof = false;
+		this.index= index;
+		this.buffer = new char[BUFFER_SIZE];
+	}
+	
+	
+	public static boolean isPrintable(final String data) {
+		final int length = data.length();
+		for (int offset = 0; offset < length; ) {
+			final int codePoint = data.codePointAt(offset);
+			
+			if (!isPrintable(codePoint)) {
+				return false;
+			}
+			
+			offset += Character.charCount(codePoint);
+		}
+		
+		return true;
+	}
+	
+	public static boolean isPrintable(final int c) {
+		return (c >= 0x20 && c <= 0x7E) || c == 0x9 || c == 0xA || c == 0xD || c == 0x85
+				|| (c >= 0xA0 && c <= 0xD7FF) || (c >= 0xE000 && c <= 0xFFFD)
+				|| (c >= 0x10000 && c <= 0x10FFFF);
+	}
+	
+	
+	public Mark getMark() {
+		return new Mark(this.name, this.index, this.line, this.column, this.dataWindow, this.pointer);
+	}
+	
+	public void forward() {
+		forward(1);
+	}
+	
+	/**
+	 * read the next length characters and move the pointer.
+	 * if the last character is high surrogate one more character will be read
+	 *
+	 * @param length amount of characters to move forward
+	 */
+	public void forward(int length) {
+		for (int i = 0; i < length && ensureEnoughData(); i++) {
+		int c = this.dataWindow[this.pointer++];
+		this.index++;
+		if (Constant.LINEBR.has(c)
+				|| (c == '\r' && (ensureEnoughData() && this.dataWindow[this.pointer] != '\n'))) {
+				this.line++;
+				this.column = 0;
+			} else if (c != 0xFEFF) {
+				this.column++;
+			}
+		}
+	}
+	
+	public int peek() {
+		return (ensureEnoughData()) ? this.dataWindow[this.pointer] : '\0';
+	}
+	
+	/**
+	 * Peek the next index-th code point
+	 *
+	 * @param index to peek
+	 * @return the next index-th code point
+	 */
+	public int peek(int index) {
+		return (ensureEnoughData(index)) ? this.dataWindow[this.pointer + index] : '\0';
+	}
+	
+	/**
+	 * peek the next length code points
+	 *
+	 * @param length amount of the characters to peek
+	 * @return the next length code points
+	 */
+	public String prefix(int length) {
+		if (length == 0) {
+			return "";
+		} else if (ensureEnoughData(length)) {
+			return new String(this.dataWindow, this.pointer, length);
+		} else {
+			return new String(this.dataWindow, this.pointer,
+					Math.min(length, this.dataLength - this.pointer));
+		}
+	}
+	
+	/**
+	 * prefix(length) immediately followed by forward(length)
+	 * @param length amount of characters to get
+	 * @return the next length code points
+	 */
+	public String prefixForward(int length) {
+		final String prefix = prefix(length);
+		this.pointer += length;
+		this.index += length;
+		// prefix never contains new line characters
+		this.column += length;
+		return prefix;
+	}
+	
+	private boolean ensureEnoughData() {
+		return ensureEnoughData(0);
+	}
+	
+	private boolean ensureEnoughData(int size) {
+		if (!this.eof && this.pointer + size >= this.dataLength) {
+			update();
+		}
+		return (this.pointer + size) < this.dataLength;
+	}
+	
+	private void update() {
+		try {
+			int read = this.stream.read(this.buffer, 0, BUFFER_SIZE - 1);
+			if (read > 0) {
+				int cpIndex = (this.dataLength - this.pointer);
+				this.dataWindow = Arrays.copyOfRange(this.dataWindow, this.pointer, this.dataLength + read);
+				
+				if (Character.isHighSurrogate(this.buffer[read - 1])) {
+					if (this.stream.read(this.buffer, read, 1) == -1) {
+						this.eof = true;
+					} else {
+						read++;
+					}
+				}
+				
+				int nonPrintable = ' ';
+				for (int i = 0; i < read; cpIndex++) {
+					int codePoint = Character.codePointAt(this.buffer, i);
+					this.dataWindow[cpIndex] = codePoint;
+					if (isPrintable(codePoint)) {
+						i += Character.charCount(codePoint);
+					} else {
+						nonPrintable = codePoint;
+						i = read;
+					}
+				}
+				
+				this.dataLength = cpIndex;
+				this.pointer = 0;
+				if (nonPrintable != ' ') {
+					throw new ReaderException(this.name, cpIndex - 1, nonPrintable,
+							"special characters are not allowed");
+				}
+			} else {
+				this.eof = true;
+			}
+		} catch (IOException ioe) {
+			throw new YAMLException(ioe);
+		}
+	}
+	
+	
+	public int getColumn() {
+		return this.column;
+	}
+	
+	/**
+	 * @return current position as number (in characters) from the beginning of the stream
+	 */
+	public int getIndex() {
+		return this.index;
+	}
+	
+	public int getLine() {
+		return this.line;
+	}
+	
+}