Package org.openquark.cal.valuenode

Source Code of org.openquark.cal.valuenode.RecordValueNode$RecordValueNodeProvider

/*
* 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.
*/


/*
* RecordValueNode.java
* Created: July 5, 2004
* By: Iulian Radu
*/

package org.openquark.cal.valuenode;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

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;


/**
* Specialized AlgebraicValueNode for handling record values.
*
* This object holds a list of value nodes whose elements correspond to values of the record fields,
* along with a list of field names.
*
* @author Iulian Radu
*/
public class RecordValueNode extends AbstractRecordValueNode {

    /**
     * A custom ValueNodeProvider for the RecordValueNode.
     * @author Iulian Radu
     */
    public static class RecordValueNodeProvider extends ValueNodeProvider<RecordValueNode> {
       
        public RecordValueNodeProvider(ValueNodeBuilderHelper builderHelper) {
            super(builderHelper);
        }

        /**
         * @see org.openquark.cal.valuenode.ValueNodeProvider#getValueNodeClass()
         */
        @Override
        public Class<RecordValueNode> getValueNodeClass() {
            return RecordValueNode.class;
        }
       
        /**
         * @see org.openquark.cal.valuenode.ValueNodeProvider#isSpecialLiteralizedValueNode()
         */
        @Override
        public boolean isSpecialLiteralizedValueNode() {
            return false;
        }
       
        /**
         * {@inheritDoc}
         */
        @Override
        public RecordValueNode getNodeInstance(Object value, DataConstructor dataConstructor, TypeExpr typeExpr) {

            RecordType recordType = typeExpr.rootRecordType();
           
            if (recordType == null) {
                // This expression is not a record type, so this provides does not handle it
                return null;
            }
           
            List<FieldName> fieldNames = recordType.getHasFieldNames();
           
            // value is either a List of ValueNode or null
            List<ValueNode> listValue = UnsafeCast.asTypeOf(value, Collections.<ValueNode>emptyList());

            if (listValue == null) {
                // Create default value nodes for the fields
               
                int arity = fieldNames.size();
                listValue = new ArrayList<ValueNode>(arity);
                for (int i = 0 ; i < arity; i++) {
               
                    TypeExpr argType = recordType.getHasFieldType(fieldNames.get(i));
                    ValueNode valueNode = getValueNodeBuilderHelper().getValueNodeForTypeExpr(argType);
                    if (valueNode == null) {
                        // Cannot create a node represented, thus cannot create this node
                        return null;
                    }
                    listValue.add(valueNode);
                }
            }
           
            return new RecordValueNode(recordType, listValue, fieldNames);
        }
    }
   
    /** Names of the record fields, sorted via the FieldName ordering. */
    private final List<FieldName> fieldNames;
   
    /** Value nodes of the record fields. fieldNodes[i] is the node for field fieldNames[i] */
    private final List<ValueNode> fieldNodes;
   
    /**
     * Constructor
     *
     * @param recordType type expression of the record
     * @param fieldNodes list of record field values
     * @param fieldNames list of record field names corresponding to values, sorted in the FieldName ordering.
     */
    RecordValueNode(TypeExpr recordType, List<ValueNode> fieldNodes, List<FieldName> fieldNames) {
        super(recordType);
        if ((fieldNodes == null) || (fieldNames == null)) {
            throw new NullPointerException();
        }
        if (recordType.rootRecordType() == null) {
            throw new IllegalArgumentException();
        }
       
        this.fieldNodes = fieldNodes;
        this.fieldNames = fieldNames;
    }
   
    /**
     * @see org.openquark.cal.valuenode.ValueNode#getValue()
     */
    @Override
    public Object getValue() {
        return new ArrayList<ValueNode>(fieldNodes);
    }
   
    /**
     * Retrieve the value node representing the nth field in the record.
     * Note: Fields are indexed alphabetically
     * 
     * @param n number of field
     * @return ValueNode
     */
    @Override
    public ValueNode getValueAt(int n) {
        return fieldNodes.get(n);
    }
   
    /**
     * @return the number of fields that the record is asserted to have.
     */
    @Override
    public int getNFieldNames() {
        return fieldNames.size();
    }
   
    /**
     * {@inheritDoc}
     */
    @Override
    public FieldName getFieldName(int i) {
        return fieldNames.get(i);
    }
   
    /**
     * {@inheritDoc}
     */
    @Override
    public List<FieldName> getFieldNames() {
        return fieldNames;
    }
   
