Package org.openquark.cal.compiler

Source Code of org.openquark.cal.compiler.RecordVar

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


/*
* RecordVar.java
* Created: Feb 17, 2004
* By: Bo Ilic
*/
package org.openquark.cal.compiler;

import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;

import org.openquark.cal.internal.serialization.ModuleSerializationTags;
import org.openquark.cal.internal.serialization.RecordInputStream;
import org.openquark.cal.internal.serialization.RecordOutputStream;
import org.openquark.cal.internal.serialization.RecordInputStream.RecordHeaderInfo;


/**
* Represents a variable value for a record (i.e. a finite set of Fields, along with their associated types).
* This variable can have constraints on it so that the RecordType that this RecordVar can be
* instantiated to are not allowed to include certain field names.
*
* @author Bo Ilic
*/
public class RecordVar implements PolymorphicVar {
       
    private static final int serializationSchema = 0;
   
    private static final int alreadyVisitedRecordVarSerializationSchema = 0;
   
    // Serialization flags
    private static final byte HAS_NON_NULL_INSTANCE = 0x01;
    private static final byte HAS_LACKS_FIELD_CONSTRAINTS = 0x02;
    private static final byte HAS_TYPE_CLASS_CONSTRAINTS = 0x04;
   
    /**
     * The array of possible record tags used in calls to {@link RecordInputStream#findRecord(short[])} by
     * the {@link #load} method.
     */
    private static final short[] RECORD_VAR_RECORD_TAGS = new short[] {
        ModuleSerializationTags.RECORD_VAR,
        ModuleSerializationTags.ALREADY_VISITED_RECORD_VAR
    };
   
    static final Set<FieldName> NO_LACKS_FIELDS = Collections.<FieldName>emptySet();
   
    /**
     * An instantiated RecordVar (i.e. instance != null) behaves like its instantiation.
     * There are consistency requirements for the instantiation:
     * instance.baseRecordVar.lacksFieldsSet must contain this.lacksFieldsSet
     * instance.hasFieldsMap must not contain any of the fields in this.lacksFieldsSet     
     */        
    private RecordType instance; 
   
    /**
     * The preferred textual name of this RecordVar. null if there is no preferredName.
     * When a record variable is introduced via a textual declaration, such as with a type declaration, a class instance
     * definition, or a data declaration, then we can preserve the user supplied name, and make use of it whenever
     * possible in displaying String representations of types involving this RecordVar.    
     */
    private String preferredName;   
       
    /**
     * (FieldName Set) fields that this RecordVar is constrained not have. For example, in the type
     * (r\field1, r\field2, r\field3) => {r | field1 :: Int, field2 :: Char}, then for the record variable r,
     * this set is {field1, field2, field3}.
     */
    private Set<FieldName> lacksFieldsSet;
   
    /**
     * (TypeClass SortedSet). an uninstantiated record variable can be constrained to be instantiated to a record type
     * such that the types of all the fields in the record type satisfy all the type class constraints in
     * typeClassConstraintSet.
     * Note that if a class B inherits from A and B is in the typeClassConstraintSet, then A will not be-
     * the constraint is implicit.
     * If typeClassConstraintSet is the empty set, then the record variable can be instantiated to any recordVariable,
     * subject to the constraints of the lacksFieldsSet.
     */
    private SortedSet<TypeClass> typeClassConstraintSet;        
   
    /**
     * A special RecordVar corresponding to a record variable that is constrained so that lacksFieldsSet includes
     * all fields. In our implementation:
     * {NO_FIELDS | foo :: Int, bar :: Char} is what we mean by {foo :: Int, bar :: Char}
     */
    static final RecordVar NO_FIELDS = new RecordVar();
   
    /**
     * A special constructor used for constructing the special NO_FIELDS value of RecordVar and for
     * constructing blank RecordVars during deserialization.   
     */
    private RecordVar () {
        lacksFieldsSet = null;
        typeClassConstraintSet = TypeClass.NO_CLASS_CONSTRAINTS;    
    }
   
