Package org.openquark.cal.valuenode

Source Code of org.openquark.cal.valuenode.DataConstructorValueNode$DataConstructorValueNodeProvider

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


/*
* DataConstructorValueNode.java
* Created: June 4, 2000
* By: Michael Cheng
*/

package org.openquark.cal.valuenode;

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

import org.openquark.cal.compiler.DataConstructor;
import org.openquark.cal.compiler.QualifiedName;
import org.openquark.cal.compiler.SourceModel;
import org.openquark.cal.compiler.TypeConsApp;
import org.openquark.cal.compiler.TypeExpr;
import org.openquark.cal.compiler.io.InputPolicy;
import org.openquark.cal.compiler.io.OutputPolicy;
import org.openquark.cal.foreignsupport.module.Prelude.AlgebraicValue;
import org.openquark.cal.foreignsupport.module.Prelude.EitherValue;
import org.openquark.cal.foreignsupport.module.Prelude.MaybeValue;
import org.openquark.cal.foreignsupport.module.Prelude.OrderingValue;
import org.openquark.cal.foreignsupport.module.Prelude.UnitValue;
import org.openquark.cal.module.Cal.Core.CAL_Prelude;
import org.openquark.cal.services.MetaModule;
import org.openquark.cal.services.Perspective;
import org.openquark.util.UnsafeCast;


/**
* DataConstructorValueNode is a specialized ValueNode used to represent DataConstructor values. 
* It holds a DataConstructor object, and a list containing ValueNodes which represent the
* component values of the DataConstructor object.
*
* DataConstructorValueNode is special among the derived classes of AlgebraicValueNode. This is
* because it can be used to represent any algebraic CAL value. However, the representation of the
* value may not be as efficient or easy-to-work-with for Java clients for the cases in which a
* special purpose AlgebraicValueNode, optimized for values of a particular type, is available.
*
* @author Michael Cheng
*/
public class DataConstructorValueNode extends AlgebraicValueNode {

    /**
     * A custom ValueNodeProvider for the DataConstructorValueNode.
     * @author Frank Worsley
     */
    public static class DataConstructorValueNodeProvider extends ValueNodeProvider<DataConstructorValueNode> {
       
        /** The working module for which the typeExprToNodeMap has been calculated. */
        private MetaModule workingModule = null;
       
        /**
         * Map from typeExpr.toString() -> value node provided for that type.
         * Contains type expr for which value nodes have already been determined, or null if
         * 1) we are currently trying to build a value node for that type.
         * 2) a value node for that type cannot be built.
         **/
        private final Map<String, DataConstructorValueNode> typeExprToNodeMap = new HashMap<String, DataConstructorValueNode>();
       
        public DataConstructorValueNodeProvider(ValueNodeBuilderHelper builderHelper) {
            super(builderHelper);
        }

        /**
         * @see org.openquark.cal.valuenode.ValueNodeProvider#getValueNodeClass()
         */
        @Override
        public Class<DataConstructorValueNode> getValueNodeClass() {
            return DataConstructorValueNode.class;
        }
       
        /**
         * @see org.openquark.cal.valuenode.ValueNodeProvider#isSpecialLiteralizedValueNode()
         */
        @Override
        public boolean isSpecialLiteralizedValueNode() {
            return false;
        }
       
