blob: 8b2dc3597d583be1b4bf70f3b3345bdff173cb34 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2014 The University of York, Willink Transformations 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:
* Horacio Hoyos - initial API and implementation
******************************************************************************/
package org.eclipse.qvtd.pivot.qvtimperative.evaluation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.Variable;
import org.eclipse.ocl.pivot.internal.evaluation.OCLEvaluationVisitor;
import org.eclipse.ocl.pivot.internal.prettyprint.PrettyPrinter;
import org.eclipse.ocl.pivot.values.InvalidValueException;
import org.eclipse.qvtd.pivot.qvtbase.Predicate;
import org.eclipse.qvtd.pivot.qvtbase.Transformation;
import org.eclipse.qvtd.pivot.qvtcorebase.Assignment;
import org.eclipse.qvtd.pivot.qvtcorebase.CoreDomain;
import org.eclipse.qvtd.pivot.qvtcorebase.GuardPattern;
import org.eclipse.qvtd.pivot.qvtcorebase.PropertyAssignment;
import org.eclipse.qvtd.pivot.qvtcorebase.VariableAssignment;
import org.eclipse.qvtd.pivot.qvtimperative.ImperativeModel;
import org.eclipse.qvtd.pivot.qvtimperative.Mapping;
import org.eclipse.qvtd.pivot.qvtimperative.MappingCall;
import org.eclipse.qvtd.pivot.qvtimperative.MappingLoop;
import org.eclipse.qvtd.pivot.qvtimperative.MappingSequence;
import org.eclipse.qvtd.pivot.qvtimperative.MappingStatement;
import org.eclipse.qvtd.pivot.qvtimperative.MiddlePropertyAssignment;
import org.eclipse.qvtd.pivot.qvtimperative.MiddlePropertyCallExp;
import org.eclipse.qvtd.pivot.qvtimperative.VariablePredicate;
/**
* The Class QVTiTracingEvaluationVisitor is a decorator visitor that prints
* AST, variable values and other information useful for debugging.
*
* @author Horacio Hoyos
*/
public abstract class QVTiAbstractTracingEvaluationVisitor extends QVTiEvaluationVisitorDecorator
{
protected static Logger logger = Logger.getLogger("QVTiTrace");
/** The Constant DEFAULT_INDENT. */
protected static final String DEFAULT_INDENT = " ";
/** The verbose level low. */
static int VERBOSE_LEVEL_LOW = 0;
/** The verbose level med. */
static int VERBOSE_LEVEL_MED = 1;
/** The verbose level high. */
static int VERBOSE_LEVEL_HIGH = 2;
/** The verbose level. */
protected static int verboseLevel = VERBOSE_LEVEL_LOW;
/** The indent level. */
private int indentLevel = 0;
/**
* Instantiates a new QVTi tracing evaluation visitor.
*
* @param decorated the decorated
*/
public QVTiAbstractTracingEvaluationVisitor(@NonNull IQVTiEvaluationVisitor decorated) {
this(decorated, 0);
if (logger.getAppender("QVTTrace") == null) {
FileAppender appender = new FileAppender();
// configure the appender here, with file location, etc
appender.setFile("QVTTrace.log");
appender.setName("QVTTrace");
appender.setLayout(new PatternLayout(PatternLayout.DEFAULT_CONVERSION_PATTERN));
appender.setAppend(false);
appender.activateOptions();
logger.addAppender(appender);
}
logger.setAdditivity(false);
logger.setLevel(Level.DEBUG);
}
/**
* Instantiates a new QVTi tracing evaluation visitor.
*
* @param decorated the decorated
* @param indentLevel the indent level
*/
protected QVTiAbstractTracingEvaluationVisitor(@NonNull IQVTiEvaluationVisitor decorated, int indentLevel) {
super(decorated);
this.indentLevel = indentLevel;
}
/**
* Gets the verbose level.
*
* @return the verbose level
*/
public int getVerboseLevel() {
return verboseLevel;
}
/**
* Sets the verbose level.
*
* @param verboseLevel the new verbose level
*/
public void setVerboseLevel(int verboseLevel) {
QVTiAbstractTracingEvaluationVisitor.verboseLevel = verboseLevel;
}
/**
* Gets the indent.
*
* @return the indent
*/
protected @NonNull String getIndent() {
StringBuilder outputBuffer = new StringBuilder(indentLevel);
for (int i = 0; i < indentLevel; i++){
outputBuffer.append(DEFAULT_INDENT);
}
@SuppressWarnings("null")@NonNull String string = outputBuffer.toString();
return string;
}
@Override
public @NonNull Pattern getRegexPattern(@NonNull String regex) {
return delegate.getRegexPattern(regex);
}
/**
* Safe print.
*
* @param element the element
* @return the string
*/
protected @NonNull String safePrint(@Nullable Element element) {
return element != null ? PrettyPrinter.print(element) : "null";
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.evaluation.QVTiEvaluationVisitorDecorator#visitAssignment(org.eclipse.qvtd.pivot.qvtcorebase.Assignment)
*/
@Override
public @Nullable Object visitAssignment(@NonNull Assignment assignment) {
indentLevel++;
try {
Object value = safeVisit(assignment.getValue());
// Unbox to asign to ecore type
value = delegate.getEnvironmentFactory().getIdResolver().unboxedValueOf(value);
logger.info(getIndent() + "VisitAssignment " + prettyPrint(assignment.getESObject())
+ " := " + prettyPrint(value));
} catch (InvalidValueException ex) {
logger.info(getIndent() + "VisitAssignment " + prettyPrint(assignment.getESObject())
+ " := Invalid expression" );
}
Object result = delegate.visitAssignment(assignment);
indentLevel--;
return result;
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.evaluation.QVTiEvaluationVisitorDecorator#visitCoreDomain(org.eclipse.qvtd.pivot.qvtcorebase.CoreDomain)
*/
@Override
public @Nullable Object visitCoreDomain(@NonNull CoreDomain coreDomain) {
logger.info(getIndent() + "CoreDomain " + coreDomain.getName());
indentLevel++;
Object result = delegate.visitCoreDomain(coreDomain);
indentLevel--;
return result;
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.evaluation.QVTiEvaluationVisitorDecorator#visitGuardPattern(org.eclipse.qvtd.pivot.qvtcorebase.GuardPattern)
*/
@Override
public @Nullable Object visitGuardPattern(@NonNull GuardPattern guardPattern) {
if (guardPattern.getArea() instanceof CoreDomain) {
logger.info(getIndent() + "Visiting CoreDomain GuardPattern");
}
if (guardPattern.getArea() instanceof Mapping) {
logger.info(getIndent() + "Visiting Mapping GuardPattern");
}
indentLevel++;
// Some variables are assigned after visit
List<Variable> nullVars = new ArrayList<Variable>();
for (@SuppressWarnings("null")@NonNull Variable v : guardPattern.getVariable()) {
if (delegate.getEvaluationEnvironment().getValueOf(v) == null) {
nullVars.add(v);
OCLExpression initExp = v.getOwnedInit();
if (initExp != null) {
indentLevel++;
logger.info(getIndent() + "Variable " + v.getName() + " init value:" + safePrint(initExp));
indentLevel--;
}
} else {
logger.info(getIndent() + "Variable " + v.getName() + ": " + prettyPrint(delegate.getEvaluationEnvironment().getValueOf(v)));
}
}
Object result = delegate.visitGuardPattern(guardPattern);
if (result == Boolean.TRUE) {
for (Variable v : nullVars) {
logger.info(getIndent() + "Variable " + v.getName() + ": " + prettyPrint(delegate.getEvaluationEnvironment().getValueOf(v)));
}
}
if (guardPattern.getPredicate().size() == 0) {
logger.info(getIndent() + "GuardPattern has no predicates.");
} else {
logger.info(getIndent() + "GuardPattern result: " + result);
}
indentLevel--;
return result;
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.evaluation.QVTiEvaluationVisitorDecorator#visitImperativeModel(org.eclipse.qvtd.pivot.qvtimperative.ImperativeModel)
*/
@Override
public @Nullable Object visitImperativeModel(@NonNull ImperativeModel imperativeModel) {
return delegate.visitImperativeModel(imperativeModel);
}
/* (non-Javadoc)
* @see org.eclipse.ocl.pivot.util.AbstractExtendingVisitor#visitIteratorExp(org.eclipse.ocl.pivot.IteratorExp)
*/
@Override
public @Nullable Object visitIteratorExp(@NonNull org.eclipse.ocl.pivot.IteratorExp object) {
return delegate.visitIteratorExp(object);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.evaluation.QVTiEvaluationVisitorDecorator#visitMapping(org.eclipse.qvtd.pivot.qvtimperative.Mapping)
*/
@Override
public @Nullable Object visitMapping(@NonNull Mapping mapping) {
logger.info(getIndent() + "Mapping " + mapping.getName());
indentLevel++;
Object result = delegate.visitMapping(mapping);
indentLevel--;
return result;
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.evaluation.QVTiEvaluationVisitorDecorator#visitMappingCall(org.eclipse.qvtd.pivot.qvtimperative.MappingCall)
*/
@Override
public @Nullable Object visitMappingCall(@NonNull MappingCall mappingCall) {
logger.info(getIndent() + "Visiting MappingCall, calling: " + mappingCall.getReferredMapping().getName());
indentLevel++;
Object result = delegate.visitMappingCall(mappingCall);
indentLevel--;
return result;
}
@Override
public @Nullable Object visitMappingLoop(@NonNull MappingLoop mappingLoop) {
logger.info(getIndent() + "Visiting MappingLoop, binding: " + mappingLoop.getOwnedIterators().get(0).getName());
indentLevel++;
Object result = delegate.visitMappingLoop(mappingLoop);
indentLevel--;
return result;
}
@Override
public @Nullable Object visitMappingSequence(@NonNull MappingSequence mappingSequence) {
Object result = delegate.visitMappingSequence(mappingSequence);
return result;
}
@Override
public @Nullable Object visitMappingStatement(@NonNull MappingStatement vappingStatement) {
return visiting(vappingStatement);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.util.QVTimperativeVisitor#visitMiddlePropertyAssignment(org.eclipse.qvtd.pivot.qvtimperative.MiddlePropertyAssignment)
*/
@Override
public Object visitMiddlePropertyAssignment(@NonNull MiddlePropertyAssignment propertyAssignment) {
indentLevel++;
try {
Object value = safeVisit(propertyAssignment.getValue());
// Unbox to asign to ecore type
value = delegate.getEnvironmentFactory().getIdResolver().unboxedValueOf(value);
logger.info(getIndent() + "VisitMiddlePropertyAssignment " + propertyAssignment.getSlotExpression()
+ "." + propertyAssignment.getTargetProperty().getName() + " = " + prettyPrint(value));
} catch (InvalidValueException ex) {
logger.info(getIndent() + "VisitMiddlePropertyAssignment " + propertyAssignment.getSlotExpression()
+ "." + propertyAssignment.getTargetProperty().getName() + " = InvalidValue" );
}
Object result = delegate.visitMiddlePropertyAssignment(propertyAssignment);
indentLevel--;
return result;
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.util.QVTimperativeVisitor#visitMiddlePropertyCallExp(org.eclipse.qvtd.pivot.qvtimperative.MiddlePropertyCallExp)
*/
@Override
public Object visitMiddlePropertyCallExp(@NonNull MiddlePropertyCallExp callExp) {
return delegate.visitMiddlePropertyCallExp(callExp);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.evaluation.QVTiEvaluationVisitorDecorator#visitPropertyAssignment(org.eclipse.qvtd.pivot.qvtcorebase.PropertyAssignment)
*/
@Override
public @Nullable Object visitPropertyAssignment(@NonNull PropertyAssignment propertyAssignment) {
try {
Object value = safeVisit(propertyAssignment.getValue());
// Unbox to asign to ecore type
value = delegate.getEnvironmentFactory().getIdResolver().unboxedValueOf(value);
logger.info(getIndent() + "VisitPropertyAssignment " + propertyAssignment.getSlotExpression()
+ "." + propertyAssignment.getTargetProperty().getName() + " = " + prettyPrint(value));
} catch (InvalidValueException ex) {
logger.info(getIndent() + "VisitPropertyAssignment " + propertyAssignment.getSlotExpression()
+ "." + propertyAssignment.getTargetProperty().getName() + " = InvalidValueException" );
}
//indentLevel++;
Object result = delegate.visitPropertyAssignment(propertyAssignment);
//indentLevel--;
return result;
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.evaluation.QVTiEvaluationVisitorDecorator#visitTransformation(org.eclipse.qvtd.pivot.qvtbase.Transformation)
*/
@Override
public @Nullable Object visitTransformation(@NonNull Transformation transformation) {
logger.info("---- Transformation " + transformation.getName() + " ----");
indentLevel++;
Object result = delegate.visitTransformation(transformation);
indentLevel--;
logger.info("---- Transformation End ----");
return result;
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.evaluation.QVTiEvaluationVisitorDecorator#visitPredicate(org.eclipse.qvtd.pivot.qvtbase.Predicate)
*/
@Override
public @Nullable Object visitPredicate(@NonNull Predicate predicate) {
OCLExpression exp = predicate.getConditionExpression();
Object value = safeVisit(exp);
logger.info(getIndent() + "Predicate " + safePrint(exp) + " is " + String.valueOf(value));
return delegate.visitPredicate(predicate);
}
/* (non-Javadoc)
* @see org.eclipse.qvtd.pivot.qvtimperative.evaluation.QVTiEvaluationVisitorDecorator#visitVariableAssignment(org.eclipse.qvtd.pivot.qvtcorebase.VariableAssignment)
*/
@Override
public @Nullable Object visitVariableAssignment(@NonNull VariableAssignment variableAssignment) {
Variable v = variableAssignment.getTargetVariable();
logger.info(getIndent() + "VisitVariableAssignment " + v
+ ": " + ((OCLEvaluationVisitor)delegate).safeVisit(variableAssignment.getValue()));
indentLevel++;
Object result = delegate.visitVariableAssignment(variableAssignment);
indentLevel--;
return result;
}
@Override
public @Nullable Object visitVariablePredicate(@NonNull VariablePredicate variablePredicate) {
Variable v = variablePredicate.getTargetVariable();
logger.info(getIndent() + "VisitVariablePredicate " + v
+ ": " + ((OCLEvaluationVisitor)delegate).safeVisit(variablePredicate.getConditionExpression()));
indentLevel++;
Object result = delegate.visitVariablePredicate(variablePredicate);
indentLevel--;
return result;
}
/**
* Pretty print Objects. EObjects are printed at 1 level depth (i.e. no
* navigation through associations). Other objects are printed as Strings
*
* @param object the object
* @return the string
*/
protected String prettyPrint(Object object) {
List<String> lines = new ArrayList<String>();
if (object instanceof EObject) {
EObject eo = (EObject) object;
EClass eClass = eo.eClass();
lines.add(eClass.getName() + " [" + Integer.toHexString(eo.hashCode()) + "] {"); //
indentLevel++;
for (EStructuralFeature feature : eClass.getEAllStructuralFeatures()) {
Object value = eo.eGet(feature);
String line = getIndent() + feature.getName() + " = ";
if (value instanceof Collection<?>) {
int size = ((Collection<?>)value).size();
if (size > 0) {
line += "[" + ((EObject)((Iterable<?>) value).iterator().next()).eClass().getName() + "] x " + size;
} else {
line += "[ ]";
}
} else if (value instanceof EObject) {
eClass = ((EObject) value).eClass();
line += eClass.getName() + " [" + Integer.toHexString(value.hashCode()) + "]";
} else {
if (value == null) {
line += "[ null ]";
} else {
line += String.valueOf(value);
}
}
lines.add(line);
}
lines.add(getIndent() + "}");
indentLevel--;
StringBuilder sb = new StringBuilder();
for (String line : lines) {
sb.append(line).append('\n');
}
sb.delete(sb.length()-1, sb.length());
return sb.toString();
} else {
return String.valueOf(object);
}
}
protected String prettyPrintUnident(Object object) {
List<String> lines = new ArrayList<String>();
if (object instanceof EObject) {
EObject eo = (EObject) object;
EClass eClass = eo.eClass();
lines.add(eClass.getName() + " [" + Integer.toHexString(eo.hashCode()) + "] {"); //
indentLevel++;
for (EStructuralFeature feature : eClass.getEAllStructuralFeatures()) {
Object value = eo.eGet(feature);
String line = DEFAULT_INDENT + feature.getName() + " = ";
if (value instanceof Collection<?>) {
int size = ((Collection<?>)value).size();
if (size > 0) {
line += "[" + ((EObject)((Iterable<?>) value).iterator().next()).eClass().getName() + "] x " + size;
} else {
line += "[ ]";
}
} else if (value instanceof EObject) {
eClass = ((EObject) value).eClass();
line += eClass.getName() + " [" + Integer.toHexString(value.hashCode()) + "]";
} else {
if (value == null) {
line += "[ null ]";
} else {
line += String.valueOf(value);
}
}
lines.add(line);
}
lines.add(DEFAULT_INDENT + "}");
indentLevel--;
StringBuilder sb = new StringBuilder();
for (String line : lines) {
sb.append(line).append('\n');
}
sb.delete(sb.length()-1, sb.length());
return sb.toString();
} else {
return String.valueOf(object);
}
}
}