    /**
     * {@inheritDoc}
     */
    @Override
    public TypeExpr getFieldTypeExpr(FieldName fieldName) {
        return fieldNodes.get(fieldNames.indexOf(fieldName)).getTypeExpr();
    }
       
    /**
     * {@inheritDoc}
     */
    @Override
    public void setValueNodeAt(int i, ValueNode valueNode) {
        fieldNodes.set(i, valueNode);
    }

    /**
     * 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.
     * @param newTypeExpr the new type of the copied node.
     * @return ValueNode
     */
    @Override
    public RecordValueNode copyValueNode(TypeExpr newTypeExpr) {
   
        checkCopyType(newTypeExpr);

        // Copy field nodes

        int fieldCount = fieldNames.size();
        List<ValueNode> newFieldNodes = new ArrayList<ValueNode>(fieldCount);
        for (int i = 0; i < fieldCount; i++) {
            ValueNode fieldVN = fieldNodes.get(i);
            newFieldNodes.add(fieldVN.copyValueNode());
        }
       
        RecordValueNode newNode = new RecordValueNode(newTypeExpr, newFieldNodes, fieldNames);
        return newNode;
    }
   
    /**
     * Returns the source model representation of the expression represented by
     * this ValueNode. This creates a record constructor from the contained
     * fields.
     *
     * Ex: for a record with fields "age" and "height" with values 1.0 and 2.0,
     *     this produces "{age = 1.0, height = 2.0}"
     *
     * @return SourceModel.Expr
     */
    @Override
    public SourceModel.Expr getCALSourceModel() {

        int numFields = fieldNodes.size();
        SourceModel.Expr.Record.FieldModification[] fieldValuePairs = new SourceModel.Expr.Record.FieldModification[numFields];

        for (int i = 0; i < numFields; i++) {
            fieldValuePairs[i] =
                SourceModel.Expr.Record.FieldModification.Extension.make(
                    SourceModel.Name.Field.make(fieldNames.get(i)),
                    fieldNodes.get(i).getCALSourceModel());
        }

        return SourceModel.Expr.Record.make(null, fieldValuePairs);
    }
   
    /**
     * Returns the text representation of the expression represented by this ValueNode.
     * This produces a similar output to getCALValue, except getting text values for each field node.
     * @return String
     */
    @Override
    public String getTextValue() {

        StringBuilder calValue = new StringBuilder("{");
        for (int i = 0, n = fieldNodes.size(); i < n; i++) {
            if (i > 0) {
                calValue.append(", ");
            }
            calValue.append(fieldNames.get(i).getCalSourceForm()).append( " = ").append(fieldNodes.get(i).getTextValue());
        }
        calValue.append("}");
        return calValue.toString();
    }
   
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean containsParametricValue() {
        return getTypeExpr().isPolymorphic();
    }
   
    /**
     * 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 record constructor on the fields, of the form:
        // (\arg_0 ... arg_N -> { <field 0 name> :: (<input policy for field 0> <args>) , ... , <field k name> :: (<input policy for field k> <args>) })
       
        int nFields = fieldNodes.size();
        SourceModel.Expr.Record.FieldModification[] fieldValuePairs = new SourceModel.Expr.Record.FieldModification[nFields];

        int argCount = 0;
        List<SourceModel.Parameter> paramsForMarshaler = new ArrayList<SourceModel.Parameter>();
       
        for (int i = 0; i < nFields; i++) {
           
            InputPolicy fieldInputPolicy = fieldNodes.get(i).getInputPolicy();
           
            int nFieldInputPolicyArgs = fieldInputPolicy.getNArguments();
                      
            SourceModel.Expr[] fieldInputPolicyArgs = new SourceModel.Expr[nFieldInputPolicyArgs + 1];
            fieldInputPolicyArgs[0] = fieldInputPolicy.getMarshaler();

            //this loop is from 1 as the first element is always the input policy itself
            for (int j = 1; j <= nFieldInputPolicyArgs; j++) {
                String fieldArg = "arg_" + (argCount++);
               
                paramsForMarshaler.add(SourceModel.Parameter.make(fieldArg, false));
                fieldInputPolicyArgs[j] = SourceModel.Expr.Var.makeUnqualified(fieldArg);              
            }
           
            final SourceModel.Expr fieldValue;
            if (fieldInputPolicyArgs.length >= 2) {
                fieldValue = SourceModel.Expr.Application.make(fieldInputPolicyArgs);
            } else {
                fieldValue = fieldInputPolicyArgs[0];
            }
       

            fieldValuePairs[i] = SourceModel.Expr.Record.FieldModification.Extension.make(SourceModel.Name.Field.make(fieldNames.get(i)), fieldValue);
        }

        SourceModel.Expr marshaler;
        if (paramsForMarshaler.isEmpty()) {
            marshaler = SourceModel.Expr.Record.make(null, fieldValuePairs);
           
        } else {
            marshaler = SourceModel.Expr.Lambda.make(
                paramsForMarshaler.toArray(new SourceModel.Parameter[paramsForMarshaler.size()]),
                SourceModel.Expr.Record.make(null, fieldValuePairs));
        }
               
        return InputPolicy.makeWithTypeAndMarshaler(
            getNonParametricType().toSourceModel().getTypeExprDefn(),
            marshaler, paramsForMarshaler.size());
    }
   
    /**
     * Return an array of objects which are the values needed by the marshaler
     * 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 : fieldNodes) {
            Object[] vals = vn.getInputJavaValues();
            if (vals != null) {
                argumentValues.addAll(Arrays.asList(vals));
            }
        }
       
        return argumentValues.toArray();
       
    }
   
    /**
     * 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()'.
     *
     * The output value for a record is a java.util.List whose elements correspond to input
     * values of the record fields (ordered alphabetically).
     *
     * @param value - the java value
     */
    @Override
    public void setOutputJavaValue(Object value) {
        if (!(value instanceof List)) {
            throw new IllegalArgumentException("Error in RangeValueNode.setOutputJavaValue: output must be an instance of List, not an instance of: " + value.getClass().getName());
        }
       
        List<?> fieldNodesList = (List<?>)value;
        for (int i = 0, n = fieldNodesList.size(); i < n; i++) {
            fieldNodes.get(i).setOutputJavaValue(fieldNodesList.get(i));
        }
    }
   