        /**
         * {@inheritDoc}
         *
         * This method builds a new DataConstructorValueNode for a type expression.
         * If no data constructor is provided, it smartly picks a default data constructor to use,
         * so that there is no endless chain of recursive data constructors.
         * If no non-recursive data constructor can be found this method returns null.
         *
         * This method also returns null if typeExpr is present in the typeExprToNodeMap but has a null
         * value node mapped to it. This case would occur if we are trying to find a data constructor and encounter
         * one that requires an argument for which we have not yet found a constructor itself. This would indicate
         * a cyclic constructor and therefore this method returns null.
         *
         * @param value the value for the value node
         * @param dataConstructor the data constructor for the value node
         * @param typeExpr the type expression for the value node
         * @return a new ValueNode or null if the type constructor has no non-recursive data constructors.
         */
        @Override
        public DataConstructorValueNode getNodeInstance(Object value, DataConstructor dataConstructor, TypeExpr typeExpr) {
           
            // Check for handleability.
            TypeConsApp typeConsApp = typeExpr.rootTypeConsApp();
            if (typeConsApp == null || typeConsApp.isFunctionType()) {
                return null;
            }
           
            // Check visibility of the type.. (should we do this?)
            Perspective perspective = getValueNodeBuilderHelper().getPerspective();
            if (perspective.getTypeConstructor(typeConsApp.getName()) == null) {
                return null;
            }
           
            if (dataConstructor == null) {
                // No data constructor provided - we have to pick one, and create a default value.
               
                // If the perspective's working module has changed, the typeExprToNodeMap is no longer valid.
                if (perspective.getWorkingModule() != workingModule) {
                    workingModule = perspective.getWorkingModule();
                    typeExprToNodeMap.clear();
                }
               
                // TODOEL: ~HACK - we use the string-ified value as a key because we want it equals() in the map.
                String typeExprString = typeExpr.toString();
           
                // First, check if we've previously created a value node for this expression.
                if (typeExprToNodeMap.containsKey(typeExprString)) {

                    // We've already found a value node for this type, so just return it.
                    // If we're currently in the process of finding a value node for it, then this
                    // will end up returning null. That's ok since it indicates to the caller that
                    // there is a cycle.
                    DataConstructorValueNode valueNode = typeExprToNodeMap.get(typeExprString);
                    return valueNode != null ? valueNode.copyValueNode(typeExpr) : null;
               
                } else {
                    // Add a signal that we are building a value node for this type.
                    typeExprToNodeMap.put(typeExprString, null);
                }
               
                // Iterate over the visible data constructors.
                DataConstructor[] dataConsArray = perspective.getDataConstructorsForType(typeConsApp.getName());
               
                if (dataConsArray != null) {

                    for (final DataConstructor element : dataConsArray) {

                        // See if a value node can be built for this data constructor (call back with non-null data constructor).
                        DataConstructorValueNode dataConsValueNode = getNodeInstance(null, element, typeExpr);
                       
                        if (dataConsValueNode != null) {
                            typeExprToNodeMap.put(typeExprString, dataConsValueNode);
                            return dataConsValueNode;
                        }
                    }
                }
           
                return null;

            } else {
               
                if (value == null) {

                    // Try and get default child nodes for the value.
                    // If there are no valid visible data constructors value will still be null.  This should only happen
                    // when a data constructor value is output and the child nodes are then initialized during unwinding.
                   
                    int arity = dataConstructor.getArity();
                   
                    if (arity == 0) {
                        // No arguments, so we don't need any child value nodes.
                        value = Collections.EMPTY_LIST;
                       
                    } else {
                        // Construct child value nodes for the arguments.
                        ValueNode[] childValueNodes = new ValueNode[arity];
                   
                        for (int i = 0 ; i < arity; i++) {
                       
                            TypeExpr argType = TypeExpr.getComponentTypeExpr(typeExpr, i, dataConstructor);
   
                            childValueNodes[i] = getValueNodeBuilderHelper().getValueNodeForTypeExpr(argType)
                       
                            if (childValueNodes[i] == null) {
                           
                                // Looks like this argument type has already been encountered and we have
                                // not been able to find a value node for it. That means this data constructor is
                                // cyclic and cannot be used.
                                return null;
                            }
                        }
                   
                        value = Arrays.asList(childValueNodes);
                    }
                }
               
                List<ValueNode> listValue = UnsafeCast.asTypeOf(value, Collections.<ValueNode>emptyList());
                return new DataConstructorValueNode(listValue, dataConstructor, typeExpr);
            }
        }
    }
   
   
    /** The data constructor this value node is for. */
    private final DataConstructor dataConstructor;

    /**
     * The List containing the ValueNodes representing the value components of this DataConstructorValueNode.
     * There is a value component for each argument of the data constructor.
     */
    private final List<ValueNode> childrenList;
   
    private Object outputValue;
   
    /**
     * Constructs a new DataConstructorValueNode representing a DataConstructor.
     * @param childrenList the list of child value nodes for the given data constructor
     * @param dataConstructorValue the data constructor this value node represents
     * @param typeExprParam the TypeExpr of the data value represented by this DataConstructorValueNode.
     */
    public DataConstructorValueNode(List<ValueNode> childrenList, DataConstructor dataConstructorValue, TypeExpr typeExprParam) {
   
        super(typeExprParam);
       
        if (dataConstructorValue == null) {
            throw new NullPointerException();
        }
       
        this.dataConstructor = dataConstructorValue;
        this.childrenList = childrenList != null ? new ArrayList<ValueNode>(childrenList) : new ArrayList<ValueNode>();
    }
   
    /**
     * @see org.openquark.cal.valuenode.ValueNode#getValue()
     */
    @Override
    public Object getValue() {
        return this;
    }     
   
