| /******************************************************************************* |
| * 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); |
| } |
| |
| |
| } |
| } |