/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * ExpressionCopier.java * Creation date: Dec. 10, 2006 * By: Raymond Cypher */ package org.openquark.cal.compiler; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; /** * ExpressionCopier is an implementation of the ExpressionVisitor which * performs a deep copy of the Expression elements. Each visit method * returns as its return value a deep copy of the element it visits. * <p> * * This class is intended to be the base class of other visitors that need to * perform Expression transformations, i.e. taking an Expression as input, * generating a new Expression as output. For example, a visitor that needs * to change all invocations of "Math.sin" to "Math.cos" can be written * as: * <p> * * <pre><code> * class ReferenceRenamer extends ExpressionCopier<Void> { * * public Expression visitVar( * Expression.Var var, Void arg) { * * if (var.getName().getQualifiedName().equals("Math.sin")) { * * // Create a new Var instance with appropriate changes. * * } else { * return super.visitVar(var, arg); * } * } * } * </code></pre> * * To use this transformation, one can simply apply it to an Expression's * root element: * <pre><code> * Expression transformedElement = * (Expression)originalElement.accept(new ReferenceRenamer(), null); * </code></pre> * * The cast of the return value is required because the ExpressionVisitor * interface declares all visit methods to have the generic return type of * java.lang.Object. Each visit method in every subclass of ExpressionCopier is * generally obligated to return an object of the same type as the element being * visited. An exception to this rule is that it is usually acceptable that * visiting an element in the Expression hierarchy returns an element * representing a different kind of expression. * <p> * * Note that the visitVar method above * defaults to a call to the supertype's implementation. This * ensures that in the case when no transformations are * required, a simple deep copy of the Expression element is returned. In * general, in any situations where the supertype's implementation is not * invoked, it is the responsibility of the subclass' implementation to 1) * traverse through any child elements if necessary, and 2) provide the new * Expression element resulting from the transformation of the element being * visited. * <p> * * In ExpressionCopier, the argument supplied to the visit methods are ignored. * Subclasses of ExpressionCopier are free to use the argument for their own * purposes, and the traversal logic in ExpressionCopier will properly * propagate the supplied arguments down to child elements. * <p> * * Note: the design of this class mandates that all fields whose type descend * from Expression must be visited by the visitor. In particular, * for array-valued fields, each array element must be individually visited and * consequently copied. * * This usage guideline applies to any modifications to this class, as well * as any subclass that aims to preserve the deep-copying semantics. * <p> * * @param <T> the argument type. If the visitation argument is not used, specify {@link Void}. * * @author Raymond Cypher */ public class ExpressionCopier<T> implements ExpressionVisitor<T, Object> { /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitApplication(org.openquark.cal.compiler.Expression.Application, java.lang.Object) */ public Expression visitApplication( Expression.Appl appl, T arg) { return new Expression.Appl( (Expression)appl.getE1().accept(this, arg), (Expression)appl.getE2().accept(this, arg)); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitCast(org.openquark.cal.compiler.Expression.Cast, java.lang.Object) */ public Expression visitCast( Expression.Cast cast, T arg) { return Expression.Cast.makeWithCastTypeProvider(cast.getCastTypeProvider(), (Expression.Var)cast.getVarToCast().accept(this, arg)); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitDataConsSelection(org.openquark.cal.compiler.Expression.DataConsSelection, java.lang.Object) */ public Expression visitDataConsSelection( Expression.DataConsSelection dcSelection, T arg) { return new Expression.DataConsSelection( (Expression)dcSelection.getDCValueExpr().accept(this, arg), dcSelection.getDataConstructor(), dcSelection.getFieldIndex(), (Expression.ErrorInfo)dcSelection.getErrorInfo().accept(this, arg)); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitErrorInfo(org.openquark.cal.compiler.Expression.ErrorInfo, java.lang.Object) */ public Expression visitErrorInfo( Expression.ErrorInfo errorInfo, T arg){ return new Expression.ErrorInfo(errorInfo.getTopLevelFunctionName(), errorInfo.getLine(), errorInfo.getColumn()); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitLetDefn(org.openquark.cal.compiler.Expression.LetDefn, java.lang.Object) */ public Expression.Let.LetDefn visitLetDefn( Expression.Let.LetDefn letDefn, T arg) { return new Expression.Let.LetDefn( letDefn.getVar(), (Expression)letDefn.getExpr().accept(this, arg), letDefn.getVarType()); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitLetNonRec(org.openquark.cal.compiler.Expression.LetNonRec, java.lang.Object) */ public Expression visitLetNonRec( Expression.LetNonRec letNonRec, T arg) { return new Expression.LetNonRec( (Expression.Let.LetDefn)letNonRec.getDefn().accept(this, arg), (Expression)letNonRec.getBody().accept(this, arg)); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitLetRec(org.openquark.cal.compiler.Expression.LetRec, java.lang.Object) */ public Expression visitLetRec( Expression.LetRec letRec, T arg) { Expression.Let.LetDefn defns[] = letRec.getDefns(); Expression.Let.LetDefn newDefns[] = new Expression.Let.LetDefn[defns.length]; for (int i = 0; i < defns.length; ++i) { newDefns[i] = (Expression.LetNonRec.LetDefn)defns[i].accept(this, arg); } return new Expression.LetRec( newDefns, (Expression)letRec.getBody().accept(this, arg)); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitLiteral(org.openquark.cal.compiler.Expression.Literal, java.lang.Object) */ public Expression visitLiteral( Expression.Literal literal, T arg) { return new Expression.Literal(literal.getLiteral()); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitPackCons(org.openquark.cal.compiler.Expression.PackCons, java.lang.Object) */ public Expression visitPackCons( Expression.PackCons packCons, T arg){ return new Expression.PackCons(packCons.getDataConstructor()); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitRecordCase(org.openquark.cal.compiler.Expression.RecordCase, java.lang.Object) */ public Expression visitRecordCase( Expression.RecordCase recordCase, T arg){ SortedMap<FieldName, String> newFieldBindingVarMap = new TreeMap<FieldName, String>(recordCase.getFieldBindingVarMap()); return new Expression.RecordCase( (Expression)recordCase.getConditionExpr().accept(this,arg), recordCase.getBaseRecordPatternVarName(), newFieldBindingVarMap, (Expression)recordCase.getResultExpr().accept(this, arg)); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitRecordExtension(org.openquark.cal.compiler.Expression.RecordExtension, java.lang.Object) */ public Expression visitRecordExtension( Expression.RecordExtension recordExtension, T arg){ SortedMap<FieldName, Expression> newExtensionFieldsMap = new TreeMap<FieldName, Expression>(); SortedMap<FieldName, Expression> extensionFieldsMap = recordExtension.getExtensionFieldsMap(); for (final Map.Entry<FieldName, Expression> entry : extensionFieldsMap.entrySet()) { final FieldName key = entry.getKey(); final Expression fieldExpression = entry.getValue(); Expression newFieldExpression = (Expression)fieldExpression.accept(this, arg); newExtensionFieldsMap.put(key, newFieldExpression); } Expression baseRecordExpression = recordExtension.getBaseRecordExpr(); Expression newBaseRecordExpression = (baseRecordExpression == null) ? null : (Expression)baseRecordExpression.accept(this, arg); return new Expression.RecordExtension( newBaseRecordExpression, newExtensionFieldsMap); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitRecordSelection(org.openquark.cal.compiler.Expression.RecordSelection, java.lang.Object) */ public Expression visitRecordSelection( Expression.RecordSelection recordSelection, T arg){ return new Expression.RecordSelection( (Expression)recordSelection.getRecordExpr().accept(this, arg), recordSelection.getFieldName()); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitRecordUpdate(org.openquark.cal.compiler.Expression.RecordUpdate, java.lang.Object) */ public Expression visitRecordUpdate( Expression.RecordUpdate recordUpdate, T arg) { SortedMap<FieldName, Expression> newUpdateFieldValuesMap = new TreeMap<FieldName, Expression>(); SortedMap<FieldName, Expression> updateFieldValuesMap = recordUpdate.getUpdateFieldValuesMap(); for (final Map.Entry<FieldName, Expression> entry : updateFieldValuesMap.entrySet()) { final FieldName key = entry.getKey(); final Expression valueExpression = entry.getValue(); Expression newValueExpression = (Expression)valueExpression.accept(this, arg); newUpdateFieldValuesMap.put(key, newValueExpression); } return new Expression.RecordUpdate( (Expression)recordUpdate.getBaseRecordExpr().accept(this, arg), newUpdateFieldValuesMap); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitSwitch(org.openquark.cal.compiler.Expression.Switch, java.lang.Object) */ public Expression visitSwitch( Expression.Switch switchExpr, T arg) { Expression.Switch.SwitchAlt alts[] = switchExpr.getAlts(); Expression.Switch.SwitchAlt newAlts[] = new Expression.Switch.SwitchAlt[alts.length]; for (int i = 0; i < alts.length; ++i) { newAlts[i] = (Expression.Switch.SwitchAlt)alts[i].accept(this, arg); } Expression.ErrorInfo errorInfo = switchExpr.getErrorInfo(); Expression.ErrorInfo newErrorInfo = (errorInfo == null) ? null : (Expression.ErrorInfo)errorInfo.accept(this, arg); return new Expression.Switch( (Expression)switchExpr.getSwitchExpr().accept(this, arg), newAlts, newErrorInfo); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitSwitchAlt_Matching(org.openquark.cal.compiler.Expression.Switch.SwitchAlt.Matching, java.lang.Object) */ public Expression.Switch.SwitchAlt visitSwitchAlt_Matching( Expression.Switch.SwitchAlt.Matching switchAlt, T arg) { List<Object> newAltTags = new ArrayList<Object>(switchAlt.getAltTags()); Map<FieldName, String> newFieldNameToVarNameMap = new HashMap<FieldName, String>(switchAlt.getFieldNameToVarNameMap()); return new Expression.Switch.SwitchAlt.Matching( newAltTags, newFieldNameToVarNameMap, (Expression)switchAlt.getAltExpr().accept(this, arg)); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitSwitchAlt_Positional(org.openquark.cal.compiler.Expression.Switch.SwitchAlt.Positional, java.lang.Object) */ public Expression.Switch.SwitchAlt visitSwitchAlt_Positional( Expression.Switch.SwitchAlt.Positional switchAlt, T arg) { List<Object> newAltTags = new ArrayList<Object>(switchAlt.getAltTags()); SortedMap<Integer, String> newPositionToVarNameMap = new TreeMap<Integer, String>(switchAlt.getPositionToVarNameMap()); return new Expression.Switch.SwitchAlt.Positional( newAltTags, newPositionToVarNameMap, (Expression)switchAlt.getAltExpr().accept(this, arg)); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitTailRecursiveCall(org.openquark.cal.compiler.Expression.TailRecursiveCall, java.lang.Object) */ public Expression visitTailRecursiveCall( Expression.TailRecursiveCall tailRecursiveCall, T arg) { Expression args[] = tailRecursiveCall.getArguments(); Expression newArgs[] = new Expression[args.length]; for (int i = 0; i < args.length; ++i) { newArgs[i] = (Expression)args[i].accept(this, arg); } return new Expression.TailRecursiveCall( (Expression.Var)tailRecursiveCall.getVar().accept(this, arg), newArgs); } /* (non-Javadoc) * @see org.openquark.cal.compiler.ExpressionVisitor#visitVar(org.openquark.cal.compiler.Expression.Var, java.lang.Object) */ public Expression visitVar( Expression.Var var, T arg) { FunctionalAgent fa = var.getFunctionalAgent(); QualifiedName qn = var.getName(); Expression.ErrorInfo ei = var.getErrorInfo(); if (fa != null) { return new Expression.Var(fa); } else if (ei != null) { return new Expression.Var(ei); } else { return new Expression.Var(qn); } } }