    /**
     * 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 DataConstructorValueNode copyValueNode(TypeExpr newTypeExpr) {
   
        checkCopyType(newTypeExpr);
   
        // eg. data Foo a = Bar (Baz a) a;
        // newTypeExpr will be of type "Foo a", but data constructor will be Bar, and children will be [Baz a, a].
        // dataConstructor type will be a -> Foo a.
       
        int childrenCount = childrenList.size();
        List<ValueNode> newChildrenList = new ArrayList<ValueNode>(childrenCount);

        for (int i = 0; i < childrenCount; i++) {

            ValueNode childVN = childrenList.get(i);
            TypeExpr elementTypeExpr = TypeExpr.getComponentTypeExpr(newTypeExpr, i, dataConstructor);
            newChildrenList.add(childVN.copyValueNode(elementTypeExpr));
        }

        DataConstructorValueNode dvn =  new DataConstructorValueNode(newChildrenList, getDataConstructor(), newTypeExpr);
        dvn.setOutputJavaValue(outputValue);
        return dvn;
    }

    /**
     * Returns the source model representation of the expression represented by
     * this ValueNode.
     *
     * @return SourceModel.Expr
     */
    @Override
    public SourceModel.Expr getCALSourceModel() {

        int childrenCount = childrenList.size();
        SourceModel.Expr[] params = new SourceModel.Expr[childrenCount];

        for (int i = 0; i < childrenCount; i++) {
            params[i] = childrenList.get(i).getCALSourceModel();
        }

        return SourceModel.Expr.makeGemCall(dataConstructor.getName(), params);
    }
   
    /**
     * Returns whether valueNode contains a parametric value
     * @see ValueNode#containsParametricValue() 
     */
    @Override
    public boolean containsParametricValue() {
        if (outputValue != null) {
            return false;
        }
       
        for (int i = 0, childrenCount = childrenList.size(); i < childrenCount; i++) {
            if (childrenList.get(i).containsParametricValue()) {
                return true;
            }
        }
        return false;      
    }
   
    /**
     * Returns a list of the children ValueNodes representing the arguments values of the
     * data constructor this DataConstructorValueNode represents. Note that the returned
     * list is not modifiable.
     * @return List
     */
    public List<ValueNode> getChildrenList() {
        return Collections.unmodifiableList(childrenList);
    }

    /**
     * Sets the children of this data constructor node to the nodes in the given list.
     * @param newChildrenList the new list of children. Must be non-null and same size as current list.
     */   
    public void setChildrenList(List<ValueNode> newChildrenList) {
       
        if (newChildrenList == null) {
            throw new IllegalArgumentException("new children list cannot be null");
        }
       
        if (newChildrenList.size() != childrenList.size()) {
            throw new IllegalArgumentException("new children list cannot be different size than current list");
        }
       
        childrenList.clear();
        childrenList.addAll(newChildrenList);
    }
   
    /**
     * Returns the DataConstructor.
     * @return DataConstructor
     */
    public final DataConstructor getDataConstructor() {
        return dataConstructor;
    }
   
    /**
     * Returns the display text representation of the expression represented by this ValueNode.
     * @return String
     */
    @Override
    public String getTextValue() {
        if (outputValue != null) {
            return outputValue.toString();
        }
       
        String dcName = dataConstructor.getName().getUnqualifiedName();
        // If there is no children, then simply return the DataConstructor name (and no need for brackets either).
        if (childrenList.size() == 0) {

            return dcName;
        }

        StringBuilder sb = new StringBuilder("(");
        sb.append(dcName);

        for (int i = 0, childrenCount = childrenList.size(); i < childrenCount; i++) {

            sb.append(" ");
            sb.append(childrenList.get(i).getTextValue());
        }

        sb.append(')');

        return sb.toString();              
    }   
   
