adding an annotated string
diff --git a/bundles/runtime/org.eclipse.fx.core/src/org/eclipse/fx/core/text/AnnotatedString.java b/bundles/runtime/org.eclipse.fx.core/src/org/eclipse/fx/core/text/AnnotatedString.java
new file mode 100644
index 0000000..90c1be8
--- /dev/null
+++ b/bundles/runtime/org.eclipse.fx.core/src/org/eclipse/fx/core/text/AnnotatedString.java
@@ -0,0 +1,282 @@
+/*******************************************************************************

+ * Copyright (c) 2017 BestSolution.at 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

+ *

+ * Contributors:

+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation

+ *******************************************************************************/

+package org.eclipse.fx.core.text;

+

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.List;

+import java.util.stream.IntStream;

+import java.util.stream.Stream;

+

+import org.eclipse.fx.core.Range;

+import org.eclipse.fx.core.array.ArrayUtils;

+

+/**

+ * {@link CharSequence} who is built from annotated segments

+ *

+ * @since 3.0

+ */

+public final class AnnotatedString implements CharSequence {

+	final char[] content;

+	final int[][] ranges;

+	final String[][] annotations;

+

+	/**

+	 * Consumes a segment

+	 */

+	public interface SegmentConsumer {

+		/**

+		 * Consumes a segment

+		 *

+		 * @param start

+		 *            the start index (inclusive)

+		 * @param end

+		 *            the end index (exclusive)

+		 * @param annotation

+		 *            the annotations

+		 */

+		public void consume(int start, int end, String[] annotation);

+	}

+

+	/**

+	 * A segment of the {@link AnnotatedString}

+	 */

+	public class Segment {

+		private final int idx;

+

+		Segment(int idx) {

+			this.idx = idx;

+		}

+

+		/**

+		 * @return the text in the segment

+		 */

+		public char[] text() {

+			return Arrays.copyOfRange(AnnotatedString.this.content, start(), end());

+		}

+

+		/**

+		 * @return stream of characters

+		 */

+		public IntStream chars() {

+			return ArrayUtils.stream(AnnotatedString.this.content, start(), end());

+		}

+

+		/**

+		 * @return the start index of the segment (inclusive)

+		 */

+		public int start() {

+			return AnnotatedString.this.ranges[this.idx][0];

+		}

+

+		/**

+		 * @return the end index of the segment (exclusive)

+		 */

+		public int end() {

+			return AnnotatedString.this.ranges[this.idx][1];

+		}

+

+		/**

+		 * @return annotations

+		 */

+		public String[] annotations() {

+			return Arrays.copyOf(AnnotatedString.this.annotations[this.idx],

+					AnnotatedString.this.annotations[this.idx].length);

+		}

+	}

+

+	AnnotatedString(char[] content, int[][] ranges, String[][] annotations) {

+		this.content = content;

+		this.ranges = ranges;

+		this.annotations = annotations;

+	}

+

+	/**

+	 * Apply the consumer on each segment

+	 *

+	 * @param consumer

+	 *            the consumer

+	 */

+	public void forEachSegment(SegmentConsumer consumer) {

+		for (int r = 0; r < this.ranges.length; r++) {

+			consumer.consume(this.ranges[r][0], this.ranges[r][1], this.annotations[r]);

+		}

+	}

+

+	/**

+	 * @return stream of segments

+	 */

+	public Stream<Segment> stream() {

+		// Iterator<Segment> it = new Iterator<Segment>() {

+		// private int cur = 0;

+		//

+		// @Override

+		// public Segment next() {

+		// if( hasNext() ) {

+		// return new Segment(cur++);

+		// } else {

+		// throw new NoSuchElementException();

+		// }

+		//

+		// }

+		//

+		// @Override

+		// public boolean hasNext() {

+		// return this.cur < AnnotatedString.this.ranges.length;

+		// }

+		// };

+		// return StreamSupport.stream(() ->

+		// Spliterators.spliterator(it, this.ranges.length,Spliterator.ORDERED),

+		// Spliterator.SUBSIZED | Spliterator.SIZED | Spliterator.ORDERED,

+		// false);

+		return IntStream.range(0, this.ranges.length).mapToObj(Segment::new);

+

+	}

+

+	/**

+	 * Get the content for the provided index

+	 *

+	 * @param segmentIndex

+	 *            the index

+	 * @return the content

+	 */

+	public char[] getContent(int segmentIndex) {

+		return Arrays.copyOfRange(this.content, this.ranges[segmentIndex][0], this.ranges[segmentIndex][1]);

+	}

+

+	/**

+	 * Copy the content to the provided character array

+	 *

+	 * @param segmentIndex

+	 *            the segment index

+	 * @param target

+	 *            the target array

+	 * @return the number of chars copied to the target

+	 */

+	public int copyContent(int segmentIndex, char[] target) {

+		int l = this.ranges[segmentIndex][1] - this.ranges[segmentIndex][0];

+		System.arraycopy(this.content, 0, target, 0, l);

+		return l;

+	}

+

+	@Override

+	public int length() {

+		return this.content.length;

+	}

+

+	@Override

+	public char charAt(int index) {

+		return this.content[index];

+	}

+

+	@Override

+	public CharSequence subSequence(int start, int end) {

+		int idx = -1;

+		for (int i = 0; i < this.ranges.length; i++) {

+			if (Range.intersects(start, end, this.ranges[i][0], this.ranges[i][1])) {

+				idx = i;

+				break;

+			}

+		}

+

+		int[][] targetRange = new int[0][0];

+		String[][] targetAnnotation = new String[0][0];

+		if (idx != -1) {

+			targetRange = new int[idx + 1][2];

+			targetAnnotation = new String[idx + 1][0];

+		}

+

+		return new AnnotatedString(Arrays.copyOfRange(this.content, start, end), targetRange, targetAnnotation);

+	}

+

+	@Override

+	public String toString() {

+		return String.valueOf(this.content);

+	}

+

+	/**

+	 * Create a builder to create an {@link AnnotatedString}

+	 *

+	 * @param capacity

+	 *            the initial capacity

+	 * @return the builder

+	 */

+	public static Builder create(int capacity) {

+		return new Builder(capacity);

+	}

+

+	private static class Struct {

+		int[] range;

+		String[] annotations;

+

+		public Struct(int start, int end, String[] annotations) {

+			this.range = new int[] { start, end };

+			this.annotations = annotations;

+		}

+	}

+

+	/**

+	 * Builder used to create an {@link AnnotatedString}

+	 */

+	public final static class Builder {

+		private StringBuilder builder;

+		private final List<Struct> ranges = new ArrayList<>();

+

+		Builder(int capacity) {

+			this.builder = new StringBuilder(capacity);

+		}

+

+		/**

+		 * Add the provided the character array

+		 *

+		 * @param c

+		 *            the character array

+		 * @param annotations

+		 *            the annotations

+		 * @return the builder

+		 */

+		public Builder add(char[] c, String... annotations) {

+			this.ranges.add(new Struct(this.builder.length(), this.builder.length() + c.length, annotations));

+			this.builder.append(c);

+			return this;

+		}

+

+		/**

+		 * Add the provided {@link CharSequence}

+		 *

+		 * @param s

+		 *            the sequence

+		 * @param annotations

+		 *            the annotations

+		 * @return the builder

+		 */

+		public Builder add(CharSequence s, String... annotations) {

+			this.ranges.add(new Struct(this.builder.length(), this.builder.length() + s.length(), annotations));

+			this.builder.append(s);

+			return this;

+		}

+

+		/**

+		 * @return an {@link AnnotatedString} instance

+		 */

+		public AnnotatedString build() {

+			char[] content = new char[this.builder.length()];

+			this.builder.getChars(0, this.builder.length(), content, 0);

+

+			int[][] ranges = new int[this.ranges.size()][2];

+			String[][] annotations = new String[this.ranges.size()][0];

+			ArrayUtils.fill(ranges, i -> this.ranges.get(i).range);

+			ArrayUtils.fill(annotations, i -> this.ranges.get(i).annotations);

+

+			return new AnnotatedString(content, ranges, annotations);

+		}

+	}

+}