Bug 577760: Add AbstractAstProblemReporter
Change-Id: I107c70e76b023c1c37f00caacddba517cb7c4e78
diff --git a/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/ast/core/util/AbstractAstProblemReporter.java b/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/ast/core/util/AbstractAstProblemReporter.java
new file mode 100644
index 0000000..a68844d
--- /dev/null
+++ b/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/ast/core/util/AbstractAstProblemReporter.java
@@ -0,0 +1,197 @@
+/*=============================================================================#
+ # 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.ast.core.util;
+
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullLateInit;
+
+import static org.eclipse.statet.ltk.core.StatusCodes.CTX12;
+import static org.eclipse.statet.ltk.core.StatusCodes.SUBSEQUENT;
+import static org.eclipse.statet.ltk.core.StatusCodes.TYPE1;
+import static org.eclipse.statet.ltk.core.StatusCodes.TYPE123;
+import static org.eclipse.statet.ltk.core.StatusCodes.TYPE1_RUNTIME_ERROR;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.text.BadLocationException;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.text.core.TextLineInformation;
+
+import org.eclipse.statet.ecommons.runtime.core.util.MessageBuilder;
+
+import org.eclipse.statet.internal.ltk.core.LtkCorePlugin;
+import org.eclipse.statet.ltk.ast.core.AstNode;
+import org.eclipse.statet.ltk.core.LtkCore;
+import org.eclipse.statet.ltk.core.StatusCodes;
+import org.eclipse.statet.ltk.core.source.SourceContent;
+import org.eclipse.statet.ltk.core.util.SourceMessageUtil;
+import org.eclipse.statet.ltk.issues.core.IssueRequestor;
+import org.eclipse.statet.ltk.issues.core.Problem;
+import org.eclipse.statet.ltk.issues.core.impl.BasicProblem;
+
+
+@NonNullByDefault
+public class AbstractAstProblemReporter {
+
+
+ protected static final int MASK= TYPE123 | CTX12 | SUBSEQUENT;
+
+
+ private final String modelTypeId;
+
+ private final boolean reportSubsequent= false;
+
+ private SourceContent sourceContent= nonNullLateInit();
+ private IssueRequestor requestor= nonNullLateInit();
+
+ private final SourceMessageUtil messageUtil= new SourceMessageUtil();
+ private final MessageBuilder messageBuilder= new MessageBuilder();
+ private final List<Problem> problemBuffer= new ArrayList<>(100);
+
+
+ public AbstractAstProblemReporter(final String modelTypeId) {
+ this.modelTypeId= modelTypeId;
+ }
+
+
+ public String getModelTypeId() {
+ return this.modelTypeId;
+ }
+
+
+ protected void init(final SourceContent content, final IssueRequestor requestor) {
+ this.sourceContent= nonNullAssert(content);
+ this.messageUtil.setSourceContent(content);
+ this.requestor= nonNullAssert(requestor);
+ }
+
+ protected void flush() {
+ if (!this.problemBuffer.isEmpty()) {
+ this.requestor.acceptProblems(this.modelTypeId, this.problemBuffer);
+ }
+ }
+
+ @SuppressWarnings("null")
+ protected void clear() {
+ this.problemBuffer.clear();
+ this.sourceContent= null;
+ this.requestor= null;
+ }
+
+
+ protected final boolean requiredCheck(final int code) {
+ return code != StatusCodes.TYPE1_OK &&
+ (this.reportSubsequent || (code & SUBSEQUENT) == 0);
+ }
+
+ protected final SourceMessageUtil getMessageUtil() {
+ return this.messageUtil;
+ }
+
+ protected final MessageBuilder getMessageBuilder() {
+ return this.messageBuilder;
+ }
+
+ protected final void addProblem(final int severity, final int code, final String message,
+ int startOffset, int endOffset) {
+ if (startOffset < this.sourceContent.getStartOffset()) {
+ startOffset= this.sourceContent.getStartOffset();
+ }
+ if (endOffset < startOffset) {
+ endOffset= startOffset;
+ }
+ else if (endOffset > this.sourceContent.getEndOffset()) {
+ endOffset= this.sourceContent.getEndOffset();
+ }
+
+ this.problemBuffer.add(new BasicProblem(this.modelTypeId, severity, code, message,
+ startOffset, endOffset ));
+
+ if (this.problemBuffer.size() >= 100) {
+ this.requestor.acceptProblems(this.modelTypeId, this.problemBuffer);
+ this.problemBuffer.clear();
+ }
+ }
+
+
+ protected void handleCommonCodes(final AstNode node, final int code)
+ throws BadLocationException, InvocationTargetException {
+ switch (code & TYPE1) {
+
+ case TYPE1_RUNTIME_ERROR:
+ addProblem(Problem.SEVERITY_ERROR, code,
+ "Error when parsing source code. Please submit a bug report with a code snippet / log entry.", //$NON-NLS-1$
+ node.getStartOffset(), node.getStartOffset() );
+ return;
+
+ default:
+ handleUnknownCodes(node);
+ return;
+ }
+ }
+
+ protected void handleUnknownCodes(final AstNode node) {
+ final int code= (node.getStatusCode() & MASK);
+ final StringBuilder sb= new StringBuilder();
+ sb.append("Unhandled/Unknown code of AST node (").append(this.modelTypeId).append("):"); //$NON-NLS-1$ //$NON-NLS-2$
+ sb.append('\n');
+ sb.append(" code= 0x").append(Integer.toHexString(code)); //$NON-NLS-1$
+ sb.append('\n');
+ sb.append(" node= ").append(node);
+ sb.append(" (").append(node.getStartOffset()).append(", ").append(node.getLength()).append(')'); //$NON-NLS-1$ //$NON-NLS-2$
+ sb.append('\n');
+ if (this.sourceContent != null) {
+ final TextLineInformation lines= this.sourceContent.getStringLines();
+ final int line= lines.getLineOfOffset(node.getStartOffset() - this.sourceContent.getStartOffset());
+ sb.append(" Line ").append((line + 1)); //$NON-NLS-1$
+ sb.append('\n');
+
+ final int firstLine= Math.max(0, line - 2);
+ final int lastLine= Math.min(lines.getNumberOfLines() - 1,
+ lines.getLineOfOffset(node.getEndOffset() - this.sourceContent.getStartOffset()) + 2 );
+ sb.append(" source (line ").append(firstLine + 1).append('-').append(lastLine + 1).append(")= \n"); //$NON-NLS-1$ //$NON-NLS-2$
+ sb.append(this.sourceContent.getString(),
+ lines.getStartOffset(firstLine), lines.getEndOffset(lastLine) );
+ }
+ LtkCorePlugin.log(new Status(IStatus.WARNING, LtkCore.BUNDLE_ID, sb.toString()));
+ }
+
+
+ protected int expandSpaceStart(final int offset) {
+ switch ((this.sourceContent.contains(offset - 1)) ? this.sourceContent.getChar(offset - 1) : -1) {
+ case ' ':
+ case '\t':
+ return offset - 1;
+ default:
+ return offset;
+ }
+ }
+
+ protected int expandSpaceEnd(final int offset) {
+ switch ((this.sourceContent.contains(offset)) ? this.sourceContent.getChar(offset) : -1) {
+ case ' ':
+ case '\t':
+ return offset + 1;
+ default:
+ return offset;
+ }
+ }
+
+}