    private RecordVar(String preferredName, Set<FieldName> lacksFieldsSet, SortedSet<TypeClass> typeClassConstraintSet) {
        if (lacksFieldsSet == null || typeClassConstraintSet == null) {
            throw new NullPointerException();
        }
       
        if (preferredName != null && !LanguageInfo.isValidTypeVariableName(preferredName)) {
            throw new IllegalArgumentException("invalid preferred name " + preferredName);
        }
       
        this.preferredName = preferredName;
        this.lacksFieldsSet = lacksFieldsSet;
        this.typeClassConstraintSet = typeClassConstraintSet;
    }
   
    /**    
     * @param preferredName the preferred textual name of this RecordVar. null if there is no preferredName.
     * @return an unconstrained polymorphic RecordVar that can be further instantiated. Its type is {r}.
     */
    static RecordVar makePolymorphicRecordVar(String preferredName) {
        return new RecordVar(preferredName, NO_LACKS_FIELDS, TypeClass.NO_CLASS_CONSTRAINTS);
    }
   
    /**
     * General factory method for creating a new RecordVar.
     *   
     * @param preferredName the preferred textual name of this RecordVar. null if there is no preferredName.
     * @param lacksFieldsSet (FieldName Set) fields that this RecordVar is constrained not have.
     * @param typeClassConstraintSet (TypeClass Set) an uninstantiated record variable can be constrained to be
     *      instantiated to a record type such that the types of all the fields in the record type satisfy all the
     *      type class constraints in typeClassConstraintSet. constraints set should initially be
     *      created with TypeClass.makeNewClassConstraintSet().
     * @param validateTypeClassConstraintSet remove redundant superclass constaints, and make the set unmodifiable. This
     *      is not necessary if reusing another TypeVar's or RecordVar's typeClassConstraintSet.
     * @return the new RecordVar. It can be instantiated if needed.
     */  
    static RecordVar makeRecordVar(String preferredName, Set<FieldName> lacksFieldsSet, SortedSet<TypeClass> typeClassConstraintSet, boolean validateTypeClassConstraintSet) {
       
        if (lacksFieldsSet.isEmpty() && typeClassConstraintSet.isEmpty()) {
            return makePolymorphicRecordVar(preferredName);
        }
         
        if (validateTypeClassConstraintSet) {       
            //remove redundant superclass constraints (so that the type class constraint set
            //is in a canonical form.
            TypeClass.removeSuperclassConstraints(typeClassConstraintSet);
       
            return new RecordVar (preferredName, Collections.unmodifiableSet(lacksFieldsSet),
                Collections.unmodifiableSortedSet(typeClassConstraintSet));
        } else {
            return new RecordVar (preferredName, Collections.unmodifiableSet(lacksFieldsSet), typeClassConstraintSet);       
        }
    }

    /**   
     * @return an efficient copy of this uninstantiated record variable.
     */
    RecordVar copyUninstantiatedRecordVar() {
        if (isNoFields()) {
            return this;
        } else {
            //since both lacksFieldsSet and typeClassConstraintSet are final and unmodifiable, can use the same instances.
            return new RecordVar(preferredName, lacksFieldsSet, typeClassConstraintSet);
        }
    }
   
    /**
     * If this RecordVar is instantiated, then adds all the uninstantiated RecordVars contained in its instance to varSet.
     * Otherwise adds itself to varSet.  Don't depend on varSet coming out in any particular order.
     * @param varSet Set
     */
    void getUninstantiatedRecordVars(Set<RecordVar> varSet) {
        if(instance != null) {
            instance.getUninstantiatedRecordVars(varSet);
            return;
        }
       
        // NO_FIELDS doesn't really count as an uninstantiated record var, because
        // it is instantiated as {} (in a sense)
        if(!isNoFields()) {
            varSet.add(this);
        }
    }
   
    /**
     * If this RecordVar is instantiated (i.e. it is not NO_FIELDS or an uninstantiated record
     * variable), then this function follows the chain of instantiations to get to a RecordVar
     * which is NO_FIELDS or uninstantiated.
     * @return follows the chain of instantiated RecordVars to reach an uninstantiated RecordVar
     *     or NO_FIELDS. The returned RecordVar may be this if this RecordVar is already uninstantiated
     *     or NO_FIELDS.
     */
    RecordVar prune() {

        if (instance != null) {               
            return instance.getPrunedRecordVar();
        }

        return this;
    }
   
