/*******************************************************************************
* Copyright (c) 2005, 2010 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 v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
* E.D.Willink - refactored to separate from OCLAnalyzer and OCLParser
* - Bug 259818
* Zeligsoft - Bug 243976, 251349
*******************************************************************************/
package org.eclipse.ocl.parser;
import java.util.List;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.ocl.Environment;
import org.eclipse.ocl.cst.CSTNode;
import org.eclipse.ocl.cst.InitOrDerValueCS;
import org.eclipse.ocl.cst.InvCS;
import org.eclipse.ocl.cst.InvOrDefCS;
import org.eclipse.ocl.cst.OCLDocumentCS;
import org.eclipse.ocl.cst.OCLExpressionCS;
import org.eclipse.ocl.cst.PackageDeclarationCS;
import org.eclipse.ocl.cst.PrePostOrBodyDeclCS;
import org.eclipse.ocl.cst.PrePostOrBodyEnum;
import org.eclipse.ocl.cst.VariableCS;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.expressions.Variable;
import org.eclipse.ocl.helper.ConstraintKind;
import org.eclipse.ocl.internal.l10n.OCLMessages;
import org.eclipse.ocl.utilities.ExpressionInOCL;
import org.eclipse.ocl.utilities.OCLFactory;
import org.eclipse.ocl.utilities.TypedElement;
/**
* The <code>OCLAnalyzer</code> performs semantic analysis on a CST produced by
* an <code>OCLParser</code> to create the OCL 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.
*/
public class OCLAnalyzer<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>
extends
AbstractOCLAnalyzer<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> {
private OCLFactoryWithHistory history;
/**
* Construct an OCL semantic analyzer that will use a given parser to
* perform syntactic and lexical analysis.
*
* @param parser
* the syntactic (and lexical) parser
*
* @since 1.3
*/
public OCLAnalyzer(AbstractOCLParser parser) {
super(parser);
}
/**
* Construct an OCL semantic analyzer that will use a given parser to
* perform syntactic and lexical analysis.
*
* @param parser
* the syntactic (and lexical) parser
*/
public OCLAnalyzer(OCLParser parser) {
super(parser);
}
/**
* Construct an OCL semantic analyzer with default syntactic and lexical
* parsers all operating within a given environment.
*
* @param environment
* the symbolic and problem handling environment
*/
public OCLAnalyzer(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> environment) {
this(new OCLParser(new OCLLexer(environment)));
}
/**
* Construct an OCL semantic analyzer with default syntactic and lexical
* parsers all operating within a given environment. Then prime the analyzer
* by performing keyword and lexical parsing of the given source text.
*
* @param environment
* the symbolic and problem handling environment
* @param text
* the source text for analysis
*/
public OCLAnalyzer(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> environment,
String text) {
this(new OCLParser(new OCLLexer(environment, text.toCharArray())));
getLexer().lexer(getAbstractParser().getIPrsStream());
}
/**
* @deprecated Use the {@link #getAbstractParser()} method, instead
*/
@Deprecated
@Override
public OCLParser getParser() {
return (OCLParser) super.getParser();
}
@Override
protected OCLFactory createOCLFactory(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
history = env.getFactory().createOCLFactoryWithHistory(env);
return history;
}
private <T> T sanitize(T parseResult) {
history.clear();
return parseResult;
}
/**
* Performs a concrete-syntax parser and throws <code>ParserException</code>
* if any parse errors are encountered.
*
* @return the parsed CST, or <code>null</code> if it could not be parsed
*/
public CSTNode parseConcreteSyntax() {
return getAbstractParser().parser();
}
/**
* Parses the specified concrete syntax model to an abstract syntax model.
* The result is contained by an <tt>Constraint</tt>, so care must be taken
* when the result is no longer needed to dispose of the entire tree rooted
* in the <tt>Constraint</tt>.
*
* @param cst
* the OCL concrete syntax model
* @param constraintType
* the kind of constraint to parse
*
* @return the OCL expression, if it successfully parsed
*/
public OCLExpression<C> parseAST(OCLExpressionCS cst,
ConstraintKind constraintType) {
AbstractOCLParser parser = getAbstractParser();
OCLExpression<C> result = null;
switch (constraintType) {
case PRECONDITION :
PrePostOrBodyDeclCS pre = parser.createPrePostOrBodyDeclCS(
PrePostOrBodyEnum.PRE_LITERAL, null, cst);
result = uml.getSpecification(
prePostOrBodyDeclCS(getOCLEnvironment(), pre))
.getBodyExpression();
break;
case BODYCONDITION :
PrePostOrBodyDeclCS body = parser.createPrePostOrBodyDeclCS(
PrePostOrBodyEnum.BODY_LITERAL, null, cst);
result = uml.getSpecification(
prePostOrBodyDeclCS(getOCLEnvironment(), body))
.getBodyExpression();
break;
case POSTCONDITION :
PrePostOrBodyDeclCS post = parser.createPrePostOrBodyDeclCS(
PrePostOrBodyEnum.POST_LITERAL, null, cst);
result = uml.getSpecification(
prePostOrBodyDeclCS(getOCLEnvironment(), post))
.getBodyExpression();
break;
default :
InvCS inv = parser.createInvCS(null, cst);
result = uml.getSpecification(invCS(inv, getOCLEnvironment()))
.getBodyExpression();
break;
}
return sanitize(result);
}
/**
* Parses the input as an OCLDocumentCS.
*
* @param constraints
* the constraints list to populate
* @return the parsed constraints (as many as could be parsed)
*
* @since 1.3
*/
public List<CT> parseOCLDocument(final List<CT> constraints) {
CSTNode cstNode = parseConcreteSyntax();
if ((cstNode != null) && !(cstNode instanceof PackageDeclarationCS)) {
ERROR(cstNode, "parseOCLDocument",//$NON-NLS-1$
OCLMessages.bind(OCLMessages.ParseCSTNodeType_ERROR_,
"PackageDeclarationCS",//$NON-NLS-1$
cstNode.eClass().getName()));
return sanitize(constraints);
}
OCLDocumentCS documentCS = getAbstractParser().createOCLDocumentCS(
(PackageDeclarationCS) cstNode);
documentCS(documentCS, constraints);
return sanitize(constraints);
}
/**
* Parses the input as a PackageDeclarationCS.
*
* @param constraints
* @return the parsed constraints (as many as could be parsed)
*/
public List<CT> parsePackageDeclarationCS(List<CT> constraints) {
CSTNode cstNode = parseConcreteSyntax();
if ((cstNode != null) && !(cstNode instanceof PackageDeclarationCS)) {
ERROR(cstNode, "parsePackageDeclarationCS",//$NON-NLS-1$
OCLMessages.bind(OCLMessages.ParseCSTNodeType_ERROR_,
"PackageDeclarationCS",//$NON-NLS-1$
cstNode.eClass().getName()));
return sanitize(constraints);
}
List<PackageDeclarationCS> packageDecls = new BasicEList.FastCompare<PackageDeclarationCS>(
4);
// reverse the chain of package declarations to process them in the
// forward order
PackageDeclarationCS pkgdecl = (PackageDeclarationCS) cstNode;
while (pkgdecl != null) {
packageDecls.add(0, pkgdecl);
pkgdecl = pkgdecl.getPackageDeclarationCS();
}
for (PackageDeclarationCS packageDeclCS : packageDecls) {
packageDeclarationCS(packageDeclCS, constraints);
}
return sanitize(constraints);
}
/**
* Parses the input as an InvOrDefCS.
*
* @return the parsed OCL constraint, or <code>null</code> if it could not
* be parsed
*/
public CT parseInvOrDefCS() {
CSTNode cstNode = parseConcreteSyntax();
if (cstNode != null) {
if (cstNode instanceof InvOrDefCS) {
return sanitize(invOrDefCS((InvOrDefCS) cstNode,
getOCLEnvironment()));
}
ERROR(cstNode, "parseInvOrDefCS",//$NON-NLS-1$
OCLMessages.bind(OCLMessages.ParseCSTNodeType_ERROR_,
"InvOrDefCS",//$NON-NLS-1$
cstNode.eClass().getName()));
}
return sanitize(null);
}
/**
* Parses the input as a PrePostOrBodyDeclCS.
*
* @return the parsed OCL constraint, or <code>null</code> if it could not
* be parsed
*/
public CT parsePrePostOrBodyDeclCS() {
CSTNode cstNode = parseConcreteSyntax();
if (cstNode != null) {
if (cstNode instanceof PrePostOrBodyDeclCS) {
return sanitize(prePostOrBodyDeclCS(getOCLEnvironment(),
(PrePostOrBodyDeclCS) cstNode));
}
}
ERROR(cstNode, "parsePrePostOrBodyDeclCS",//$NON-NLS-1$
OCLMessages.bind(OCLMessages.ParseCSTNodeType_ERROR_,
"PrePostOrBodyDeclCS",//$NON-NLS-1$
formatEClassName(cstNode)));
return sanitize(null);
}
/**
* Parses the input as an InitOrDerValueCS.
*
* @return the parsed OCL constraint, or <code>null</code> if it could not
* be parsed
*/
public CT parseInitOrDerValueCS() {
CSTNode cstNode = parseConcreteSyntax();
if (cstNode != null) {
if (cstNode instanceof InitOrDerValueCS) {
return sanitize(initOrDerValueCS(getOCLEnvironment(),
(InitOrDerValueCS) cstNode));
}
}
ERROR(cstNode, "parseInitOrDerValueCS",//$NON-NLS-1$
OCLMessages.bind(OCLMessages.ParseCSTNodeType_ERROR_,
"InitOrDerValueCS",//$NON-NLS-1$
formatEClassName(cstNode)));
return sanitize(null);
}
/**
* Parses the input as a VariableDeclarationCS.
*
* @param addToEnvironment
* boolean whether or not to add the the parsed variable to the
* environment
* @return the parsed variable declaration, or <code>null</code> if it could
* not be parsed
*/
public Variable<C, PM> parseVariableDeclarationCS(boolean addToEnvironment) {
CSTNode cstNode = parseConcreteSyntax();
if (cstNode != null) {
if (cstNode instanceof VariableCS) {
return sanitize(variableDeclarationCS((VariableCS) cstNode,
getOCLEnvironment(), true));
}
ERROR(cstNode, "parseVariableDeclarationCS",//$NON-NLS-1$
OCLMessages.bind(OCLMessages.ParseCSTNodeType_ERROR_,
"VariableDeclarationCS",//$NON-NLS-1$
cstNode.eClass().getName()));
}
return sanitize(null);
}
@Override
public void ERROR(List<?> problemObjects, String rule, String problemMessage) {
history.setDisposable();
super.ERROR(problemObjects, rule, problemMessage);
}
@Override
public void ERROR(Object problemObject, String rule, String problemMessage) {
history.setDisposable();
super.ERROR(problemObject, rule, problemMessage);
}
@Override
public void ERROR(String problemMessage) {
history.setDisposable();
super.ERROR(problemMessage);
}
@Override
protected CT createConstraint() {
return history.record(super.createConstraint());
}
@Override
protected ExpressionInOCL<C, PM> createExpressionInOCL() {
return history.record(super.createExpressionInOCL());
}
@Override
protected boolean isErrorNode(TypedElement<C> expr) {
return history.isErrorNode(expr);
}
@Override
protected void markAsErrorNode(TypedElement<C> expr) {
history.markAsErrorNode(expr);
}
}