    /**
     * Transmutes the record value node. This creates a new value node which results from
     * converting the current node's type expression to the specified type expression.
     *
     * If transmuting to a record type expression, this method creates value nodes for record fields
     * which are not already contained in this node, and transmutes fields common to both records
     *
     * Ex: Transmuting the record (r\name,r\width)=>{r|name:String,width::Double} where width = 11.0, name="Ana"
     *     to the new type (r\height, r\name, r\width)=>{r|height::Double, name::String, width::Long} produces a
     *     record value node having the new type specified, a new value node height, a copied node name = "Ana",
     *     and transmuted value node width = 11. 
     * 
     * @see org.openquark.cal.valuenode.ValueNode#transmuteValueNode(org.openquark.cal.valuenode.ValueNodeBuilderHelper, org.openquark.cal.valuenode.ValueNodeTransformer, org.openquark.cal.compiler.TypeExpr)
     */
    @Override
    public ValueNode transmuteValueNode(ValueNodeBuilderHelper valueNodeBuilderHelper, ValueNodeTransformer valueNodeTransformer, TypeExpr newTypeExpr) {
        Class<? extends ValueNode> handlerClass = valueNodeBuilderHelper.getValueNodeClass(newTypeExpr);

        if ((getClass().equals(handlerClass))) {
           
            RecordType recordType = newTypeExpr.rootRecordType();
            List<FieldName> newFieldNames = recordType.getHasFieldNames();
            List<ValueNode> newFieldNodes = new ArrayList<ValueNode>(newFieldNames.size());
           
            // Transmute all fields which have equal names, and create those which do not
            for (final FieldName newFieldName : newFieldNames) {
                ValueNode newFieldNode;
               
                TypeExpr newFieldType = recordType.getHasFieldType(newFieldName);
                int nameIndex = fieldNames.indexOf(newFieldName);
                if (nameIndex != -1) {
                   
                    // The record field in the new type is contained in our record
                    ValueNode oldFieldNode = this.fieldNodes.get(nameIndex);
                       
                    // Transmute field to copy/change type
                    newFieldNode = oldFieldNode.transmuteValueNode(
                        valueNodeBuilderHelper,
                        valueNodeTransformer,
                        newFieldType);
                   
                } else {
                   
                    // Field not contained in our record; ask node builder to create a new node
                    newFieldNode = valueNodeBuilderHelper.getValueNodeForTypeExpr(newFieldType);
                }
               
                newFieldNodes.add(newFieldNode);
            }
               
            // Now build the record with the new type expression and value nodes
            ValueNode vn = valueNodeBuilderHelper.buildValueNode(newFieldNodes, null, newTypeExpr);
            return vn;
           
        } else {
            return valueNodeTransformer.transform(valueNodeBuilderHelper, this, newTypeExpr);
        }
    }
   
}
TOP

Related Classes of org.openquark.cal.valuenode.RecordValueNode$RecordValueNodeProvider

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.