blob: 9fa1d937e3fde61a90957a73fc1e304003bdc719 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2018 IBM Corporation, Zeligsoft Inc., and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* IBM - Initial API and implementation
* E.D.Willink - refactored to separate from OCLAnalyzer and OCLParser
* - Bug 259818
*******************************************************************************/
package org.eclipse.ocl.lpg;
import java.io.IOException;
import java.io.Reader;
import java.util.List;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.ocl.cst.CSTNode;
import org.eclipse.ocl.internal.OCLPlugin;
import org.eclipse.ocl.utilities.ASTNode;
import org.eclipse.ocl.utilities.CallingASTNode;
import org.eclipse.ocl.utilities.TypedASTNode;
/**
* The <code>AbstractAnalyzer</code> supports semantic analysis of a CST
* produced by an <code>AbstractParser</code> to create a corresponding AST. It
* is necessary that syntactic parsing and semantic analysis are performed in
* two steps because LPG is a bottom up parser and cannot provide enough
* contextual information to create the AST on the first pass.
*
* Derived classes should extend the abstract support for AST from CST from
* tokens from text parsing and analysis to support the AST and CST classes
* appropriate to a particular language.
*/
public abstract class AbstractAnalyzer {
private final AbstractParser parser; // The parser that provides CST and
// source text
private final BasicEnvironment environment; // The symbol lookup, problem
// handling context
private FormattingHelper formatter = null; // The message formatting
// assistant
private boolean traceflag = false; // Optional AST tracing for debug
private int charOffset = 0; // Optional offset applied to reported text to
// hide
// extra surrounding context
protected AbstractAnalyzer(AbstractParser parser) {
this.parser = parser;
this.environment = parser.getEnvironment();
}
/**
* Obtains the text from which the specified CST <code>node</code> was
* parsed.
*
* @param node
* a concrete syntax node
*
* @return its text
*/
public String computeInputString(CSTNode node) {
return getAbstractParser().computeInputString(node.getStartOffset(),
node.getEndOffset());
}
public void dumpTokens() {
parser.getIPrsStream().dumpTokens();
}
public String formatClass(Object object) {
return getFormatter().formatClass(object);
}
public String formatEClassName(EObject object) {
return getFormatter().formatEClassName(object);
}
public String formatName(Object object) {
return getFormatter().formatName(object);
}
public String formatQualifiedName(Object object) {
return getFormatter().formatQualifiedName(object);
}
public String formatPath(List<String> pathName) {
return getFormatter().formatPath(pathName);
}
public String formatPath(List<String> pathName, String name) {
return getFormatter().formatPath(pathName, name);
}
public String formatString(String string) {
return getFormatter().formatString(string);
}
public String formatType(Object object) {
return getFormatter().formatType(object);
}
/**
* Obtains the parser that I use to transform the OCL text into the Concrete
* Syntax Model.
*
* @since 1.3
*/
public AbstractParser getAbstractParser() {
return parser;
}
public int getCharOffset() {
return charOffset;
}
public BasicEnvironment getEnvironment() {
return environment;
}
/**
* Get the message formatting assistant, returning the value set by
* setFormatter, if non-null, else that provided by the environment, if
* non-null, else AbstractFormattingHelper.INSTANCE.
*
* @return A non-null message formatting helper
*/
public FormattingHelper getFormatter() {
if (formatter == null) {
BasicEnvironment environment = getEnvironment();
if (environment != null) {
formatter = environment.getFormatter();
}
if (formatter == null) {
formatter = AbstractFormattingHelper.INSTANCE;
}
}
return formatter;
}
public AbstractLexer getLexer() {
return getAbstractParser().getLexer();
}
/**
* Obtains my parser.
*
* @return my parser
*
* @deprecated Since 1.3, use the {@link #getAbstractParser()} method,
* instead.
*/
@Deprecated
public AbstractParser getParser() {
return parser;
}
public boolean getTraceFlag() {
return traceflag;
}
/**
* Initializes the start and end positions of the property name in the
* specified calling AST node.
*
* @param callingASTNode
* a calling AST node
* @param cstNode
* a CST node
*/
protected void initPropertyPositions(CallingASTNode callingASTNode,
CSTNode cstNode) {
callingASTNode.setPropertyStartPosition(cstNode.getStartOffset()
+ charOffset);
callingASTNode.setPropertyEndPosition(cstNode.getEndOffset()
+ charOffset + 1);
// +1 because end offset is exclusive
}
/**
* Initializes the start and end positions of the specified AST node from
* the given CST node
*
* @param astNode
* an AST node
* @param cstNode
* a CST node
*/
protected void initStartEndPositions(ASTNode astNode, CSTNode cstNode) {
if ((astNode != null) && (cstNode != null)) {
// initASTMapping(astNode, cstNode);
astNode.setStartPosition(cstNode.getStartOffset() + charOffset);
astNode.setEndPosition(cstNode.getEndOffset() + charOffset + 1);
// +1 because end offset is exclusive
}
}
/**
* Initializes the type start and end positions of the specified typed AST
* node from the given CST node
*
* @param typedASTNode
* a typed AST node
* @param cstNode
* a CST node
*/
protected void initTypePositions(TypedASTNode typedASTNode, CSTNode cstNode) {
if ((typedASTNode != null) && (cstNode != null)) {
// if (typedASTNode instanceof TypedElement)
// initASTMapping(((TypedElement)typedASTNode).getType(), cstNode);
// else if (typedASTNode instanceof CollectionType)
// initASTMapping(((CollectionType)typedASTNode).getElementType(),
// cstNode);
typedASTNode.setTypeStartPosition(cstNode.getStartOffset()
+ charOffset);
typedASTNode.setTypeEndPosition(cstNode.getEndOffset() + charOffset
+ 1);
// +1 because end offset is exclusive
}
}
/**
* Creates a string by joining the given string list elements with ::.
*
* @param namelist
* list of names to make string out of
* @return the qualified name comprising the list of name elements
*/
protected String makeName(EList<String> namelist) {
StringBuilder msg = new StringBuilder();
for (int i = 0; i < namelist.size(); i++) {
if (i > 0) {
msg.append("::");//$NON-NLS-1$
}
msg.append(namelist.get(i));
}
return msg.toString();
}
/**
* Creates a string by joining the given string list elements with spaces.
*
* @param namelist
* list of names to make string out of
* @return string representation of the list of string elements
*/
protected String makeString(EList<String> namelist) {
StringBuilder s = new StringBuilder();
for (int i = 0; i < namelist.size(); i++) {
if (i > 0) {
s.append(" "); //$NON-NLS-1$
}
s.append(namelist.get(i));
}
return s.toString();
}
/**
* Sets the character index offset of the input.
*
* @param offset
*/
public void setCharacterOffset(int offset) {
charOffset = offset;
}
public void setFileName(String filename) {
getLexer().getILexStream().setFileName(filename);
}
/**
* Define the input text as a given array of characters.
*
* @param buffer
* the characters
*
* @deprecated clients should invoke {@link #reset(char[], String)}
*/
@Deprecated
public void initialize(char[] buffer) {
reset(buffer, null);
}
/**
* Define the input text by reading from a reader.
*
* @param reader
* providing the source text
* @throws IOException
* if reading fails
*
* @deprecated clients should invoke {@link #reset(Reader, String)}
*/
@Deprecated
public void initialize(Reader reader)
throws IOException {
reset(reader, null);
}
/**
* Define the input text as a given array of characters.
*
* @param buffer
* the characters
* @param fileName
* the associated finleName of the input, or <code>null</code> if none.
* @since 3.0
*/
public void reset(char[] buffer, String fileName) {
getLexer().reset(buffer, fileName);
}
/**
* Define the input text by reading from a reader.
*
* @param reader
* providing the source text
* @param fileName
* the associated finleName of the input, or <code>null</code> if none.
* @throws IOException
* if reading fails
* @since 3.0
*/
public void reset(Reader reader, String fileName) throws IOException {
AbstractLexer lexer = getLexer();
lexer.reset(reader, fileName);
AbstractParser parser = getAbstractParser();
if (parser.getIPrsStream() == null) {
parser.reset(lexer.getILexStream());
}
}
public void setTab(int tab) {
getLexer().getILexStream().setTab(tab);
}
public void setTraceFlag(boolean flag) {
traceflag = flag;
}
public void ERROR(String problemMessage) {
getEnvironment().utilityError(problemMessage, null, null);
}
public void ERROR(Object problemObject, String rule, String problemMessage) {
getEnvironment().analyzerError(problemMessage, rule, problemObject);
}
public void ERROR(List<?> problemObjects, String rule, String problemMessage) {
getEnvironment().analyzerError(problemMessage, rule, problemObjects);
}
protected void TRACE(String rule, String message) {
if (traceflag) {
OCLPlugin.trace(rule + ": " + message);//$NON-NLS-1$
}
}
protected void TRACE(String rule, String message, EList<String> namelist) {
TRACE(rule, message + makeString(namelist));
}
}