    boolean isNoFields() {
        return this == NO_FIELDS;   
    }
   
    /**
     * The returned lacks field set cannot be modified.
     * @return (FieldName Set) this RecordVar cannot be instantiated to anything that contains any of the lacks fields.
     */
    Set<FieldName> getLacksFieldsSet () {
        return lacksFieldsSet;
    }
   
    RecordType getInstance() {
        return instance;    
    }
       
    void setInstance(RecordType instance) {
        if (this.instance != null || isNoFields()) {
            //can't instantiate an already instantiated RecordVar or modify the special NO_FIELDS recordVar.
            throw new IllegalStateException();   
        }
       
        this.instance = instance;
    }
   
    /**{@inheritDoc} */
    public String getPreferredName() {
        return preferredName;
    }       
       
    /**
     * @return boolean checks whether this record var has an empty type class constraint set.
     */
    boolean noClassConstraints(){
        return typeClassConstraintSet.isEmpty();
    }
   
    /**
     * Represents the type class constraints on this record variable. This affects the allowable instantiations
     * of the record variable.       
     * @return SortedSet of TypeClass ordered alphabetically by fully qualified name.
     *      Will always return a non-null value. Note that this set is unmodifiable.
     */   
    SortedSet<TypeClass> getTypeClassConstraintSet() {      
        return typeClassConstraintSet;
    }             
   
    String toString(PolymorphicVarContext polymorphicVarContext) {
       polymorphicVarContext.addPolymorphicVar(this);
       return polymorphicVarContext.getPolymorphicVarName(this);
    }
   
    @Override
    public String toString() {
        if (isNoFields()) {
            return "NoFields";           
        }
               
        return "lacks=" + String.valueOf(prune().lacksFieldsSet);
    }
   
    /**
     * Write an instance of RecordVar to a RecordOutputStream.
     * @param s
     * @param visitedTypeExpr
     * @throws IOException
     */
    void write (RecordOutputStream s, Map<TypeExpr, Short> visitedTypeExpr, Map<RecordVar, Short> visitedRecordVar) throws IOException {
        Short key = visitedRecordVar.get(this);
       
        if(key != null) {
            s.startRecord(ModuleSerializationTags.ALREADY_VISITED_RECORD_VAR, alreadyVisitedRecordVarSerializationSchema);
            s.writeShortCompressed(key.shortValue());
            s.endRecord();

        } else {
            key = new Short((short)visitedRecordVar.size());
            visitedRecordVar.put(this, key);
           
            writeActual(s, visitedTypeExpr, visitedRecordVar);
        }
    }
       
    void writeActual(RecordOutputStream s, Map<TypeExpr, Short> visitedTypeExpr, Map<RecordVar, Short> visitedRecordVar) throws IOException {
        s.startRecord(ModuleSerializationTags.RECORD_VAR, serializationSchema);
        byte flags = 0;
        if (instance != null) {
            flags |= HAS_NON_NULL_INSTANCE;
        }
        if (lacksFieldsSet != null) {
            flags |= HAS_LACKS_FIELD_CONSTRAINTS;
        }
        if (!typeClassConstraintSet.isEmpty()) {
            flags |= HAS_TYPE_CLASS_CONSTRAINTS;
        }
        s.writeByte(flags);
       
        if (instance != null) {
            instance.write(s, visitedTypeExpr, visitedRecordVar);
        }
       
        if (lacksFieldsSet != null) {
            s.writeShortCompressed(lacksFieldsSet.size());           
            for (final FieldName fn : lacksFieldsSet) {
                FieldNameIO.writeFieldName(fn, s);
            }
        }
       
        if (!typeClassConstraintSet.isEmpty()) {
            s.writeShortCompressed(typeClassConstraintSet.size());       
            for (final TypeClass tc : typeClassConstraintSet) {
                s.writeQualifiedName(tc.getName());
            }
        }
       
        s.writeUTF(preferredName);
       
        s.endRecord();
    }

