/* * 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. */ /* * NTupleValueNode.java * Created: June 15, 2001 * By: Michael Cheng */ package org.openquark.cal.valuenode; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import org.openquark.cal.compiler.DataConstructor; import org.openquark.cal.compiler.FieldName; import org.openquark.cal.compiler.RecordType; import org.openquark.cal.compiler.SourceModel; import org.openquark.cal.compiler.TypeExpr; import org.openquark.cal.compiler.io.InputPolicy; import org.openquark.cal.compiler.io.OutputPolicy; import org.openquark.util.UnsafeCast; /** * A specialized ValueNode used to handle tuple record values. * Creation date: (15/06/01 11:44:36 AM) * @author Michael Cheng */ public class NTupleValueNode extends AbstractRecordValueNode { /** * A custom ValueNodeProvider for the NTupleValueNode. * @author Frank Worsley */ public static class NTupleValueNodeProvider extends ValueNodeProvider<NTupleValueNode> { public NTupleValueNodeProvider(ValueNodeBuilderHelper builderHelper) { super(builderHelper); } /** * @see org.openquark.cal.valuenode.ValueNodeProvider#getValueNodeClass() */ @Override public Class<NTupleValueNode> getValueNodeClass() { return NTupleValueNode.class; } /** * {@inheritDoc} */ @Override public NTupleValueNode getNodeInstance(Object value, DataConstructor dataConstructor, TypeExpr typeExpr) { // Check for handleability. if (!typeExpr.isTupleType()) { return null; } if (value == null) { List<ValueNode> tupleValue = new ArrayList<ValueNode>(); RecordType recordType = typeExpr.rootRecordType(); Map<FieldName, TypeExpr> hasFieldsMap = recordType.getHasFieldsMap(); for (final TypeExpr fieldType : hasFieldsMap.values()) { ValueNode newVN = getValueNodeBuilderHelper().getValueNodeForTypeExpr(fieldType); // Return null if a child value node couldn't be built. if (newVN == null) { return null; } tupleValue.add(newVN); } value = tupleValue; } // value is a List of ValueNode List<ValueNode> listValue = UnsafeCast.asTypeOf(value, Collections.<ValueNode>emptyList()); return new NTupleValueNode(listValue, typeExpr, getValueNodeBuilderHelper()); } } /** The list that holds on to the value nodes for the tuple pieces. */ private List<ValueNode> tupleValue; /** The number of tuple pieces, ie: the size of the tuple. */ private final int numPieces; /** * List that holds onto the Java marshalled values in the tuple. The 0th element of the list is the 1st component of the tuple, * the 1st element of the list holds onto the second component of the tuple, and so on. */ private List<?> outputTupleValue; private final ValueNode[] tupleElementValueNodes; /** * NTupleValueNode constructor. * @param tupleValue the list of value nodes for the tuple parameters. Size of list must match size of tuple. * @param typeExprParam the type expression for the value node * @param builderHelper */ private NTupleValueNode(List<ValueNode> tupleValue, TypeExpr typeExprParam, ValueNodeBuilderHelper builderHelper) { super(typeExprParam); if (!typeExprParam.isTupleType()) { throw new IllegalArgumentException("given type expression is not a tuple type: " + typeExprParam); } if (tupleValue == null) { throw new NullPointerException(); } RecordType recordType = typeExprParam.rootRecordType(); Map<FieldName, TypeExpr> hasFieldsMap = recordType.getHasFieldsMap(); // Set the number of pieces in this tuple so that we know when we're completed this.numPieces = hasFieldsMap.size(); if (tupleValue.size() != numPieces) { throw new IllegalArgumentException("invalid number of values in list: " + tupleValue.size() + " -- tuple size: " + numPieces); } this.tupleValue = new ArrayList<ValueNode>(tupleValue); tupleElementValueNodes = new ValueNode[numPieces]; int i = 0; for (final TypeExpr fieldTypeExpr : hasFieldsMap.values()) { tupleElementValueNodes[i] = builderHelper.getValueNodeForTypeExpr(fieldTypeExpr); ++i; } } /** * NTupleValueNode constructor. * @param tupleValue the list of value nodes for the tuple parameters. Size of list must match size of tuple. * @param outputTupleValue List that holds onto the Java marshalled values in the tuple. The 0th element of the list is the 1st component of the tuple * @param typeExprParam the type expression for the value node * @param tupleElementValueNodes */ private NTupleValueNode(List<ValueNode> tupleValue, List<?> outputTupleValue, TypeExpr typeExprParam, ValueNode[] tupleElementValueNodes) { super(typeExprParam); int tupleDimension = typeExprParam.getTupleDimension(); if (tupleDimension == -1) { throw new IllegalArgumentException("given type expression is not a tuple type: " + typeExprParam); } if (tupleValue == null && outputTupleValue == null) { throw new NullPointerException(); } // Set the number of pieces in this tuple so that we know when we're completed this.numPieces = tupleDimension; if ((tupleValue != null && tupleValue.size() != numPieces) && (outputTupleValue != null && outputTupleValue.size() != numPieces)) { throw new IllegalArgumentException("invalid number of values in list: " + tupleValue.size() + " -- tuple size: " + numPieces); } if (tupleValue != null) { this.tupleValue = new ArrayList<ValueNode>(tupleValue); } this.outputTupleValue = outputTupleValue; this.tupleElementValueNodes = tupleElementValueNodes; } /** * Makes a copy of this ValueNode, but with another TypeExpr instance (of the same type). * This is a deep copy, with respect to value nodes and the associated type expression. * Note: if the new TypeExpr is a different type from the present TypeExpr, an error is thrown. * Creation date: (06/07/01 8:49:28 AM) * @param newTypeExpr the new type of the copied node. * @return ValueNode */ @Override public NTupleValueNode copyValueNode(TypeExpr newTypeExpr) { checkCopyType(newTypeExpr); List<ValueNode> tupleCopy = null; if (tupleValue != null) { Map<FieldName, TypeExpr> hasFieldsMap = newTypeExpr.rootRecordType().getHasFieldsMap(); int tupleSize = getTupleSize(); tupleCopy = new ArrayList<ValueNode>(tupleSize); int i = 0; for (final TypeExpr componentTypeExpr : hasFieldsMap.values()) { ValueNode elementVN = tupleValue.get(i); tupleCopy.add(elementVN.copyValueNode(componentTypeExpr)); ++i; } } NTupleValueNode ntv = new NTupleValueNode(tupleCopy, outputTupleValue, newTypeExpr, tupleElementValueNodes); return ntv; } /** * Returns the source model representation of the expression represented by * this ValueNode. * * @return SourceModel.Expr */ @Override public SourceModel.Expr getCALSourceModel() { int tupleDim = tupleValue.size(); SourceModel.Expr[] components = new SourceModel.Expr[tupleDim]; for (int i = 0; i < tupleDim; i++) { components[i] = tupleValue.get(i).getCALSourceModel(); } return SourceModel.Expr.Tuple.make(components); } /** * {@inheritDoc} */ @Override public boolean containsParametricValue() { if (tupleValue != null) { for (int i = 0, tupleDim = tupleValue.size(); i < tupleDim; i++) { ValueNode elementVN = tupleValue.get(i); if (elementVN.containsParametricValue()) { return true; } } } return false; } @Override public List<ValueNode> getValue() { return Collections.unmodifiableList(tupleValue); } /** * Returns the display text representation of the expression * represented by this ValueNode. * Creation date: (03/07/01 11:03:28 AM) * @return String */ @Override public String getTextValue() { if (outputTupleValue != null) { return outputTupleValue.toString(); } StringBuilder sbTuple = new StringBuilder("("); for (int i = 0, tupleDim = tupleValue.size(); i < tupleDim; i++) { if (i > 0) { sbTuple.append(", "); } ValueNode elementVN = tupleValue.get(i); sbTuple.append(elementVN.getTextValue()); } sbTuple.append(")"); return sbTuple.toString(); } /** * @return int the size of the tuple. eg. for a 4-tuple, returns 4. */ public int getTupleSize() { return numPieces; } /** * @param index the index of the value within the tuple. * @return the value node for the ith item in the tuple. */ @Override public ValueNode getValueAt(int index) { return tupleValue.get(index); } /** * @param i the index for the value within the tuple. * @param valueNode the new value node for the ith item in the tuple. */ @Override public void setValueNodeAt(int i, ValueNode valueNode) { tupleValue.set(i, valueNode); } /** * @see ValueNode#transmuteValueNode(ValueNodeBuilderHelper, ValueNodeTransformer, TypeExpr) */ @Override public ValueNode transmuteValueNode(ValueNodeBuilderHelper valueNodeBuilderHelper, ValueNodeTransformer valueNodeTransformer, TypeExpr newTypeExpr) { Class<? extends ValueNode> handlerClass = valueNodeBuilderHelper.getValueNodeClass(newTypeExpr); // If we don't need to change type expressions... Then we just basically copy the value if (getClass().equals(handlerClass)) { int currentTupleSize = getTupleSize(); RecordType newRecordType = newTypeExpr.rootRecordType(); Map<FieldName, TypeExpr> newRecordHasFieldsMap = newRecordType.getHasFieldsMap(); List<ValueNode> newComponentList = new ArrayList<ValueNode>(); int i = 0; for (final TypeExpr componentTypeExpr : newRecordHasFieldsMap.values()) { // Copy as many current args as we have. Provide default value nodes for the others. if (i < currentTupleSize) { ValueNode componentVN = getValueAt(i); newComponentList.add(componentVN.transmuteValueNode(valueNodeBuilderHelper, valueNodeTransformer, componentTypeExpr)); } else { newComponentList.add(valueNodeBuilderHelper.getValueNodeForTypeExpr(componentTypeExpr)); } ++i; } return valueNodeBuilderHelper.buildValueNode(newComponentList, null, newTypeExpr); } else { return valueNodeTransformer.transform(valueNodeBuilderHelper, this, newTypeExpr); } } /** * Return an array of objects which are the values needed by the marshaller * described by 'getInputPolicy()'. * @return - an array of Java objects corresponding to the value represented by a value node instance. */ @Override public Object[] getInputJavaValues() { // We simply gather up all the arguments required for the components in order. // These are the arguments expected by the custom input policy build by getInputPolicy() List<Object> argumentValues = new ArrayList<Object>(); for (final ValueNode vn : tupleValue) { Object[] vals = vn.getInputJavaValues(); if (vals != null) { argumentValues.addAll(Arrays.asList(vals)); } } return argumentValues.toArray(); } /** * Return an input policy which describes how to marshall a value represented * by a value node from Java to CAL. * @return - the input policy associated with ValueNode instance. */ @Override public InputPolicy getInputPolicy() { // Instead of using Prelude.input as the input policy, we create a custom one that // simply applies the tuple constructor on the components, of the form: // (\arg_0 ... arg_N -> ( (<input policy for component 0> <args>) , ... , (<input policy for component k> <args>) )) int tupleDim = tupleValue.size(); SourceModel.Expr[] components = new SourceModel.Expr[tupleDim]; int argCount = 0; List<SourceModel.Parameter> paramsForMarshaler = new ArrayList<SourceModel.Parameter>(); for (int i = 0; i < tupleDim; i++) { InputPolicy componentInputPolicy = tupleValue.get(i).getInputPolicy(); int nComponentInputPolicyArgs = componentInputPolicy.getNArguments(); SourceModel.Expr[] componentInputPolicyArgs = new SourceModel.Expr[nComponentInputPolicyArgs + 1]; componentInputPolicyArgs[0] = componentInputPolicy.getMarshaler(); //this loop is from 1 as the first element is always the input policy itself for (int j = 1; j <= nComponentInputPolicyArgs; j++) { String componentArg = "arg_" + (argCount++); paramsForMarshaler.add(SourceModel.Parameter.make(componentArg, false)); componentInputPolicyArgs[j] = SourceModel.Expr.Var.makeUnqualified(componentArg); } if (componentInputPolicyArgs.length >= 2) { components[i] = SourceModel.Expr.Application.make(componentInputPolicyArgs); } else { components[i] = componentInputPolicyArgs[0]; } } SourceModel.Expr marshaler; if (paramsForMarshaler.isEmpty()) { marshaler = SourceModel.Expr.Tuple.make(components); } else { marshaler = SourceModel.Expr.Lambda.make( paramsForMarshaler.toArray(new SourceModel.Parameter[paramsForMarshaler.size()]), SourceModel.Expr.Tuple.make(components)); } return InputPolicy.makeWithTypeAndMarshaler(getNonParametricType().toSourceModel().getTypeExprDefn(), marshaler, paramsForMarshaler.size()); } /** * Return an output policy which describes how to marshall a value represented * by a value node from CAL to Java. * @return - the output policy associated with the ValueNode instance. */ @Override public OutputPolicy getOutputPolicy() { return OutputPolicy.DEFAULT_OUTPUT_POLICY; } /** * Set a value which is the result of the marshaller described by * 'getOutputPolicy()'. * @param value - the java value */ @Override public void setOutputJavaValue(Object value) { if (!(value instanceof List)) { throw new IllegalArgumentException("Error in NTupleValueNode.setOutputJavaValue: output must be an instance of Tuple, not an instance of: " + value.getClass().getName()); } outputTupleValue = (List<?>)value; tupleValue = new ArrayList<ValueNode>(); for (int i = 0; i < numPieces; ++i) { ValueNode vn = tupleElementValueNodes[i].copyValueNode(); vn.setOutputJavaValue(outputTupleValue.get(i)); tupleValue.add(vn); } outputTupleValue = null; } /** * {@inheritDoc} */ @Override public FieldName getFieldName(int i) { return getFieldNames().get(i); } /** * {@inheritDoc} */ @Override public List<FieldName> getFieldNames() { return ((RecordType)getTypeExpr()).getHasFieldNames(); } /** * {@inheritDoc} */ @Override public TypeExpr getFieldTypeExpr(FieldName fieldName) { return ((RecordType)getTypeExpr()).getHasFieldsMap().get(fieldName); } /** * {@inheritDoc} */ @Override public int getNFieldNames() { return numPieces; } }