Bug 574900: [Ltk-AST] Add SourceMessageUtil

Change-Id: I6f03847c102e7189120d723749be37ed8e3c9d06
diff --git a/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/core/util/SourceMessageUtil.java b/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/core/util/SourceMessageUtil.java
new file mode 100644
index 0000000..dff77b0
--- /dev/null
+++ b/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/core/util/SourceMessageUtil.java
@@ -0,0 +1,135 @@
+/*=============================================================================#
+ # Copyright (c) 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.ltk.core.util;
+
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullLateInit;
+
+import org.eclipse.jface.text.BadLocationException;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+import org.eclipse.statet.ltk.ast.core.AstNode;
+import org.eclipse.statet.ltk.ast.core.StatusDetail;
+import org.eclipse.statet.ltk.core.source.SourceContent;
+
+
+@NonNullByDefault
+public class SourceMessageUtil {
+	
+	
+	private static final int FULL_TEXT_LIMIT= 100;
+	private static final int START_TEXT_LIMIT= 25;
+	
+	
+	private final StringBuilder tmpBuilder= new StringBuilder();
+	
+	private SourceContent sourceContent= nonNullLateInit();
+	
+	
+	public SourceMessageUtil(final SourceContent sourceContent) {
+		setSourceContent(sourceContent);
+	}
+	
+	public SourceMessageUtil() {
+	}
+	
+	
+	public void setSourceContent(final SourceContent sourceContent) {
+		this.sourceContent= nonNullAssert(sourceContent);
+	}
+	
+	
+	protected final StringBuilder getStringBuilder() {
+		this.tmpBuilder.setLength(0);
+		return this.tmpBuilder;
+	}
+	
+	public String getFullText(final AstNode node) throws BadLocationException {
+		final String text= node.getText();
+		if (text != null) {
+			if (text.length() > FULL_TEXT_LIMIT) {
+				final StringBuilder sb= getStringBuilder();
+				sb.append(text, 0, FULL_TEXT_LIMIT);
+				sb.append('…');
+				return sb.toString();
+			}
+			else {
+				return text;
+			}
+		}
+		else {
+			if (node.getLength() > FULL_TEXT_LIMIT) {
+				final StringBuilder sb= getStringBuilder();
+				this.sourceContent.appendStringTo(sb,
+						node.getStartOffset(),
+						node.getStartOffset() + FULL_TEXT_LIMIT );
+				sb.append('…');
+				return sb.toString();
+			}
+			else {
+				return this.sourceContent.getString(
+						node.getStartOffset(),
+						node.getEndOffset() );
+			}
+		}
+	}
+	
+	public String getStartText(final AstNode node, final int offset)
+			throws BadLocationException {
+		final String text= node.getText();
+		if (text != null) {
+			if (text.length() > START_TEXT_LIMIT) {
+				final StringBuilder sb= getStringBuilder();
+				sb.append(text, 0, START_TEXT_LIMIT);
+				sb.append('…');
+				return sb.toString();
+			}
+			else {
+				return text;
+			}
+		}
+		else {
+			if (node.getLength() - offset > START_TEXT_LIMIT) {
+				final StringBuilder sb= getStringBuilder();
+				this.sourceContent.appendStringTo(sb,
+						node.getStartOffset() + offset,
+						node.getStartOffset() + offset + START_TEXT_LIMIT );
+				sb.append('…');
+				return sb.toString();
+			}
+			else {
+				return this.sourceContent.getString(
+						node.getStartOffset() + offset,
+						node.getEndOffset() );
+			}
+		}
+	}
+	
+	public String getDetailText(final AstNode node, final int offset, final StatusDetail detail)
+			throws BadLocationException {
+		final String text= node.getText();
+		if (text != null) {
+			final int begin= detail.getStartOffset() - node.getStartOffset() - offset;
+			return text.substring(begin, begin + detail.getLength());
+		}
+		else {
+			return this.sourceContent.getString(
+					detail.getStartOffset(),
+					detail.getEndOffset() );
+		}
+	}
+	
+}