/* * 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. */ /* * AbstractRecordValueNode.java * Creation date: Oct 4, 2006. * By: Edward Lam */ package org.openquark.cal.valuenode; import java.util.HashMap; import java.util.List; import java.util.Map; import org.openquark.cal.compiler.FieldName; import org.openquark.cal.compiler.RecordType; import org.openquark.cal.compiler.TypeExpr; /** * Abstract AlgebraicValueNode for handling values whose type expr is a RecordType. * * @author Edward Lam */ public abstract class AbstractRecordValueNode extends AlgebraicValueNode { /** * Constructor for an AbstractRecordValueNode. * @param typeExpr the TypeExpr of the data value which this ValueNode represents. */ public AbstractRecordValueNode(TypeExpr typeExpr) { super(typeExpr); } /** * Retrieve the value node representing the nth field in the record. * Note: Fields are indexed alphabetically * * @param n number of field * @return ValueNode */ public abstract ValueNode getValueAt(int n); /** * @return the number of fields that the record is asserted to have. */ public abstract int getNFieldNames(); /** * Get the name of the ith field in the record * Note: Fields are indexed by the ordering on the FieldName class. * * @param i index of field to retrieve * @return FieldName */ public abstract FieldName getFieldName(int i); /** * @return of record field names */ public abstract List<FieldName> getFieldNames(); /** * Get the type expression of the ith field * @param fieldName * @return TypeExpr */ public abstract TypeExpr getFieldTypeExpr(FieldName fieldName); /** * Set the value node of the ith field. * @param i the index for the field within the record. * @param valueNode the new value node for the ith field in the record. */ public abstract void setValueNodeAt(int i, ValueNode valueNode); /** * Renames a field of this value node. * * This function transmutes the record value node to a new type which contains the new field but * not the old. The value of this field is preserved in the process. * * Ex: Rename field "age" to "height" in record {age=1.0, code="Ras"} produces record {code="Ras", height=1.0} * * @param fieldName name of field to rename * @param newFieldName new name of field * @param builderHelper * @param transformer * @return RecordValueNode with the field renamed */ public AbstractRecordValueNode renameField(FieldName fieldName, FieldName newFieldName, ValueNodeBuilderHelper builderHelper, ValueNodeTransformer transformer) { // Create the new record type. RecordType newRecordType = getRecordTypeForRenamedField(getTypeExpr(), fieldName, newFieldName); // Create new record value node by transmuting ourselves to the new record type AbstractRecordValueNode newRecordNode = (RecordValueNode)this.transmuteValueNode(builderHelper, transformer, newRecordType); // Now, the new record value node contains a brand new node for the new field. // We will now place the old field value in this node. ValueNode oldFieldNode = getValueAt(getFieldNames().indexOf(fieldName)); newRecordNode.setValueNodeAt(newRecordNode.getFieldNames().indexOf(newFieldName), oldFieldNode.copyValueNode()); return newRecordNode; } /** * this returns the type of the record as a non parametric type. * It is used for the value node input policies, as there is no reason to input a parametric input * @return non parametric record type */ protected final TypeExpr getNonParametricType() { return getNonParametricType( getTypeExpr()); } /** * converts all records types - including nested records to non parametric version * @param typeExpr the type to convert * @return non parametric version of the type */ private TypeExpr getNonParametricType(TypeExpr typeExpr) { if (typeExpr instanceof RecordType) { Map<FieldName, TypeExpr> fieldsMap = ((RecordType)typeExpr).getHasFieldsMap(); Map<FieldName, TypeExpr> newFieldsMap = new HashMap<FieldName, TypeExpr>(); for(final Map.Entry<FieldName, TypeExpr> entry : fieldsMap.entrySet()) { newFieldsMap.put(entry.getKey(), getNonParametricType(entry.getValue())); } return TypeExpr.makeNonPolymorphicRecordType(newFieldsMap); } else { return typeExpr; } } /** * Create a new record type from the current type, * but having extra field newFieldName, and missing the field oldFieldName. * @param typeExpr the record type on which the new type will be based. * @param oldFieldName the field name to be replaced. * @param newFieldName the field name with which to replace the old field name. * @return the new record type. */ public static RecordType getRecordTypeForRenamedField(TypeExpr typeExpr, FieldName oldFieldName, FieldName newFieldName) { Map<FieldName, TypeExpr> fieldNamesToTypeMap = new HashMap<FieldName, TypeExpr>(); List<FieldName> existingFields = typeExpr.rootRecordType().getHasFieldNames(); for (final FieldName existingFieldName : existingFields) { TypeExpr existingFieldType = typeExpr.rootRecordType().getHasFieldType(existingFieldName); fieldNamesToTypeMap.put(existingFieldName, existingFieldType); } TypeExpr fieldTypeExpr = fieldNamesToTypeMap.remove(oldFieldName); fieldNamesToTypeMap.put(newFieldName, fieldTypeExpr); return TypeExpr.makeNonPolymorphicRecordType(fieldNamesToTypeMap); } }