/**
* <copyright>
*
* Copyright (c) 2007,2010 E.D.Willink 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:
* E.D.Willink - initial API and implementation
* (with inspiration from org.eclipse.ocl.util.ToStringVisitor)
*
* </copyright>
*
* $Id: OCLExpressionUnparser.java,v 1.2 2010/04/08 06:26:36 ewillink Exp $
*/
package org.eclipse.ocl.examples.parser.ocl.unparser;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypedElement;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.ocl.ecore.AssociationClassCallExp;
import org.eclipse.ocl.ecore.BooleanLiteralExp;
import org.eclipse.ocl.ecore.CallOperationAction;
import org.eclipse.ocl.ecore.CollectionItem;
import org.eclipse.ocl.ecore.CollectionLiteralExp;
import org.eclipse.ocl.ecore.CollectionLiteralPart;
import org.eclipse.ocl.ecore.CollectionRange;
import org.eclipse.ocl.ecore.Constraint;
import org.eclipse.ocl.ecore.EnumLiteralExp;
import org.eclipse.ocl.ecore.FeatureCallExp;
import org.eclipse.ocl.ecore.IfExp;
import org.eclipse.ocl.ecore.IntegerLiteralExp;
import org.eclipse.ocl.ecore.InvalidLiteralExp;
import org.eclipse.ocl.ecore.IterateExp;
import org.eclipse.ocl.ecore.IteratorExp;
import org.eclipse.ocl.ecore.LetExp;
import org.eclipse.ocl.ecore.MessageExp;
import org.eclipse.ocl.ecore.NullLiteralExp;
import org.eclipse.ocl.ecore.OCLExpression;
import org.eclipse.ocl.ecore.OperationCallExp;
import org.eclipse.ocl.ecore.PropertyCallExp;
import org.eclipse.ocl.ecore.RealLiteralExp;
import org.eclipse.ocl.ecore.SendSignalAction;
import org.eclipse.ocl.ecore.StateExp;
import org.eclipse.ocl.ecore.StringLiteralExp;
import org.eclipse.ocl.ecore.TupleLiteralExp;
import org.eclipse.ocl.ecore.TupleLiteralPart;
import org.eclipse.ocl.ecore.TypeExp;
import org.eclipse.ocl.ecore.UnlimitedNaturalLiteralExp;
import org.eclipse.ocl.ecore.UnspecifiedValueExp;
import org.eclipse.ocl.ecore.Variable;
import org.eclipse.ocl.ecore.VariableExp;
import org.eclipse.ocl.ecore.util.EcoreSwitch;
import org.eclipse.ocl.examples.common.utils.ClassUtils;
import org.eclipse.ocl.examples.parser.unparser.UnparserWithReflection;
import org.eclipse.ocl.types.CollectionType;
import org.eclipse.ocl.types.VoidType;
import org.eclipse.ocl.utilities.UMLReflection;
public abstract class OCLExpressionUnparser extends UnparserWithReflection<EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint>
{
public static final String IF = "if";
public static final String AT_PRE = "@pre";
public static final String DOT_OR_ARROW = ". or ->";
public static final String UNARY = "unary";
public static final String IMPLIES = "implies";
public static final String LOGICAL = "logical";
public static final String CONDITIONAL = "conditional";
public static final String RELATIONAL = "relational";
public static final String ADDITIVE = "additive";
public static final String MULTIPLICATIVE = "multiplicative";
protected class OCLExpressionUnparserSwitch extends EcoreSwitch<Object> {
@Override
public Object caseAssociationClassCallExp(AssociationClassCallExp object) {
return unparseAssociationClassCallExp(object);
}
@Override
public Object caseBooleanLiteralExp(BooleanLiteralExp object) {
return unparseBooleanLiteralExp(object);
}
@Override
public Object caseCollectionItem(CollectionItem object) {
return unparseCollectionItem(object);
}
@Override
public Object caseCollectionLiteralExp(CollectionLiteralExp object) {
return unparseCollectionLiteralExp(object);
}
@Override
public Object caseCollectionRange(CollectionRange object) {
return unparseCollectionRange(object);
}
@Override
public Object caseEnumLiteralExp(EnumLiteralExp object) {
return unparseEnumLiteralExp(object);
}
@Override
public Object caseIfExp(IfExp object) {
return unparseIfExp(object);
}
@Override
public Object caseIntegerLiteralExp(IntegerLiteralExp object) {
return unparseIntegerLiteralExp(object);
}
@Override
public Object caseInvalidLiteralExp(InvalidLiteralExp object) {
return unparseInvalidLiteralExp(object);
}
@Override
public Object caseIterateExp(IterateExp object) {
return unparseIterateExp(object);
}
@Override
public Object caseIteratorExp(IteratorExp object) {
return unparseIteratorExp(object);
}
@Override
public Object caseLetExp(LetExp object) {
return unparseLetExp(object);
}
@Override
public Object caseMessageExp(MessageExp object) {
return unparseMessageExp(object);
}
@Override
public Object caseNullLiteralExp(NullLiteralExp object) {
return unparseNullLiteralExp(object);
}
@Override
public Object caseOperationCallExp(OperationCallExp object) {
return unparseOperationCallExp(object);
}
@Override
public Object casePropertyCallExp(PropertyCallExp object) {
return unparsePropertyCallExp(object);
}
@Override
public Object caseRealLiteralExp(RealLiteralExp object) {
return unparseRealLiteralExp(object);
}
@Override
public Object caseStateExp(StateExp object) {
return unparseStateExp(object);
}
@Override
public Object caseStringLiteralExp(StringLiteralExp object) {
return unparseStringLiteralExp(object);
}
@Override
public Object caseTupleLiteralExp(TupleLiteralExp object) {
return unparseTupleLiteralExp(object);
}
@Override
public Object caseTupleLiteralPart(TupleLiteralPart object) {
return unparseTupleLiteralPart(object);
}
@Override
public Object caseTypeExp(TypeExp object) {
return unparseTypeExp(object);
}
@Override
public Object caseUnlimitedNaturalLiteralExp(UnlimitedNaturalLiteralExp object) {
return unparseUnlimitedNaturalLiteralExp(object);
}
@Override
public Object caseUnspecifiedValueExp(UnspecifiedValueExp object) {
return unparseUnspecifiedValueExp(object);
}
@Override
public Object caseVariable(Variable object) {
return unparseVariable(object);
}
@Override
public Object caseVariableExp(VariableExp object) {
return unparseVariableExp(object);
}
@Override public Object defaultCase(EObject object) {
append("\n***" + getClass().getSimpleName() + "-Unsupported-" + object.getClass().getSimpleName() + "***\n");
return this;
}
}
private EcoreSwitch<Object> expressionSwitch = null;
private Stack<String> precedenceStack = new Stack<String>(); // Stack of pushed PrecedenceLevels
private Map<String, Integer> precedenceIndexes = new HashMap<String, Integer>(); // Precedence of each PrecedenceLevel
protected Map<String, String> infixOperators; // Map of infix operator name to PrecedenceLevel
protected Map<String, String> prefixOperators; // Map of prefix operator name to PrecedenceLevel
public OCLExpressionUnparser(Resource resource, UMLReflection<?, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> uml) {
super(resource, uml);
List<String> precedenceKeys = buildPrecedenceMap();
int precedence = 0;
for (String precedenceKey : precedenceKeys)
precedenceIndexes.put(precedenceKey, Integer.valueOf(precedence++));
precedenceIndexes.put(null, Integer.valueOf(precedence));
infixOperators = buildInfixOperators();
prefixOperators = buildPrefixOperators();
}
/**
* Return a map of operator name to infix PrecedenceLevel.
*
* @return
*/
protected Map<String, String> buildInfixOperators() {
Map<String, String> inFixOperators = new HashMap<String, String>();
inFixOperators.put("*", MULTIPLICATIVE);
inFixOperators.put("/", MULTIPLICATIVE);
inFixOperators.put("+", ADDITIVE);
inFixOperators.put("-", ADDITIVE);
inFixOperators.put("<", RELATIONAL);
inFixOperators.put(">", RELATIONAL);
inFixOperators.put("<=", RELATIONAL);
inFixOperators.put(">=", RELATIONAL);
inFixOperators.put("=", CONDITIONAL);
inFixOperators.put("<>", CONDITIONAL);
inFixOperators.put("and", LOGICAL);
inFixOperators.put("or", LOGICAL);
inFixOperators.put("xor", LOGICAL);
inFixOperators.put("implies", IMPLIES);
return inFixOperators;
}
/**
* Return a map of operator name to prefix PrecedenceLevel.
*
* @return
*/
protected Map<String, String> buildPrefixOperators() {
Map<String, String> preFixOperators = new HashMap<String, String>();
preFixOperators.put("-", UNARY);
preFixOperators.put("not", UNARY);
return preFixOperators;
}
/**
* Return an ordered list of PrecedenceLevels.
*
* Derived parsers may totally rearrange by suffixing functionality.
*
* @return
*/
protected List<String> buildPrecedenceMap() {
List<String> precedenceKeys = new ArrayList<String>();
precedenceKeys.add(AT_PRE);
precedenceKeys.add(DOT_OR_ARROW);
precedenceKeys.add(UNARY);
precedenceKeys.add(MULTIPLICATIVE);
precedenceKeys.add(ADDITIVE);
precedenceKeys.add(IF);
precedenceKeys.add(RELATIONAL);
precedenceKeys.add(CONDITIONAL);
precedenceKeys.add(LOGICAL);
precedenceKeys.add(IMPLIES);
return precedenceKeys;
}
protected EcoreSwitch<Object> createExpressionSwitch() {
return new OCLExpressionUnparserSwitch();
}
protected Object doExpressionSwitch(EObject oclExpression) {
if (oclExpression != null)
return expressionSwitch.doSwitch(oclExpression);
else
append("_null_expression_");
return this;
}
protected Object doExpressionSwitch(ETypedElement oclExpression) {
if (oclExpression != null)
return expressionSwitch.doSwitch(oclExpression);
else
append("_null_typed_element_");
return this;
}
protected Object doExpressionsSwitch(List<OCLExpression> oclExpressions, String separator) {
if ((oclExpressions != null) && (oclExpressions.size() > 0)) {
boolean first = true;
for (OCLExpression oclExpression : oclExpressions) {
if (!first)
append(separator);
doExpressionSwitch(oclExpression);
first = false;
}
}
return this;
}
@Override protected void initialize() {
super.initialize();
if (expressionSwitch == null)
expressionSwitch = createExpressionSwitch();
}
protected String initialLower(String name) {
if (name == null || name.length() == 0) {
return name;
}
StringBuffer result = new StringBuffer(name);
result.setCharAt(0, Character.toLowerCase(result.charAt(0)));
return result.toString();
}
/**
* Complete to append text for a region at precedenceKey.
*
* For diagnostic purposes, precedenceKey must match the value at top of the precedence stack
* resulting from pushPrecedence().
*
* @param precedenceKey
*/
protected void popPrecedence(String precedenceKey) {
String innerKey = precedenceStack.pop();
assert (innerKey == precedenceKey) || innerKey.equals(precedenceKey);
String outerKey = precedenceStack.isEmpty() ? null : precedenceStack.peek();
Integer outerIndex = precedenceIndexes.get(outerKey);
Integer innerIndex = precedenceIndexes.get(innerKey);
if ((outerKey != null) && (innerKey != null) && ((outerIndex == null) || (innerIndex == null) || (innerIndex >= outerIndex)))
append(")");
}
/**
* Prepare to append text for a region at precedenceKey. null is the lowest precedenceKey.
*
* @param precedenceKey
*/
protected void pushPrecedence(String precedenceKey) {
String outerKey = precedenceStack.isEmpty() ? null : precedenceStack.peek();
Integer outerIndex = precedenceIndexes.get(outerKey);
Integer innerIndex = precedenceIndexes.get(precedenceKey);
precedenceStack.push(precedenceKey);
if ((outerKey != null) && (precedenceKey != null) && ((outerIndex == null) || (innerIndex == null) || (innerIndex >= outerIndex)))
append("(");
}
protected Object unparseAssociationClassCallExp(AssociationClassCallExp object) {
if (object == null)
append("_null_association_class_");
else {
pushPrecedence(".");
doExpressionSwitch(object.getSource());
append(".");
appendName(initialLower(formatName(object.getReferredAssociationClass())));
unparseIsMarkedPre(object);
List<OCLExpression> qualifiers = ClassUtils.asClassUnchecked(object.getQualifier(), (List<OCLExpression>)null);
if (qualifiers.size() > 0) {
append("[");
doExpressionSwitch(qualifiers.get(0));
append("]");
}
popPrecedence(".");
}
return this;
}
protected Object unparseBooleanLiteralExp(BooleanLiteralExp object) {
if (object == null)
append("_null_boolean_");
else
append(object.getBooleanSymbol().toString());
return this;
}
protected Object unparseCollectionItem(CollectionItem object) {
if (object == null)
append("_null_collection_item_");
else
doExpressionSwitch(object.getItem());
return this;
}
protected Object unparseCollectionLiteralExp(CollectionLiteralExp object) {
if (object == null)
append("_null_collection_");
else {
pushPrecedence(null);
append(object.getKind().getLiteral());
append("{");
List<CollectionLiteralPart> parts = ClassUtils.asClassUnchecked(object.getPart(), (List<CollectionLiteralPart>)null);
if (parts.size() > 0) {
append("\n");
indent();
boolean first = true;
for (CollectionLiteralPart part : parts) {
if (!first)
append(",\n");
doExpressionSwitch(part);
first = false;
}
exdent();
append("\n");
}
append("}");
popPrecedence(null);
}
return this;
}
protected Object unparseCollectionRange(CollectionRange object) {
if (object == null)
append("_null_collection_item_");
else {
doExpressionSwitch(object.getFirst());
append(" .. ");
doExpressionSwitch(object.getLast());
}
return this;
}
protected Object unparseEnumLiteralExp(EnumLiteralExp object) {
if (object == null)
append("_null_enum_");
else
appendQualifiedName(object.getReferredEnumLiteral());
return this;
}
protected Object unparseIfExp(IfExp object) {
if (object == null)
append("_null_if_");
else {
pushPrecedence(IF);
append("if ");
doExpressionSwitch(object.getCondition());
append(" then ");
doExpressionSwitch(object.getThenExpression());
append(" else ");
doExpressionSwitch(object.getElseExpression());
append(" endif");
popPrecedence(IF);
}
return this;
}
protected Object unparseIntegerLiteralExp(IntegerLiteralExp object) {
if (object == null)
append("_null_integer_");
else
append(object.getIntegerSymbol().toString());
return this;
}
protected Object unparseInvalidLiteralExp(InvalidLiteralExp object) {
if (object == null)
append("_null_invalid_");
else
append("OclInvalid");
return this;
}
protected void unparseIsMarkedPre(FeatureCallExp object) {
if (object.isMarkedPre())
append("@pre");
}
protected Object unparseIterateExp(IterateExp object) {
if (object == null)
append("_null_iterate_");
else {
pushPrecedence("->");
doExpressionSwitch(object.getSource());
append("->iterate(");
pushPrecedence(null);
List<Variable> iterators = ClassUtils.asClassUnchecked(object.getIterator(), (List<Variable>)null);
if ((iterators != null) && (iterators.size() > 0)) {
boolean first = true;
for (Variable iterator : iterators) {
if (!first)
append(", ");
unparseVariable(iterator);
first = false;
}
}
append("; ");
unparseVariable((Variable) object.getResult());
append(" |\n");
indent();
doExpressionSwitch(object.getBody());
append(")");
popPrecedence(null);
exdent();
popPrecedence("->");
}
return this;
}
protected Object unparseIteratorExp(IteratorExp object) {
if (object == null)
append("_null_iterator_");
else {
pushPrecedence("->");
doExpressionSwitch(object.getSource());
append("->");
appendName(object);
pushPrecedence(null);
append("(");
List<Variable> iterators = ClassUtils.asClassUnchecked(object.getIterator(), (List<Variable>)null);
if ((iterators != null) && (iterators.size() > 0)) {
boolean first = true;
for (Variable iterator : iterators) {
if (!first)
append(", ");
unparseVariable(iterator);
first = false;
}
}
append(" |\n");
indent();
doExpressionSwitch(object.getBody());
append(")");
popPrecedence(null);
exdent();
popPrecedence("->");
}
return this;
}
protected Object unparseLetExp(LetExp object) {
if (object == null)
append("_null_let_");
else {
pushPrecedence("let");
append("let ");
unparseVariable((Variable) object.getVariable());
append(" in ");
doExpressionSwitch(object.getIn());
popPrecedence("let");
}
return this;
}
protected Object unparseMessageExp(MessageExp object) {
if (object == null)
append("_null_message_");
else {
doExpressionSwitch(object.getTarget());
append((object.getType() instanceof CollectionType<?,?>) ? "^^" : "^");
CallOperationAction calledOperation = object.getCalledOperation();
SendSignalAction sentSignal = object.getSentSignal();
if (calledOperation != null) {
appendName(getOperation(calledOperation));
} else if (sentSignal != null) {
appendName(getSignal(sentSignal));
}
append("(");
doExpressionsSwitch(ClassUtils.asClassUnchecked(object.getArgument(), (List<OCLExpression>)null), ", ");
append(")");
}
return this;
}
protected Object unparseNullLiteralExp(NullLiteralExp object) {
if (object == null)
append("_null_null_");
else
append("null");
return this;
}
protected Object unparseOperationCallExp(OperationCallExp object) {
if (object == null)
append("'_null_operation_call_'");
else {
OCLExpression source = (OCLExpression) object.getSource();
Object sourceType = source != null ? source.getType() : null;
boolean isCollection = sourceType instanceof CollectionType<?,?>;
EOperation eOperation = object.getReferredOperation();
String operationName = eOperation != null ? eOperation.getName() : null;
int operationArity = eOperation != null ? eOperation.getEParameters().size() : 0;
boolean doneIt = false;
if (!object.isMarkedPre()) {
if (operationArity == 0) {
String precedenceKey = prefixOperators.get(operationName);
if (precedenceKey != null) {
pushPrecedence(precedenceKey);
append(operationName);
if (source != null) {
append(" ");
doExpressionSwitch(source);
}
popPrecedence(precedenceKey);
doneIt = true;
}
}
else if (operationArity == 1) {
String precedenceKey = infixOperators.get(operationName);
if (precedenceKey != null) {
pushPrecedence(precedenceKey);
if (source != null) {
doExpressionSwitch(source);
append(" ");
}
append(operationName);
append(" ");
doExpressionsSwitch(ClassUtils.asClassUnchecked(object.getArgument(), (List<OCLExpression>)null), ", ");
popPrecedence(precedenceKey);
doneIt = true;
}
}
}
if (!doneIt) {
String operationPrefixKey = isCollection ? "->" : ".";
pushPrecedence(operationPrefixKey);
if (source != null) {
doExpressionSwitch(source);
append(operationPrefixKey);
}
appendName(eOperation);
append("(");
doExpressionsSwitch(ClassUtils.asClassUnchecked(object.getArgument(), (List<OCLExpression>)null), ", ");
append(")");
unparseIsMarkedPre(object);
popPrecedence(operationPrefixKey);
}
}
return this;
}
protected Object unparsePropertyCallExp(PropertyCallExp object) {
if (object == null)
append("_null_property_call_");
else {
OCLExpression source = (OCLExpression) object.getSource();
if (source != null) {
doExpressionSwitch(source);
append(".");
}
appendName(object.getReferredProperty());
unparseIsMarkedPre(object);
List<OCLExpression> qualifiers = ClassUtils.asClassUnchecked(object.getQualifier(), (List<OCLExpression>)null);
if ((qualifiers != null) && (qualifiers.size() > 0)) {
append("[");
doExpressionsSwitch(qualifiers, ", ");
append("]");
}
}
return this;
}
protected Object unparseRealLiteralExp(RealLiteralExp object) {
if (object == null)
append("_null_real_");
else
append("'" + object.getRealSymbol().toString() +"'");
return this;
}
protected Object unparseStateExp(StateExp object) {
if (object == null)
append("_null_state_");
else
appendName(object);
return this;
}
protected Object unparseStringLiteralExp(StringLiteralExp object) {
if (object == null)
append("_null_string_");
else
append("'" + object.getStringSymbol().toString() +"'");
return this;
}
protected Object unparseTupleLiteralExp(TupleLiteralExp object) {
if (object == null)
append("_null_tuple_");
else {
pushPrecedence(null);
append("Tuple{");
boolean first = true;
for (TupleLiteralPart part : ClassUtils.asClassUnchecked(object.getPart(), (List<TupleLiteralPart>)null)) {
if (!first)
append(", ");
unparseTupleLiteralPart(part);
first = false;
}
append("}");
popPrecedence(null);
}
return this;
}
protected Object unparseTupleLiteralPart(TupleLiteralPart object) {
if (object == null)
append("_null_tuple_part_");
else {
Object type = object.getType();
appendName(object);
if (type != null) {
append(" : ");
appendName(type);
}
OCLExpression value = (OCLExpression) object.getValue();
if (value != null) {
append(" = ");
doExpressionSwitch(value);
}
}
return this;
}
protected Object unparseTypeExp(TypeExp object) {
if (object == null)
append("_null_type_");
else
appendQualifiedName(object.getReferredType());
return this;
}
protected Object unparseUnlimitedNaturalLiteralExp(UnlimitedNaturalLiteralExp object) {
if (object == null)
append("_null_unlimited_natural_");
else if (object.isUnlimited())
append("*");
else
append(object.getIntegerSymbol().toString());
return this;
}
protected Object unparseUnspecifiedValueExp(UnspecifiedValueExp object) {
if (object == null)
append("_null_unspecified_");
else {
append("?");
Object type = object.getType();
if ((type != null) && !(type instanceof VoidType<?>)) {
append(" : ");
appendName(type);
}
append("OclInvalid");
}
return this;
}
protected Object unparseVariable(Variable variable) {
if (variable == null)
append("_null_variable_");
else {
appendName(variable);
Object type = variable.getType();
if ((type instanceof EObject) && ((EObject)type).eIsProxy())
type = variable.getType();
if ((type != null) && !(type instanceof VoidType<?>)) {
append(" : ");
appendQualifiedName(type);
}
org.eclipse.ocl.expressions.OCLExpression<EClassifier> init = variable.getInitExpression();
if (init != null) {
append(" = ");
doExpressionSwitch(init);
}
}
return this;
}
protected Object unparseVariableExp(VariableExp object) {
if (object == null)
append("_null_variable_exp_");
else {
Variable variable = (Variable) object.getReferredVariable();
if (variable != null)
appendName(variable);
else
appendName(object);
}
return this;
}
}