    /**
     * @see ValueNode#transmuteValueNode(ValueNodeBuilderHelper, ValueNodeTransformer, TypeExpr)
     */
    @Override
    public ValueNode transmuteValueNode(ValueNodeBuilderHelper valueNodeBuilderHelper, ValueNodeTransformer valueNodeTransformer, TypeExpr newTypeExpr) {

        QualifiedName typeConsName = getTypeExpr().rootTypeConsApp().getName();
        TypeConsApp newTypeConsApp = newTypeExpr.rootTypeConsApp();

        // If we don't need to change type constructors, then we just basically copy the value.
        if (newTypeConsApp != null && typeConsName.equals(newTypeConsApp.getName())) {

            List<ValueNode> componentList = getChildrenList();
            List<ValueNode> newComponentList = new ArrayList<ValueNode>(componentList.size());
   
            // Go thru the component ValueNodes and transmute them.
            for (int i = 0, componentCount = componentList.size(); i < componentCount; i++) {

                ValueNode componentVN = componentList.get(i);
                TypeExpr componentTypeExpr = TypeExpr.getComponentTypeExpr(newTypeExpr, i, dataConstructor);
                newComponentList.add(componentVN.transmuteValueNode(valueNodeBuilderHelper, valueNodeTransformer, componentTypeExpr));
            }

            ValueNode newValueNode = valueNodeBuilderHelper.buildValueNode(newComponentList, dataConstructor, newTypeExpr);
            return newValueNode;

        } 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 children in order.
        // These are the arguments expected by the custom input policy build by getInputPolicy()
       
        List<Object> argumentValues = new ArrayList<Object>();
        for (final ValueNode child : childrenList) {
            Object[] vals = child.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 data constructor on the children, of the form:
        // (\arg_0 ... arg_N -> M.DC (<input policy for child 0> <args>) ... (<input policy for child k> <args>))
        //
        // For example, if this value node's type is Maybe Int, and the DC is Just, the input policy will be:
        // (\arg_0 -> Prelude.Just (Prelude.input arg_0))
        //
        // If this value node's type is (Foo.Tuple2 Int Int) (with one DC Tuple2, with 2 fields #1 and #2), the input policy will be:
        // (\arg_0 arg_1 -> Foo.Tuple2 (Prelude.input arg_0) (Prelude.input arg_1))
        //
        // If this value node's type is Maybe (Maybe Unit), and the DC in both layers is just, then input policy will be:
        // (Prelude.Just (Prelude.Just Prelude.Unit))
        //
        // Note that in the last example there are no arguments.
       
       
        int childrenCount = childrenList.size();
        SourceModel.Expr[] childrenValues = new SourceModel.Expr[childrenCount];

        int argCount = 0;
        List<SourceModel.Parameter> paramsForMarshaler = new ArrayList<SourceModel.Parameter>();
       
        for (int i = 0; i < childrenCount; i++) {
           
            InputPolicy childInputPolicy = childrenList.get(i).getInputPolicy();
           
            int nChildInputPolicyArgs = childInputPolicy.getNArguments();
           
            SourceModel.Expr[] childInputPolicyParts = new SourceModel.Expr[nChildInputPolicyArgs + 1];
            childInputPolicyParts[0] = childInputPolicy.getMarshaler();
           
            //this loop is from 1 as the first element is always the input policy itself
            for (int j = 1; j <= nChildInputPolicyArgs; j++) {
                String childArg = "arg_" + (argCount++);
                paramsForMarshaler.add(SourceModel.Parameter.make(childArg, false));   
                childInputPolicyParts[j] = SourceModel.Expr.Var.makeUnqualified(childArg);
            }
           
            if (childInputPolicyParts.length >= 2) {
                childrenValues[i] = SourceModel.Expr.Application.make(childInputPolicyParts);
            } else {
                childrenValues[i] = childInputPolicyParts[0];
            }
        }

        SourceModel.Expr marshaler;
        if (paramsForMarshaler.isEmpty()) {
            marshaler = SourceModel.Expr.makeGemCall(dataConstructor.getName(), childrenValues);
           
        } else {
            marshaler = SourceModel.Expr.Lambda.make(
                paramsForMarshaler.toArray(new SourceModel.Parameter[paramsForMarshaler.size()]),
                SourceModel.Expr.makeGemCall(dataConstructor.getName(), childrenValues));
        }
       
        return InputPolicy.makeWithTypeAndMarshaler(getTypeExpr().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) {
       
        TypeExpr typeExpr = getTypeExpr();
       
        // We only restrict the value that can be set if it is one of the small number of types we know about.
        if (typeExpr.hasRootTypeConstructor(CAL_Prelude.TypeConstructors.Maybe) ||
            typeExpr.hasRootTypeConstructor(CAL_Prelude.TypeConstructors.Either) ||
            typeExpr.hasRootTypeConstructor(CAL_Prelude.TypeConstructors.Ordering) ||
            typeExpr.hasRootTypeConstructor(CAL_Prelude.TypeConstructors.Unit)) {
           
            if (value instanceof AlgebraicValue ||
                value instanceof MaybeValue ||
                value instanceof EitherValue ||
                value instanceof UnitValue ||
                value instanceof OrderingValue) {
               
                outputValue = value;
            } else {
                outputValue = null;
            }
           
        } else {
            outputValue = value;
        }
    }   
}
TOP

Related Classes of org.openquark.cal.valuenode.DataConstructorValueNode$DataConstructorValueNodeProvider

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.