    /**
     * Load an instance of RecordVar from a RecordInputStream.
     * @param s
     * @param mti
     * @param visitedTypeExpr
     * @param msgLogger the logger to which to log deserialization messages.
     * @return an instance of RecordVar.
     */
    static RecordVar load (RecordInputStream s, ModuleTypeInfo mti, Map<Short, TypeExpr> visitedTypeExpr, Map<Short, RecordVar> visitedRecordVar, CompilerMessageLogger msgLogger) throws IOException {
        RecordHeaderInfo rhi = s.findRecord(RECORD_VAR_RECORD_TAGS);
        if (rhi == null) {
            throw new IOException("Unable to find RecordVar record header.");
        }
       
        switch(rhi.getRecordTag()) {
           
            case ModuleSerializationTags.ALREADY_VISITED_RECORD_VAR:
            {
                DeserializationHelper.checkSerializationSchema(rhi.getSchema(), alreadyVisitedRecordVarSerializationSchema, mti.getModuleName(), "RecordVar", msgLogger);

                // Read the key and do the lookup.
                short id = s.readShortCompressed();
                RecordVar recordVar = visitedRecordVar.get(new Short(id));
                if (recordVar == null) {
                    throw new IOException ("Unable to resolve previously encountered RecordVar instance in RecordVar.");
                }
                s.skipRestOfRecord();
                return recordVar;
            }
       
            case ModuleSerializationTags.RECORD_VAR:
            {
                DeserializationHelper.checkSerializationSchema(rhi.getSchema(), serializationSchema, mti.getModuleName(), "RecordVar", msgLogger);
       
                byte flags = s.readByte();
               
                // Short-circuit for the special NO_FIELDS case
                if((flags & (HAS_LACKS_FIELD_CONSTRAINTS | HAS_TYPE_CLASS_CONSTRAINTS)) == 0) {
                    s.skipRestOfRecord();
                    visitedRecordVar.put(new Short((short)visitedRecordVar.size()), NO_FIELDS);
                    return NO_FIELDS;
                }
               
                RecordVar rv = new RecordVar();
                visitedRecordVar.put(new Short((short)visitedRecordVar.size()), rv);
               
                RecordType instance = null;
                if ((flags & HAS_NON_NULL_INSTANCE) > 0) {
                    instance = (RecordType)TypeExpr.load(s, mti, visitedTypeExpr, visitedRecordVar, msgLogger);
                }
               
                Set<FieldName> lacksFieldSet = null;
                if ((flags & HAS_LACKS_FIELD_CONSTRAINTS) > 0) {
                    int nLacksFields = s.readShortCompressed();
                    if (nLacksFields > 0) {
                        lacksFieldSet = new HashSet<FieldName>();
                        for (int i = 0; i < nLacksFields; ++i) {
                            FieldName fn = FieldNameIO.load(s, mti.getModuleName(), msgLogger);
                            lacksFieldSet.add(fn);
                        }
                    } else {
                        lacksFieldSet = NO_LACKS_FIELDS;
                    }
                }
               
                SortedSet<TypeClass> typeClassConstraintSet = TypeClass.NO_CLASS_CONSTRAINTS;
                if((flags & HAS_TYPE_CLASS_CONSTRAINTS) > 0) {
                    int nTC = s.readShortCompressed();
                    if (nTC > 0) {
                        typeClassConstraintSet = TypeClass.makeNewClassConstraintSet();
                        for (int i = 0; i < nTC; ++i) {
                            QualifiedName qn = s.readQualifiedName();
                            TypeClass tc = mti.getReachableTypeClass(qn);
                            // This could be a type from a non-direct dependee module.
                            if (tc == null) {
                                IOException ioe = new IOException("Unable to resolve TypeClass " + qn + " while loading RecordVar.");
                                throw ioe;
                            }
                            typeClassConstraintSet.add(tc);
                        }
                    }
                }
               
                rv.lacksFieldsSet = lacksFieldSet;
                rv.typeClassConstraintSet = typeClassConstraintSet;
               
                if (instance != null) {
                    rv.setInstance(instance);
                }
               
                rv.preferredName = s.readUTF();                                                            
               
                s.skipRestOfRecord();
               
                return rv;
            }
           
            default:
            {
                throw new IOException("Unexpected record tag " + rhi.getRecordTag() + " found when looking for RecordVar.");
            }
        }
       
    }
}
TOP

Related Classes of org.openquark.cal.compiler.RecordVar

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.