/*
* 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.
*/
/*
* RTRecordValue.java
* Created: Apr 5, 2004
* By: Bo Ilic
*/
package org.openquark.cal.internal.runtime.lecc;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.openquark.cal.compiler.FieldName;
import org.openquark.cal.internal.runtime.RecordType;
import org.openquark.cal.runtime.CALExecutorException;
import org.openquark.cal.runtime.CalValue;
/**
* Represents a record literal value in the runtime.
* In CAL source, this corresponds to a record such as: {field1 = "abc", field2 = 2.0}.
* <p>
*
* Records involving record-polymorphic record extension, such as:
* {r | field1 = "abc", field2 = 2.0} are not represented directly using RTRecordValue objects.
* Rather, r is first evaluated to a RTRecordValue, and then the extension is made. (In a lazy
* context, this is done through reducing RTRecordExtension, in a strict context, this is done
* directly).
* <p>
*
* Records internally are implemented by 6 different subclasses:
* -records whose ordinal fields are of tuple form i.e. #1, #2, ..., #n consecutive
* are stored as arrays with O(1) access to the ordinal fields, and no storage for ordinal field names.
* -even if a record's ordinal part is not of tuple form, the field names are stored as unboxed ints.
* -storage for records is space efficient.
*
* @author Bo Ilic
*/
public abstract class RTRecordValue extends RTValue implements RecordType {
static public final RTRecordValue EMPTY_RECORD = new EmptyRecord();
/**
* A helper class to represent the data necessary to form the ordinal part of
* a record's field set. These are returned by certain intermediate functions.
* @author Bo Ilic
*/
private static abstract class OrdinalData {
static final OrdinalData EMPTY = new Empty();
private static final class Empty extends OrdinalData {
private Empty() {
// Make constructor private to prevent multiple instances.
}
@Override
boolean isTuple (){
return false;
}
@Override
boolean hasOrdinalFields() {
return false;
}
@Override
int[] getOrdinalNames() {
throw new UnsupportedOperationException();
}
@Override
int getNOrdinalNames() {
throw new UnsupportedOperationException();
}
@Override
int getNthOrdinalName(int n) {
throw new UnsupportedOperationException();
}
@Override
RTValue[] getOrdinalValues() {
throw new UnsupportedOperationException();
}
}
private static final class Tuple extends OrdinalData {
private final RTValue[] ordinalValues;
Tuple(RTValue[] ordinalValues) {
assert RTRecordValue.verifyTupleData(ordinalValues) :
"Invalid tuple data in OrdinalData.Tuple constructor.";
this.ordinalValues = ordinalValues;
}
@Override
boolean isTuple (){
return true;
}
@Override
boolean hasOrdinalFields() {
return true;
}
@Override
int[] getOrdinalNames() {
throw new UnsupportedOperationException();
}
@Override
int getNOrdinalNames() {
return ordinalValues.length;
}
@Override
int getNthOrdinalName(int n) {
if(n < ordinalValues.length) {
return n + 1;
} else {
throw new ArrayIndexOutOfBoundsException();
}
}
@Override
RTValue[] getOrdinalValues() {
return ordinalValues;
}
}
private static final class Ordinal extends OrdinalData {
private final int[] ordinalNames;
private final RTValue[] ordinalValues;
Ordinal(int[] ordinalNames, RTValue[] ordinalValues) {
assert RTRecordValue.verifyOrdinalData(ordinalNames, ordinalValues) :
"Invalid ordinal data in OrdinalData.Ordinal constructor.";
this.ordinalNames = ordinalNames;
this.ordinalValues = ordinalValues;
}
@Override
boolean isTuple (){
return false;
}
@Override
boolean hasOrdinalFields() {
return true;
}
@Override
int getNOrdinalNames() {
return ordinalNames.length;
}
@Override
int getNthOrdinalName(int n) {
return ordinalNames[n];
}
@Override
int[] getOrdinalNames() {
return ordinalNames;
}
@Override
RTValue[] getOrdinalValues() {
return ordinalValues;
}
}
abstract boolean isTuple();
abstract boolean hasOrdinalFields();
abstract int[] getOrdinalNames();
abstract int getNOrdinalNames();
abstract int getNthOrdinalName(int n);
abstract RTValue[] getOrdinalValues();
}
/**
* A helper class to represent the pair (textualNames, textualValues) returned from certain
* intermediate functions.
* @author Bo Ilic
*/
private static final class TextualData {
private final String[] textualNames;
private final RTValue[] textualValues;
private static final TextualData EMPTY = new TextualData();
private TextualData() {
textualNames = null;
textualValues = null;
}
TextualData(String[] textualNames, RTValue[] textualValues) {
assert RTRecordValue.verifyTextualData(textualNames, textualValues) :
"Invalid textual data in TextualData constructor.";
this.textualNames = textualNames;
this.textualValues = textualValues;
}
boolean hasTextualFields() {
return this != EMPTY;
}
String[] getTextualNames() {
return textualNames;
}
RTValue[] getTextualValues() {
return textualValues;
}
}
/**
* Represents the empty record value {}.
*
* @author Bo Ilic
*/
private static final class EmptyRecord extends RTRecordValue {
private EmptyRecord() {
// Make constructor private to prevent multiple instances.
}
/** {@inheritDoc} */
@Override
public RTRecordValue makeFromValues(RTValue[] ordinalValues, RTValue[] textualValues) {
assert (ordinalValues == null && textualValues == null) :
"Invalid argument in EmptyRecord.makeFromValues";
return EMPTY_RECORD;
}
/** {@inheritDoc} */
@Override
public RTValue getTextualFieldValue(String textualFieldName) {
throw new UnsupportedOperationException();
}
/** {@inheritDoc} */
@Override
public RTValue getOrdinalFieldValue(int ordinal) {
throw new UnsupportedOperationException();
}
/** {@inheritDoc} */
@Override
public int getNFields() {
return 0;
}
/** {@inheritDoc} */
@Override
public RTValue getNthValue(int n) {
throw new IndexOutOfBoundsException();
}
/** {@inheritDoc} */
@Override
public final CalValue debug_getChild(int childN) {
throw new IndexOutOfBoundsException();
}
/** {@inheritDoc} */
@Override
public boolean hasOrdinalField(int ordinal) {
return false;
}
/** {@inheritDoc} */
@Override
public boolean hasTextualField(String textualFieldName) {
return false;
}
/** {@inheritDoc} */
@Override
public int indexOfField(String fieldName) {
return -1;
}
/** {@inheritDoc} */
@Override
public List<String> fieldNames() {
return Collections.<String>emptyList();
}
/** {@inheritDoc} */
@Override
public List<RTValue> fieldValues() {
return Collections.<RTValue>emptyList();
}
/** {@inheritDoc} */
@Override
public FieldName getNthFieldName(int n) {
throw new IndexOutOfBoundsException();
}
/** {@inheritDoc} */
@Override
OrdinalData getOrdinalData() {
return OrdinalData.EMPTY;
}
/** {@inheritDoc} */
@Override
TextualData getTextualData() {
return TextualData.EMPTY;
}
/** {@inheritDoc} */
@Override
OrdinalData retractTupleFields(int tupleSize) {
throw new UnsupportedOperationException();
}
/** {@inheritDoc} */
@Override
OrdinalData retractOrdinalFields(int[] retractedOrdinalNames) {
throw new UnsupportedOperationException();
}
/** {@inheritDoc} */
@Override
TextualData retractTextualFields(String[] retractedTextualNames) {
throw new UnsupportedOperationException();
}
/** {@inheritDoc} */
@Override
OrdinalData mergeTupleFields(RTValue[] extensionOrdinalValues) {
return new OrdinalData.Tuple(extensionOrdinalValues);
}
/** {@inheritDoc} */
@Override
OrdinalData mergeOrdinalFields(int[] extensionOrdinalNames, RTValue[] extensionOrdinalValues) {
return new OrdinalData.Ordinal(extensionOrdinalNames, extensionOrdinalValues);
}
/** {@inheritDoc} */
@Override
TextualData mergeTextualFields(String[] extensionTextualNames, RTValue[] extensionTextualValues) {
return new TextualData(extensionTextualNames, extensionTextualValues);
}
/** {@inheritDoc} */
@Override
public int getNOrdinalFields() {
return 0;
}
/** {@inheritDoc} */
@Override
public int getNthOrdinalFieldName(int n) {
throw new IndexOutOfBoundsException();
}
/** {@inheritDoc} */
@Override
public RTValue getNthOrdinalValue(int n) {
throw new IndexOutOfBoundsException();
}
/** {@inheritDoc} */
@Override
public int getNTextualFields() {
return 0;
}
/** {@inheritDoc} */
@Override
public String getNthTextualFieldName(int n) {
throw new IndexOutOfBoundsException();
}
/** {@inheritDoc} */
@Override
public RTValue getNthTextualValue(int n) {
throw new IndexOutOfBoundsException();
}
/** {@inheritDoc} */
@Override
public boolean hasTupleOrdinalPart() {
return false;
}
/** {@inheritDoc} */
@Override
public RTRecordValue insertOrdinalField(int fieldOrdinal, RTValue fieldValue) {
if(fieldOrdinal == 1) {
return RTRecordValue.makeTupleRecord(new RTValue[] {fieldValue});
} else {
return RTRecordValue.makeOrdinalRecord(new int[] {fieldOrdinal}, new RTValue[] {fieldValue});
}
}
/** {@inheritDoc} */
@Override
public RTRecordValue insertTextualField(String fieldName, RTValue fieldValue) {
return RTRecordValue.makeTextualRecord(new String[] {fieldName}, new RTValue[] {fieldValue});
}
/** {@inheritDoc} */
@Override
public RTRecordValue appendRecord(RTRecordValue otherRecord) {
return otherRecord;
}
/** {@inheritDoc} */
@Override
public boolean sameFields(RecordType otherRecord) {
return (otherRecord instanceof EmptyRecord);
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateOrdinalField(int ordinal, RTValue fieldValue) {
throw new UnsupportedOperationException("Attempt to update the empty record at field #" + ordinal + ".");
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateMixedOrdinalField(int ordinal, RTValue fieldValue) {
throw new UnsupportedOperationException("Attempt to update the empty record at field #" + ordinal + ".");
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateTextualField(String textualFieldName, RTValue fieldValue) {
throw new UnsupportedOperationException("Attempt to update the empty record at field " + textualFieldName + ".");
}
/** {@inheritDoc} */
@Override
public RTRecordValue mutateOrdinalField(int ordinal, RTValue fieldValue) {
throw new UnsupportedOperationException("Attempt to mutate the empty record at field #" + ordinal + ".");
}
/** {@inheritDoc} */
@Override
public RTRecordValue mutateTextualField(String textualFieldName, RTValue fieldValue) {
throw new UnsupportedOperationException("Attempt to mutate the empty record at field " + textualFieldName + ".");
}
/**
* @see org.openquark.cal.internal.runtime.lecc.RTValue#apply(org.openquark.cal.internal.runtime.lecc.RTValue)
* {@inheritDoc}
*/
@Override
public RTValue apply(RTValue argument) {
//The meaning of application to a record value is pointwise application i.e.
//{field1 = f1, field2 = f2, ..., fieldk = fk} x == {field1 = f1 x, field2 = f2 x, ... fieldk = fk x}
//In normal circumstances, record values cannot be applied to values because the resulting types are
//not typeable in CAL source. However, this situation does arise in the case of hidden dictionary switching.
//
//For example:
//
//{field1 = dictOrdInt, field2 = dictOrdString} 0
//is changed to
//{field1 = dictOrdInt 0, field2 = dictOrdString 0}
//(which latter reduces to {field1 = dictEqInt, field2 = dictEqString})
//
//in other words, the Int value being applied is applied pointwise to each field value.
return this;
}
}
/**
* Represents tuple-records i.e.
* -all the fields in the record are ordinal fields
* -the ordinal fields are consecutive i.e. #1, #2, ..., #n.
* -not the empty record
* For example, {#1 = "abc", #2 = 100.0, #3 = True}
*
* @author Bo Ilic
*/
private static final class TupleRecord extends RTRecordValue {
/**
* the ith element of ordinalValues is the field value corresponding
* to the ordinal field name #i. Will have positive length.
*/
private final RTValue[] ordinalValues;
private TupleRecord(RTValue[] ordinalValues) {
assert RTRecordValue.verifyTupleData(ordinalValues) :
"Invalid tuple data in TupleRecord constructor.";
this.ordinalValues = ordinalValues;
}
@Override
public RTRecordValue makeFromValues(RTValue[] newOrdinalValues, RTValue[] textualValues) {
assert (textualValues == null) :
"Illegal argument in TupleRecord.makeFromValues";
return new TupleRecord(newOrdinalValues);
}
@Override
public RTValue getTextualFieldValue(String textualFieldName) {
throw new UnsupportedOperationException();
}
@Override
public RTValue getOrdinalFieldValue(int ordinal) {
int index = ordinal - 1;
// Update value to remove indirection chains.
RTValue ordinalValue = ordinalValues[index];
if (ordinalValue instanceof RTResultFunction) {
return (ordinalValues[index] = ordinalValue.getValue());
}
return ordinalValue;
}
@Override
public int getNFields() {
return ordinalValues.length;
}
@Override
public RTValue getNthValue(int n) {
// Update value to remove indirection chains.
RTValue ordinalValue = ordinalValues[n];
if (ordinalValue instanceof RTResultFunction) {
return (ordinalValues[n] = ordinalValue.getValue());
}
return ordinalValue;
}
/** {@inheritDoc} */
@Override
public final CalValue debug_getChild(int childN) {
return ordinalValues[childN];
}
@Override
public boolean hasOrdinalField(int ordinal) {
return ordinal <= ordinalValues.length;
}
@Override
public boolean hasTextualField(String textualFieldName) {
return false;
}
@Override
public int indexOfField(String fieldName) {
// Only ordinal field names make sense here
if(!FieldName.Ordinal.isValidCalSourceForm(fieldName)) {
return -1;
}
int ordinal = fieldOrdinal(fieldName);
if(ordinal > getNFields()) {
return -1;
}
return ordinal - 1;
}
@Override
public List<String> fieldNames() {
int nFields = getNFields();
String[] fieldNames = new String[nFields];
for (int i = 0; i < nFields; ++i) {
fieldNames[i] = "#" + (i + 1);
}
return Arrays.asList(fieldNames);
}
@Override
public List<RTValue> fieldValues() {
// Update values to remove indirection chains.
updateOrdinalValues();
return Collections.unmodifiableList(Arrays.asList(ordinalValues));
}
/** {@inheritDoc}*/
@Override
public FieldName getNthFieldName(int n) {
return FieldName.makeOrdinalField(getNthOrdinalFieldName(n));
}
@Override
OrdinalData getOrdinalData() {
// Update values to remove indirection chains.
updateOrdinalValues();
return new OrdinalData.Tuple(ordinalValues);
}
@Override
TextualData getTextualData() {
return TextualData.EMPTY;
}
@Override
OrdinalData retractTupleFields(int retractedTupleSize) {
// Update values to remove indirection chains.
updateOrdinalValues();
return RTRecordValue.retractFromTupleUsingTuple(ordinalValues, retractedTupleSize);
}
@Override
OrdinalData retractOrdinalFields(int[] retractedOrdinalNames) {
// Update values to remove indirection chains.
updateOrdinalValues();
return RTRecordValue.retractFromTupleUsingOrdinal(ordinalValues, retractedOrdinalNames);
}
@Override
TextualData retractTextualFields(String[] retractedTextualNames) {
throw new UnsupportedOperationException();
}
@Override
OrdinalData mergeTupleFields(RTValue[] extensionOrdinalValues) {
//this is unsupported since 2 tuple (of size > 0) must have overlapping fields
throw new UnsupportedOperationException();
}
@Override
OrdinalData mergeOrdinalFields(int[] extensionOrdinalNames, RTValue[] extensionOrdinalValues) {
// Update values to remove indirection chains.
updateOrdinalValues();
return RTRecordValue.mergeTupleWithOrdinal(ordinalValues, extensionOrdinalNames, extensionOrdinalValues);
}
@Override
TextualData mergeTextualFields(String[] extensionTextualNames, RTValue[] extensionTextualValues) {
return new TextualData(extensionTextualNames, extensionTextualValues);
}
/** {@inheritDoc} */
@Override
public int getNOrdinalFields() {
return ordinalValues.length;
}
/** {@inheritDoc} */
@Override
public int getNthOrdinalFieldName(int n) {
if (n < 0 || n >= ordinalValues.length) {
throw new IndexOutOfBoundsException();
}
return n + 1;
}
/** {@inheritDoc} */
@Override
public RTValue getNthOrdinalValue(int n) {
// Update value to remove indirection chains.
RTValue ordinalValue = ordinalValues[n];
if (ordinalValue instanceof RTResultFunction) {
return(ordinalValues[n] = ordinalValue.getValue());
}
return ordinalValue;
}
/** {@inheritDoc} */
@Override
public int getNTextualFields() {
return 0;
}
/** {@inheritDoc} */
@Override
public String getNthTextualFieldName(int n) {
throw new IndexOutOfBoundsException();
}
/** {@inheritDoc} */
@Override
public RTValue getNthTextualValue(int n) {
throw new IndexOutOfBoundsException();
}
/** {@inheritDoc} */
@Override
public boolean hasTupleOrdinalPart() {
return true;
}
/** {@inheritDoc} */
@Override
public RTRecordValue appendRecord(RTRecordValue otherRecord) {
OrdinalData ordinalData = overlappingMergeOrdinalData(getOrdinalData(), otherRecord.getOrdinalData());
return makeRecordFromFieldData(ordinalData, otherRecord.getTextualData());
}
/**
* @return boolean. True if the record has the fields #1, ..., #n, with no gaps, n >= 2
* and there are no other fields.
*/
@Override
public boolean isTuple2OrMoreRecord() {
return getNOrdinalFields() > 1;
}
/** {@inheritDoc} */
@Override
public boolean sameFields(RecordType otherRecordType) {
if(!(otherRecordType instanceof TupleRecord)) {
return false;
}
return otherRecordType.getNFields() == getNFields();
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateOrdinalField(int ordinal, RTValue fieldValue) {
RTValue[] newOrdinalValues = ordinalValues.clone();
newOrdinalValues[ordinal - 1] = fieldValue;
return new TupleRecord(newOrdinalValues);
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateMixedOrdinalField(int ordinal, RTValue fieldValue) {
// This method should only be called when an we are updating ordinal fields
// followed by mutating textual fields. Since an textual record doesn't
// contain ordinal values this is an error.
throw new UnsupportedOperationException("Attempt to do mixed update on tuple record.");
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateTextualField(String textualFieldName, RTValue fieldValue) {
throw new UnsupportedOperationException("Attempt to update the tuple record at field " + textualFieldName + ".");
}
/** {@inheritDoc} */
@Override
public RTRecordValue mutateOrdinalField(int ordinal, RTValue fieldValue) {
ordinalValues[ordinal - 1] = fieldValue;
return this;
}
/** {@inheritDoc} */
@Override
public RTRecordValue mutateTextualField(String textualFieldName, RTValue fieldValue) {
throw new UnsupportedOperationException("Attempt to mutate the tuple record at field " + textualFieldName + ".");
}
/**
* @see org.openquark.cal.internal.runtime.lecc.RTValue#apply(org.openquark.cal.internal.runtime.lecc.RTValue)
*/
@Override
public RTValue apply(RTValue argument) {
//The meaning of application to a record value is pointwise application i.e.
//{field1 = f1, field2 = f2, ..., fieldk = fk} x == {field1 = f1 x, field2 = f2 x, ... fieldk = fk x}
//In normal circumstances, record values cannot be applied to values because the resulting types are
//not typeable in CAL source. However, this situation does arise in the case of hidden dictionary switching.
//
//For example:
//
//{field1 = dictOrdInt, field2 = dictOrdString} 0
//is changed to
//{field1 = dictOrdInt 0, field2 = dictOrdString 0}
//(which latter reduces to {field1 = dictEqInt, field2 = dictEqString})
//
//in other words, the Int value being applied is applied pointwise to each field value.
int nOrdinalFields = getNOrdinalFields();
RTValue[] newOrdinalValues = new RTValue[nOrdinalFields];
for (int i = 0; i < nOrdinalFields; ++i) {
newOrdinalValues[i] = getNthOrdinalValue(i).apply(argument);
}
return new TupleRecord(newOrdinalValues);
}
/**
* {@inheritDoc}
*/
@Override
public final String debug_getNodeStartText() {
if (ordinalValues.length == 1) {
return "{";
}
return "(";
}
/**
* {@inheritDoc}
*/
@Override
public final String debug_getNodeEndText() {
if (ordinalValues.length == 1) {
return "}";
}
return ")";
}
/**
* {@inheritDoc}
*/
@Override
public final String debug_getChildPrefixText(int childN) {
int nOrdinalFields = ordinalValues.length;
if (nOrdinalFields == 1) {
return super.debug_getChildPrefixText(childN);
}
if (childN >= 0 && childN < nOrdinalFields) {
if (childN == 0) {
return "";
}
return ", ";
}
throw new IndexOutOfBoundsException();
}
/**
* Update the ordinal values to remove any indirection chains.
*/
private final void updateOrdinalValues () {
for (int i = 0, n = ordinalValues.length; i < n; ++i) {
RTValue ordinalValue = ordinalValues[i];
if (ordinalValue instanceof RTResultFunction) {
ordinalValues[i] = ordinalValue.getValue();
}
}
}
}
/**
* Represents ordinal records i.e.
* -all the fields in the record are ordinal fields
* -not a TupleRecord or the EmptyRecord.
*
* For example,
* {#1 = "abc", #3 = True} is an OrdinalRecord
* {#2 = 100.0, #3 = True} is an OrdinalRecord
* {#1 = "abc", #2 = 100.0, #3 = True} is a TupleRecord and not an OrdinalRecord.
*
* @author Bo Ilic
*/
private static final class OrdinalRecord extends RTRecordValue {
/**
* the ordinal field names (as int values) in ascending ordinal order. Will have positive length
* and not be [1, 2, 3, ..., ordinalValues.length]
*/
private final int[] ordinalNames;
/**
* the ith element of ordinalValues is the field value corresponding to the ordinal field name
* held at the ith element of ordinalNames.
*/
private final RTValue[] ordinalValues;
private OrdinalRecord(int[] ordinalNames, RTValue[] ordinalValues) {
assert RTRecordValue.verifyOrdinalData(ordinalNames, ordinalValues) :
"Invalid ordinal data in OrdinalRecord constructor.";
this.ordinalNames = ordinalNames;
this.ordinalValues = ordinalValues;
}
@Override
public RTRecordValue makeFromValues(RTValue[] newOrdinalValues, RTValue[] textualValues) {
assert (textualValues == null) :
"Illegal argument in OrdinalRecord.makeFromValues.";
return new OrdinalRecord(this.ordinalNames, newOrdinalValues);
}
@Override
public RTValue getTextualFieldValue(String textualFieldName) {
throw new UnsupportedOperationException();
}
@Override
public RTValue getOrdinalFieldValue(int ordinal) {
int index = Arrays.binarySearch(ordinalNames, ordinal);
// Update value to remove indirection chain.
RTValue ordinalValue = ordinalValues[index];
if (ordinalValue instanceof RTResultFunction) {
return (ordinalValues[index] = ordinalValue.getValue());
}
return ordinalValue;
}
@Override
public int getNFields() {
return ordinalNames.length;
}
@Override
public RTValue getNthValue(int n) {
// Update value to remove indirection chains.
RTValue ordinalValue = ordinalValues[n];
if (ordinalValue instanceof RTResultFunction) {
return (ordinalValues[n] = ordinalValue.getValue());
}
return ordinalValue;
}
/**
* {@inheritDoc}
*/
@Override
public final CalValue debug_getChild(int childN) {
return ordinalValues[childN];
}
@Override
public boolean hasOrdinalField(int ordinal) {
//if the ordinal is bigger than all the ordinals in ordinalNames, then we can dispense with binary search
if (ordinal > ordinalNames[ordinalNames.length - 1]) {
return false;
}
return Arrays.binarySearch(ordinalNames, ordinal) >= 0;
}
@Override
public boolean hasTextualField(String textualFieldName) {
return false;
}
@Override
public int indexOfField(String fieldName) {
// Only ordinal field names make sense here
if(!FieldName.Ordinal.isValidCalSourceForm(fieldName)) {
return -1;
}
int ordinal = fieldOrdinal(fieldName);
int index = Arrays.binarySearch(ordinalNames, ordinal);
if(index < 0) {
return -1;
}
return index;
}
@Override
public List<String> fieldNames() {
int nFields = getNFields();
String[] fieldNames = new String[nFields];
for (int i = 0; i < nFields; ++i) {
fieldNames[i] = "#" + ordinalNames[i];
}
return Arrays.asList(fieldNames);
}
@Override
public List<RTValue> fieldValues() {
// Update values to remove indirection chains.
updateOrdinalValues();
return Collections.unmodifiableList(Arrays.asList(ordinalValues));
}
/** {@inheritDoc}*/
@Override
public FieldName getNthFieldName(int n) {
return FieldName.makeOrdinalField(ordinalNames[n]);
}
@Override
OrdinalData getOrdinalData() {
// Update values to remove indirection chains.
updateOrdinalValues();
return new OrdinalData.Ordinal(ordinalNames, ordinalValues);
}
@Override
TextualData getTextualData() {
return TextualData.EMPTY;
}
@Override
OrdinalData retractTupleFields(int retractedTupleSize) {
// Update values to remove indirection chains.
updateOrdinalValues();
return RTRecordValue.retractFromOrdinalUsingTuple(ordinalNames, ordinalValues, retractedTupleSize);
}
@Override
OrdinalData retractOrdinalFields(int[] retractedOrdinalNames) {
// Update values to remove indirection chains.
updateOrdinalValues();
return RTRecordValue.retractFromOrdinalUsingOrdinal(ordinalNames, ordinalValues, retractedOrdinalNames);
}
@Override
TextualData retractTextualFields(String[] retractedTextualNames) {
throw new UnsupportedOperationException();
}
@Override
OrdinalData mergeTupleFields(RTValue[] extensionOrdinalValues) {
// Update values to remove indirection chains.
updateOrdinalValues();
return RTRecordValue.mergeOrdinalWithTuple(ordinalNames, ordinalValues, extensionOrdinalValues);
}
@Override
OrdinalData mergeOrdinalFields(int[] extensionOrdinalNames, RTValue[] extensionOrdinalValues) {
// Update values to remove indirection chains.
updateOrdinalValues();
return RTRecordValue.mergeOrdinalWithOrdinal(ordinalNames, ordinalValues, extensionOrdinalNames, extensionOrdinalValues);
}
@Override
TextualData mergeTextualFields(String[] extensionTextualNames, RTValue[] extensionTextualValues) {
return new TextualData(extensionTextualNames, extensionTextualValues);
}
/** {@inheritDoc} */
@Override
public int getNOrdinalFields() {
return ordinalNames.length;
}
/** {@inheritDoc} */
@Override
public int getNthOrdinalFieldName(int n) {
return ordinalNames[n];
}
/** {@inheritDoc} */
@Override
public RTValue getNthOrdinalValue(int n) {
// Update value to remove indirection chains.
RTValue ordinalValue = ordinalValues[n];
if (ordinalValue instanceof RTResultFunction) {
return (ordinalValues[n] = ordinalValue.getValue());
}
return ordinalValue;
}
/** {@inheritDoc} */
@Override
public int getNTextualFields() {
return 0;
}
/** {@inheritDoc}*/
@Override
public String getNthTextualFieldName(int n) {
throw new IndexOutOfBoundsException();
}
/** {@inheritDoc} */
@Override
public RTValue getNthTextualValue(int n) {
throw new IndexOutOfBoundsException();
}
/** {@inheritDoc} */
@Override
public boolean hasTupleOrdinalPart() {
return false;
}
/** {@inheritDoc} */
@Override
public RTRecordValue appendRecord(RTRecordValue otherRecord) {
OrdinalData ordinalData = overlappingMergeOrdinalData(getOrdinalData(), otherRecord.getOrdinalData());
return makeRecordFromFieldData(ordinalData, otherRecord.getTextualData());
}
/** {@inheritDoc} */
@Override
public boolean sameFields(RecordType otherRecordType) {
if(!(otherRecordType instanceof OrdinalRecord)) {
return false;
}
OrdinalRecord otherOrdinalRecord = (OrdinalRecord)otherRecordType;
return Arrays.equals(otherOrdinalRecord.ordinalNames, ordinalNames);
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateOrdinalField(int ordinal, RTValue fieldValue) {
int index = Arrays.binarySearch(ordinalNames, ordinal);
RTValue[] newOrdinalValues = ordinalValues.clone();
newOrdinalValues[index] = fieldValue;
return new OrdinalRecord(ordinalNames, newOrdinalValues);
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateMixedOrdinalField(int ordinal, RTValue fieldValue) {
// This method should only be called when an we are updating ordinal fields
// followed by mutating textual fields. Since an ordinal record doesn't
// contain textual values this is an error.
throw new UnsupportedOperationException("Attempt to do mixed update on ordinal record.");
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateTextualField(String textualFieldName, RTValue fieldValue) {
throw new UnsupportedOperationException("Attempt to update the ordinal record at field " + textualFieldName + ".");
}
/** {@inheritDoc} */
@Override
public RTRecordValue mutateOrdinalField(int ordinal, RTValue fieldValue) {
int index = Arrays.binarySearch(ordinalNames, ordinal);
ordinalValues[index] = fieldValue;
return this;
}
/** {@inheritDoc} */
@Override
public RTRecordValue mutateTextualField(String textualFieldName, RTValue fieldValue) {
throw new UnsupportedOperationException("Attempt to mutate the ordinal record at field " + textualFieldName + ".");
}
/**
* @see org.openquark.cal.internal.runtime.lecc.RTValue#apply(org.openquark.cal.internal.runtime.lecc.RTValue)
*/
@Override
public RTValue apply(RTValue argument) {
//The meaning of application to a record value is pointwise application i.e.
//{field1 = f1, field2 = f2, ..., fieldk = fk} x == {field1 = f1 x, field2 = f2 x, ... fieldk = fk x}
//In normal circumstances, record values cannot be applied to values because the resulting types are
//not typeable in CAL source. However, this situation does arise in the case of hidden dictionary switching.
//
//For example:
//
//{field1 = dictOrdInt, field2 = dictOrdString} 0
//is changed to
//{field1 = dictOrdInt 0, field2 = dictOrdString 0}
//(which latter reduces to {field1 = dictEqInt, field2 = dictEqString})
//
//in other words, the Int value being applied is applied pointwise to each field value.
int nOrdinalFields = getNOrdinalFields();
RTValue[] newOrdinalValues = new RTValue[nOrdinalFields];
for (int i = 0; i < nOrdinalFields; ++i) {
newOrdinalValues[i] = getNthOrdinalValue(i).apply(argument);
}
return new OrdinalRecord(this.ordinalNames, newOrdinalValues);
}
/**
* Update the ordinal values to remove any indirection chains.
*/
private final void updateOrdinalValues () {
for (int i = 0, n = ordinalValues.length; i < n; ++i) {
RTValue ordinalValue = ordinalValues[i];
if (ordinalValue instanceof RTResultFunction) {
ordinalValues[i] = ordinalValue.getValue();
}
}
}
}
/**
* Represents textual records i.e.
* -all the fields in the record are textual fields
* -not the EmptyRecord (i.e. there is at least 1 textual field).
*
* For example,
* {name = "abc", flag = True} is a TextualRecord
*
* @author Bo Ilic
*/
private static final class TextualRecord extends RTRecordValue {
/** the textual field names of the record, in ascending alphabetical order. Will have positive length. */
private final String[] textualNames;
/**
* the ith element of textualValues is the field value corresponding to the textual field name
* held at the ith element of ordinalNames.
*/
private final RTValue[] textualValues;
private TextualRecord(String[] textualNames, RTValue[] textualValues) {
assert RTRecordValue.verifyTextualData(textualNames, textualValues) :
"Invalid textual data in TextualRecord constructor.";
this.textualNames = textualNames;
this.textualValues = textualValues;
}
@Override
public RTRecordValue makeFromValues(RTValue[] ordinalValues, RTValue[] newTextualValues) {
assert (ordinalValues == null) :
"Illegal argument in TextualRecord.makeFromValues.";
return new TextualRecord(this.textualNames, newTextualValues);
}
@Override
public RTValue getTextualFieldValue(String textualFieldName) {
int index = Arrays.binarySearch(textualNames, textualFieldName);
// Update value to remove indirection chains.
RTValue textualValue = textualValues[index];
if (textualValue instanceof RTResultFunction) {
return (textualValues[index] = textualValue.getValue());
}
return textualValue;
}
@Override
public RTValue getOrdinalFieldValue(int ordinal) {
throw new UnsupportedOperationException();
}
@Override
public int getNFields() {
return textualNames.length;
}
@Override
public RTValue getNthValue(int n) {
// Update value to remove indirection chains.
RTValue textualValue = textualValues[n];
if (textualValue instanceof RTResultFunction) {
return (textualValues[n] = textualValue.getValue());
}
return textualValue;
}
/** {@inheritDoc} */
@Override
public final CalValue debug_getChild(int childN) {
return textualValues[childN];
}
@Override
public boolean hasOrdinalField(int ordinal) {
return false;
}
@Override
public boolean hasTextualField(String textualFieldName) {
return Arrays.binarySearch(textualNames, textualFieldName) >= 0;
}
@Override
public int indexOfField(String fieldName) {
int index = Arrays.binarySearch(textualNames, fieldName);
if(index < 0) {
return -1;
}
return index;
}
@Override
public List<String> fieldNames() {
return Collections.unmodifiableList(Arrays.asList(textualNames));
}
@Override
public List<RTValue> fieldValues() {
// Update values to remove indirection chains.
updateTextualValues();
return Collections.unmodifiableList(Arrays.asList(textualValues));
}
/** {@inheritDoc}*/
@Override
public FieldName getNthFieldName(int n) {
return FieldName.make(textualNames[n]);
}
@Override
OrdinalData getOrdinalData() {
return OrdinalData.EMPTY;
}
@Override
TextualData getTextualData() {
// Update values to remove indirecton chains.
updateTextualValues();
return new TextualData(textualNames, textualValues);
}
@Override
OrdinalData retractTupleFields(int retractedTupleSize) {
throw new UnsupportedOperationException();
}
@Override
OrdinalData retractOrdinalFields(int[] retractedOrdinalNames) {
throw new UnsupportedOperationException();
}
@Override
TextualData retractTextualFields(String[] retractedTextualNames) {
// Update values to remove indirecton chains.
updateTextualValues();
return RTRecordValue.retractFromTextualUsingTextual(textualNames, textualValues, retractedTextualNames);
}
@Override
OrdinalData mergeTupleFields(RTValue[] extensionOrdinalValues) {
return new OrdinalData.Tuple(extensionOrdinalValues);
}
@Override
OrdinalData mergeOrdinalFields(int[] extensionOrdinalNames, RTValue[] extensionOrdinalValues) {
return new OrdinalData.Ordinal(extensionOrdinalNames, extensionOrdinalValues);
}
@Override
TextualData mergeTextualFields(String[] extensionTextualNames, RTValue[] extensionTextualValues) {
// Update values to remove indirecton chains.
updateTextualValues();
return RTRecordValue.mergeTextualWithTextual(textualNames, textualValues, extensionTextualNames, extensionTextualValues);
}
/** {@inheritDoc}*/
@Override
public int getNOrdinalFields() {
return 0;
}
/** {@inheritDoc}*/
@Override
public int getNthOrdinalFieldName(int n) {
throw new IndexOutOfBoundsException();
}
/** {@inheritDoc}*/
@Override
public RTValue getNthOrdinalValue(int n) {
throw new IndexOutOfBoundsException();
}
/** {@inheritDoc}*/
@Override
public int getNTextualFields() {
return textualNames.length;
}
/** {@inheritDoc}*/
@Override
public String getNthTextualFieldName(int n) {
return textualNames[n];
}
/** {@inheritDoc}*/
@Override
public RTValue getNthTextualValue(int n) {
// Update value to remove indirection chains.
RTValue textualValue = textualValues[n];
if (textualValue instanceof RTResultFunction) {
return (textualValues[n] = textualValue.getValue());
}
return textualValue;
}
/** {@inheritDoc} */
@Override
public boolean hasTupleOrdinalPart() {
return false;
}
/** {@inheritDoc} */
@Override
public RTRecordValue appendRecord(RTRecordValue otherRecord) {
TextualData textualData = overlappingMergeTextualData(getTextualData(), otherRecord.getTextualData());
return makeRecordFromFieldData(otherRecord.getOrdinalData(), textualData);
}
/** {@inheritDoc} */
@Override
public boolean sameFields(RecordType otherRecordType) {
if(!(otherRecordType instanceof TextualRecord)) {
return false;
}
TextualRecord otherTextualRecord = (TextualRecord)otherRecordType;
return Arrays.equals(otherTextualRecord.textualNames, textualNames);
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateOrdinalField(int ordinal, RTValue fieldValue) {
throw new UnsupportedOperationException("Attempt to update the textual record at field #" + ordinal + ".");
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateMixedOrdinalField(int ordinal, RTValue fieldValue) {
// This method should only be called when an we are updating ordinal fields
// followed by mutating textual fields. Since an textual record doesn't
// contain ordinal values this is an error.
throw new UnsupportedOperationException("Attempt to do mixed update on textual record.");
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateTextualField(String textualFieldName, RTValue fieldValue) {
int index = Arrays.binarySearch(textualNames, textualFieldName);
RTValue[] newTextualValues = textualValues.clone();
newTextualValues[index] = fieldValue;
return new TextualRecord(textualNames, newTextualValues);
}
/** {@inheritDoc} */
@Override
public RTRecordValue mutateOrdinalField(int ordinal, RTValue fieldValue) {
throw new UnsupportedOperationException("Attempt to mutate the textual record at field #" + ordinal + ".");
}
/** {@inheritDoc} */
@Override
public RTRecordValue mutateTextualField(String textualFieldName, RTValue fieldValue) {
int index = Arrays.binarySearch(textualNames, textualFieldName);
textualValues[index] = fieldValue;
return this;
}
/**
* @see org.openquark.cal.internal.runtime.lecc.RTValue#apply(org.openquark.cal.internal.runtime.lecc.RTValue)
*/
@Override
public RTValue apply(RTValue argument) {
//The meaning of application to a record value is pointwise application i.e.
//{field1 = f1, field2 = f2, ..., fieldk = fk} x == {field1 = f1 x, field2 = f2 x, ... fieldk = fk x}
//In normal circumstances, record values cannot be applied to values because the resulting types are
//not typeable in CAL source. However, this situation does arise in the case of hidden dictionary switching.
//
//For example:
//
//{field1 = dictOrdInt, field2 = dictOrdString} 0
//is changed to
//{field1 = dictOrdInt 0, field2 = dictOrdString 0}
//(which latter reduces to {field1 = dictEqInt, field2 = dictEqString})
//
//in other words, the Int value being applied is applied pointwise to each field value.
int nTextualFields = getNTextualFields();
RTValue[] newTextualValues = new RTValue[nTextualFields];
for (int i = 0; i < nTextualFields; ++i) {
newTextualValues[i] = getNthTextualValue(i).apply(argument);
}
return new TextualRecord(this.textualNames, newTextualValues);
}
/**
* Update the textual values to remove any indirection chains.
*/
private final void updateTextualValues () {
for (int i = 0, n = textualValues.length; i < n; ++i) {
RTValue textualValue = textualValues[i];
if (textualValue instanceof RTResultFunction) {
textualValues[i] = textualValue.getValue();
}
}
}
}
/**
* Represents records having both ordinal and textual fields, and such that the ordinal part is a tuple i.e.
* -there is at least 1 ordinal field, and at least 1 textual field
* -the ordinal fields are consecutive i.e. #1, #2, ..., #n.
* For example,
* {#1 = 2.0, #2 = "FooBar", name = "abc", flag = True} is a TupleMixedRecord
*
* @author Bo Ilic
*/
private static final class TupleMixedRecord extends RTRecordValue {
/**
* the ith element of ordinalValues is the field value corresponding to
* the ordinal field name #i. Will have positive length.
*/
private final RTValue[] ordinalValues;
/**
* the textual field names of the record, in ascending alphabetical
* order. Will have positive length.
*/
private final String[] textualNames;
/**
* the ith element of textualValues is the field value corresponding to
* the textual field name held at the ith element of ordinalNames.
*/
private final RTValue[] textualValues;
private TupleMixedRecord(RTValue[] ordinalValues, String[] textualNames, RTValue[] textualValues) {
assert RTRecordValue.verifyTupleData(ordinalValues) :
"Invalid tuple data in TupleMixedRecord constructor.";
assert RTRecordValue.verifyTextualData(textualNames, textualValues) :
"Invalid textual data in TupleMixedRecord constructor.";
this.ordinalValues = ordinalValues;
this.textualNames = textualNames;
this.textualValues = textualValues;
}
@Override
public RTRecordValue makeFromValues(RTValue[] newOrdinalValues, RTValue[] newTextualValues) {
return new TupleMixedRecord(newOrdinalValues, this.textualNames, newTextualValues);
}
@Override
public RTValue getTextualFieldValue(String textualFieldName) {
int index = Arrays.binarySearch(textualNames, textualFieldName);
// Update the value to remove indirection chains.
RTValue textualValue = textualValues[index];
if (textualValue instanceof RTResultFunction) {
return (textualValues[index] = textualValue.getValue());
}
return textualValue;
}
@Override
public RTValue getOrdinalFieldValue(int ordinal) {
int index = ordinal - 1;
// Update value to remove indirection chains.
RTValue ordinalValue = ordinalValues[index];
if (ordinalValue instanceof RTResultFunction) {
return (ordinalValues[index] = ordinalValue.getValue());
}
return ordinalValue;
}
@Override
public int getNFields() {
return ordinalValues.length + textualNames.length;
}
@Override
public RTValue getNthValue(int n) {
if (n < ordinalValues.length) {
// Update value to remove indirection chains.
RTValue ordinalValue = ordinalValues[n];
if (ordinalValue instanceof RTResultFunction) {
return (ordinalValues[n] = ordinalValue.getValue());
}
return ordinalValue;
}
n = n - ordinalValues.length;
// Update value to remove indirection chains.
RTValue textualValue = textualValues[n];
if (textualValue instanceof RTResultFunction) {
return (textualValues[n] = textualValue.getValue());
}
return textualValue;
}
/** {@inheritDoc} */
@Override
public final CalValue debug_getChild(int childN) {
if (childN < ordinalValues.length) {
return ordinalValues[childN];
}
return textualValues[childN - ordinalValues.length];
}
@Override
public boolean hasOrdinalField(int ordinal) {
return ordinal <= ordinalValues.length;
}
@Override
public boolean hasTextualField(String textualFieldName) {
return Arrays.binarySearch(textualNames, textualFieldName) >= 0;
}
@Override
public int indexOfField(String fieldName) {
if(FieldName.Ordinal.isValidCalSourceForm(fieldName)) {
int ordinal = fieldOrdinal(fieldName);
if(ordinal > getNOrdinalFields()) {
return -1;
}
return ordinal - 1;
} else {
int index = Arrays.binarySearch(textualNames, fieldName);
if(index < 0) {
return -1;
}
return getNOrdinalFields() + index;
}
}
@Override
public final List<String> fieldNames() {
int nFields = getNFields();
String[] fieldNames = new String[nFields];
int nOrdinalFields = ordinalValues.length;
for (int i = 0; i < nOrdinalFields; ++i) {
fieldNames[i] = "#" + (i + 1);
}
//copy the textual field names
System.arraycopy(textualNames, 0, fieldNames, nOrdinalFields, textualNames.length);
return Arrays.asList(fieldNames);
}
@Override
public final List<RTValue> fieldValues() {
// Update fields to remove indirection chains.
updateTextualValues();
updateOrdinalValues();
int nFields = getNFields();
RTValue[] fieldValues = new RTValue[nFields];
int nOrdinalFields = ordinalValues.length;
//copy the ordinal field values
System.arraycopy(ordinalValues, 0, fieldValues, 0, nOrdinalFields);
//copy the textual field values
System.arraycopy(textualValues, 0, fieldValues, nOrdinalFields, textualValues.length);
return Arrays.asList(fieldValues);
}
/** {@inheritDoc}*/
@Override
public final FieldName getNthFieldName(int n) {
int nOrdinalFields = ordinalValues.length;
if (n < nOrdinalFields) {
return FieldName.makeOrdinalField(n + 1);
}
return FieldName.make(textualNames[n - nOrdinalFields]);
}
@Override
final OrdinalData getOrdinalData() {
// Update values to remove indirection chains.
updateOrdinalValues();
return new OrdinalData.Tuple(ordinalValues);
}
@Override
final TextualData getTextualData() {
// Update values to remove indirection chains.
updateTextualValues();
return new TextualData(textualNames, textualValues);
}
@Override
final OrdinalData retractTupleFields(int retractedTupleSize) {
// Update values to remove indirection chains.
updateOrdinalValues();
return RTRecordValue.retractFromTupleUsingTuple(ordinalValues, retractedTupleSize);
}
@Override
final OrdinalData retractOrdinalFields(int[] retractedOrdinalNames) {
// Update values to remove indirection chains.
updateOrdinalValues();
return RTRecordValue.retractFromTupleUsingOrdinal(ordinalValues, retractedOrdinalNames);
}
@Override
final TextualData retractTextualFields(String[] retractedTextualNames) {
// Update values to remove indirection chains.
updateTextualValues();
return RTRecordValue.retractFromTextualUsingTextual(textualNames, textualValues, retractedTextualNames);
}
@Override
final OrdinalData mergeTupleFields(RTValue[] extensionOrdinalValues) {
throw new UnsupportedOperationException();
}
@Override
final OrdinalData mergeOrdinalFields(int[] extensionOrdinalNames, RTValue[] extensionOrdinalValues) {
// Update values to remove indirection chains.
updateOrdinalValues();
return RTRecordValue.mergeTupleWithOrdinal(ordinalValues, extensionOrdinalNames, extensionOrdinalValues);
}
@Override
final TextualData mergeTextualFields(String[] extensionTextualNames, RTValue[] extensionTextualValues) {
// Update values to remove indirection chains.
updateTextualValues();
return RTRecordValue.mergeTextualWithTextual(textualNames, textualValues, extensionTextualNames, extensionTextualValues);
}
/** {@inheritDoc}*/
@Override
public int getNOrdinalFields() {
return ordinalValues.length;
}
/** {@inheritDoc}*/
@Override
public int getNthOrdinalFieldName(int n) {
if (n < 0 || n >= ordinalValues.length) {
throw new IndexOutOfBoundsException();
}
return (n + 1);
}
/** {@inheritDoc}*/
@Override
public RTValue getNthOrdinalValue(int n) {
// Update value to remove indirection chains.
RTValue ordinalValue = ordinalValues[n];
if (ordinalValue instanceof RTResultFunction) {
return (ordinalValues[n] = ordinalValue.getValue());
}
return ordinalValue;
}
/** {@inheritDoc}*/
@Override
public int getNTextualFields() {
return textualNames.length;
}
/** {@inheritDoc}*/
@Override
public String getNthTextualFieldName(int n) {
return textualNames[n];
}
/** {@inheritDoc}*/
@Override
public RTValue getNthTextualValue(int n) {
// Update value to remove indirection chains.
RTValue textualValue = textualValues[n];
if (textualValue instanceof RTResultFunction) {
return (textualValues[n] = textualValue.getValue());
}
return textualValue;
}
/** {@inheritDoc} */
@Override
public boolean hasTupleOrdinalPart() {
return true;
}
/** {@inheritDoc} */
@Override
public RTRecordValue appendRecord(RTRecordValue otherRecord) {
OrdinalData ordinalData = overlappingMergeOrdinalData(getOrdinalData(), otherRecord.getOrdinalData());
TextualData textualData = overlappingMergeTextualData(getTextualData(), otherRecord.getTextualData());
return makeRecordFromFieldData(ordinalData, textualData);
}
/** {@inheritDoc} */
@Override
public boolean sameFields(RecordType otherRecordType) {
if(!(otherRecordType instanceof TupleMixedRecord)) {
return false;
}
TupleMixedRecord otherTupleMixedRecord = (TupleMixedRecord)otherRecordType;
if(otherTupleMixedRecord.ordinalValues.length != ordinalValues.length) {
return false;
}
return Arrays.equals(otherTupleMixedRecord.textualNames, textualNames);
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateOrdinalField(int ordinal, RTValue fieldValue) {
RTValue[] newOrdinalValues = ordinalValues.clone();
newOrdinalValues[ordinal - 1] = fieldValue;
return new TupleMixedRecord(newOrdinalValues, textualNames, textualValues);
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateMixedOrdinalField(int ordinal, RTValue fieldValue) {
RTValue[] newOrdinalValues = ordinalValues.clone();
newOrdinalValues[ordinal - 1] = fieldValue;
// We want to make a new array to hold textual values in the new record. This
// allows the new records textual values to be mutated without affecting this
// record.
RTValue[] newTextualValues = new RTValue[textualValues.length];
System.arraycopy(textualValues, 0, newTextualValues, 0, newTextualValues.length);
return new TupleMixedRecord(newOrdinalValues, textualNames, newTextualValues);
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateTextualField(String textualFieldName, RTValue fieldValue) {
int index = Arrays.binarySearch(textualNames, textualFieldName);
RTValue[] newTextualValues = textualValues.clone();
newTextualValues[index] = fieldValue;
return new TupleMixedRecord(ordinalValues, textualNames, newTextualValues);
}
/** {@inheritDoc} */
@Override
public RTRecordValue mutateOrdinalField(int ordinal, RTValue fieldValue) {
ordinalValues[ordinal - 1] = fieldValue;
return this;
}
/** {@inheritDoc} */
@Override
public RTRecordValue mutateTextualField(String textualFieldName, RTValue fieldValue) {
int index = Arrays.binarySearch(textualNames, textualFieldName);
textualValues[index] = fieldValue;
return this;
}
/**
* @see org.openquark.cal.internal.runtime.lecc.RTValue#apply(org.openquark.cal.internal.runtime.lecc.RTValue)
*/
@Override
public RTValue apply(RTValue argument) {
//The meaning of application to a record value is pointwise application i.e.
//{field1 = f1, field2 = f2, ..., fieldk = fk} x == {field1 = f1 x, field2 = f2 x, ... fieldk = fk x}
//In normal circumstances, record values cannot be applied to values because the resulting types are
//not typeable in CAL source. However, this situation does arise in the case of hidden dictionary switching.
//
//For example:
//
//{field1 = dictOrdInt, field2 = dictOrdString} 0
//is changed to
//{field1 = dictOrdInt 0, field2 = dictOrdString 0}
//(which latter reduces to {field1 = dictEqInt, field2 = dictEqString})
//
//in other words, the Int value being applied is applied pointwise to each field value.
int nOrdinalFields = getNOrdinalFields();
RTValue[] newOrdinalValues = new RTValue[nOrdinalFields];
for (int i = 0; i < nOrdinalFields; ++i) {
newOrdinalValues[i] = getNthOrdinalValue(i).apply(argument);
}
int nTextualFields = getNTextualFields();
RTValue[] newTextualValues = new RTValue[nTextualFields];
for (int i = 0; i < nTextualFields; ++i) {
newTextualValues[i] = getNthTextualValue(i).apply(argument);
}
return new TupleMixedRecord(newOrdinalValues, this.textualNames, newTextualValues);
}
/**
* Update the ordinal values to remove any indirection chains.
*/
private final void updateOrdinalValues () {
for (int i = 0, n = ordinalValues.length; i < n; ++i) {
RTValue ordinalValue = ordinalValues[i];
if (ordinalValue instanceof RTResultFunction) {
ordinalValues[i] = ordinalValue.getValue();
}
}
}
/**
* Update the textual values to remove any indirection chains.
*/
private final void updateTextualValues () {
for (int i = 0, n = textualValues.length; i < n; ++i) {
RTValue textualValue = textualValues[i];
if (textualValue instanceof RTResultFunction) {
textualValues[i] = textualValue.getValue();
}
}
}
}
/**
* Represents records having both ordinal and textual fields, and such that the ordinal part is not a tuple i.e.
* -there is at least 1 ordinal field, and at least 1 textual field
* -the ordinal fields are not consecutive i.e. #1, #2, ..., #n.
* For example,
* {#1 = 2.0, #3 = "FooBar", name = "abc", flag = True} is a MixedRecord
*
* @author Bo Ilic
*/
private static final class MixedRecord extends RTRecordValue {
/**
* the ordinal field names (as int values) in ascending ordinal order.
* Will have positive length and not be [1, 2, 3, ...,
* ordinalValues.length]
*/
private final int[] ordinalNames;
/**
* the ith element of ordinalValues is the field value corresponding to
* the ordinal field name held at the ith element of ordinalNames.
*/
private final RTValue[] ordinalValues;
/**
* the textual field names of the record, in ascending alphabetical
* order. Will have positive length.
*/
private final String[] textualNames;
/**
* the ith element of textualValues is the field value corresponding to
* the textual field name held at the ith element of ordinalNames.
*/
private final RTValue[] textualValues;
private MixedRecord(int[] ordinalNames, RTValue[] ordinalValues, String[] textualNames, RTValue[] textualValues) {
assert RTRecordValue.verifyOrdinalData(ordinalNames, ordinalValues) :
"Invalid ordinal data in MixedRecord constructor.";
assert RTRecordValue.verifyTextualData(textualNames, textualValues) :
"Invalid textual data in MixedRecord constructor.";
this.ordinalNames = ordinalNames;
this.ordinalValues = ordinalValues;
this.textualNames = textualNames;
this.textualValues = textualValues;
}
@Override
public RTRecordValue makeFromValues(RTValue[] newOrdinalValues, RTValue[] newTextualValues) {
return new MixedRecord(this.ordinalNames, newOrdinalValues, this.textualNames, newTextualValues);
}
@Override
public RTValue getTextualFieldValue(String textualFieldName) {
int index = Arrays.binarySearch(textualNames, textualFieldName);
// Update the value to remove any indirection chains.
RTValue textualValue = textualValues[index];
if (textualValue instanceof RTResultFunction) {
return (textualValues[index] = textualValue.getValue());
}
return textualValue;
}
@Override
public RTValue getOrdinalFieldValue(int ordinal) {
int index = Arrays.binarySearch(ordinalNames, ordinal);
// Update the value to remove any indirection chains.
RTValue ordinalValue = ordinalValues[index];
if (ordinalValue instanceof RTResultFunction) {
return (ordinalValues[index] = ordinalValue.getValue());
}
return ordinalValue;
}
@Override
public int getNFields() {
return ordinalNames.length + textualNames.length;
}
/**
* Update the ordinal values to remove any indirection chains.
*/
private final void updateOrdinalValues () {
for (int i = 0, n = ordinalValues.length; i < n; ++i) {
RTValue ordinalValue = ordinalValues[i];
if (ordinalValue instanceof RTResultFunction) {
ordinalValues[i] = ordinalValue.getValue();
}
}
}
/**
* Update the textual values to remove any indirection chains.
*/
private final void updateTextualValues () {
for (int i = 0, n = textualValues.length; i < n; ++i) {
RTValue textualValue = textualValues[i];
if (textualValue instanceof RTResultFunction) {
textualValues[i] = textualValue.getValue();
}
}
}
@Override
public RTValue getNthValue(int n) {
if (n < ordinalValues.length) {
// Update the value to remove indirection chains.
RTValue ordinalValue = ordinalValues[n];
if (ordinalValue instanceof RTResultFunction) {
return (ordinalValues[n] = ordinalValue.getValue());
}
return ordinalValue;
}
n = n - ordinalValues.length;
// Update the value to remove indirection chains.
RTValue textualValue = textualValues[n];
if (textualValue instanceof RTResultFunction) {
return (textualValues[n] = textualValue.getValue());
}
return textualValue;
}
/** {@inheritDoc} */
@Override
public final CalValue debug_getChild(int childN) {
if (childN < ordinalValues.length) {
return ordinalValues[childN];
}
return textualValues[childN - ordinalValues.length];
}
@Override
public boolean hasOrdinalField(int ordinal) {
//if the ordinal is bigger than all the ordinals in ordinalNames, then we can dispense with binary search
if (ordinal > ordinalNames[ordinalNames.length - 1]) {
return false;
}
return Arrays.binarySearch(ordinalNames, ordinal) >= 0;
}
@Override
public boolean hasTextualField(String textualFieldName) {
return Arrays.binarySearch(textualNames, textualFieldName) >= 0;
}
@Override
public int indexOfField(String fieldName) {
if(FieldName.Ordinal.isValidCalSourceForm(fieldName)) {
int ordinal = fieldOrdinal(fieldName);
int index = Arrays.binarySearch(ordinalNames, ordinal);
if(index < 0) {
return -1;
}
return index;
} else {
int index = Arrays.binarySearch(textualNames, fieldName);
if(index < 0) {
return -1;
}
return getNOrdinalFields() + index;
}
}
@Override
public List<String> fieldNames() {
int nFields = getNFields();
String[] fieldNames = new String[nFields];
int nOrdinalFields = ordinalNames.length;
for (int i = 0; i < nOrdinalFields; ++i) {
fieldNames[i] = "#" + ordinalNames[i];
}
//copy the textual field names
System.arraycopy(textualNames, 0, fieldNames, nOrdinalFields, textualNames.length);
return Arrays.asList(fieldNames);
}
@Override
public List<RTValue> fieldValues() {
int nFields = getNFields();
RTValue[] fieldValues = new RTValue[nFields];
int nOrdinalFields = ordinalValues.length;
int nTextualFields = textualValues.length;
// Update the ordinal values to remove any indirection chains.
updateOrdinalValues();
// Update the textual values to remove any indirection chains.
updateTextualValues();
//copy the ordinal field values
System.arraycopy(ordinalValues, 0, fieldValues, 0, nOrdinalFields);
//copy the textual field values
System.arraycopy(textualValues, 0, fieldValues, nOrdinalFields, nTextualFields);
return Arrays.asList(fieldValues);
}
/** {@inheritDoc}*/
@Override
public FieldName getNthFieldName(int n) {
int nOrdinalFields = ordinalNames.length;
if (n < nOrdinalFields) {
return FieldName.makeOrdinalField(ordinalNames[n]);
}
return FieldName.make(textualNames[n - nOrdinalFields]);
}
@Override
OrdinalData getOrdinalData() {
// Update the ordinal values to remove any indirection chains.
updateOrdinalValues();
return new OrdinalData.Ordinal(ordinalNames, ordinalValues);
}
@Override
TextualData getTextualData() {
// Update the textual values to remove any indirection chains.
updateTextualValues();
return new TextualData(textualNames, textualValues);
}
@Override
OrdinalData retractTupleFields(int retractedTupleSize) {
// Update values to remove indirection chains.
updateOrdinalValues();
return RTRecordValue.retractFromOrdinalUsingTuple(ordinalNames, ordinalValues, retractedTupleSize);
}
@Override
OrdinalData retractOrdinalFields(int[] retractedOrdinalNames) {
// Update values to remove indirection chains.
updateOrdinalValues();
return RTRecordValue.retractFromOrdinalUsingOrdinal(ordinalNames, ordinalValues, retractedOrdinalNames);
}
@Override
TextualData retractTextualFields(String[] retractedTextualNames) {
// Update values to remove indirection chains.
updateTextualValues();
return RTRecordValue.retractFromTextualUsingTextual(textualNames, textualValues, retractedTextualNames);
}
@Override
OrdinalData mergeTupleFields(RTValue[] extensionOrdinalValues) {
// Update values to remove indirection chains.
updateOrdinalValues();
return RTRecordValue.mergeOrdinalWithTuple(ordinalNames, ordinalValues, extensionOrdinalValues);
}
@Override
OrdinalData mergeOrdinalFields(int[] extensionOrdinalNames, RTValue[] extensionOrdinalValues) {
// Update values to remove indirection chains.
updateOrdinalValues();
return RTRecordValue.mergeOrdinalWithOrdinal(ordinalNames, ordinalValues, extensionOrdinalNames, extensionOrdinalValues);
}
@Override
TextualData mergeTextualFields(String[] extensionTextualNames, RTValue[] extensionTextualValues) {
// Update values to remove indirection chains.
updateTextualValues();
return RTRecordValue.mergeTextualWithTextual(textualNames, textualValues, extensionTextualNames, extensionTextualValues);
}
/** {@inheritDoc}*/
@Override
public int getNOrdinalFields() {
return ordinalValues.length;
}
/** {@inheritDoc}*/
@Override
public int getNthOrdinalFieldName(int n) {
return ordinalNames[n];
}
/** {@inheritDoc}*/
@Override
public RTValue getNthOrdinalValue(int n) {
// Update the value to remove any indirection chains.
RTValue ordinalValue = ordinalValues[n];
if (ordinalValue instanceof RTResultFunction) {
return (ordinalValues[n] = ordinalValue.getValue());
}
return ordinalValue;
}
/** {@inheritDoc}*/
@Override
public int getNTextualFields() {
return textualNames.length;
}
/** {@inheritDoc}*/
@Override
public String getNthTextualFieldName(int n) {
return textualNames[n];
}
/** {@inheritDoc}*/
@Override
public RTValue getNthTextualValue(int n) {
// Update the value to remove any indirection chains.
RTValue textualValue = textualValues[n];
if (textualValue instanceof RTResultFunction) {
return (textualValues[n] = textualValue.getValue());
}
return textualValue;
}
/** {@inheritDoc} */
@Override
public boolean hasTupleOrdinalPart() {
return false;
}
/** {@inheritDoc} */
@Override
public RTRecordValue appendRecord(RTRecordValue otherRecord) {
OrdinalData ordinalData = overlappingMergeOrdinalData(getOrdinalData(), otherRecord.getOrdinalData());
TextualData textualData = overlappingMergeTextualData(getTextualData(), otherRecord.getTextualData());
return makeRecordFromFieldData(ordinalData, textualData);
}
/** {@inheritDoc} */
@Override
public boolean sameFields(RecordType otherRecordType) {
if(!(otherRecordType instanceof MixedRecord)) {
return false;
}
MixedRecord otherMixedRecord = (MixedRecord)otherRecordType;
return Arrays.equals(otherMixedRecord.ordinalNames, ordinalNames)
&& Arrays.equals(otherMixedRecord.textualNames, textualNames);
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateOrdinalField(int ordinal, RTValue fieldValue) {
int index = Arrays.binarySearch(ordinalNames, ordinal);
RTValue[] newOrdinalValues = ordinalValues.clone();
newOrdinalValues[index] = fieldValue;
return new MixedRecord(ordinalNames, newOrdinalValues, textualNames, textualValues);
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateTextualField(String textualFieldName, RTValue fieldValue) {
int index = Arrays.binarySearch(textualNames, textualFieldName);
RTValue[] newTextualValues = textualValues.clone();
newTextualValues[index] = fieldValue;
return new MixedRecord(ordinalNames, ordinalValues, textualNames, newTextualValues);
}
/** {@inheritDoc} */
@Override
public RTRecordValue updateMixedOrdinalField(int ordinal, RTValue fieldValue) {
int index = Arrays.binarySearch(ordinalNames, ordinal);
RTValue[] newOrdinalValues = ordinalValues.clone();
newOrdinalValues[index] = fieldValue;
// We want to make a new array to hold textual values in the new record. This
// allows the new records textual values to be mutated without affecting this
// record.
RTValue[] newTextualValues = new RTValue[textualValues.length];
System.arraycopy(textualValues, 0, newTextualValues, 0, newTextualValues.length);
return new MixedRecord(ordinalNames, newOrdinalValues, textualNames, newTextualValues);
}
/** {@inheritDoc} */
@Override
public RTRecordValue mutateOrdinalField(int ordinal, RTValue fieldValue) {
int index = Arrays.binarySearch(ordinalNames, ordinal);
ordinalValues[index] = fieldValue;
return this;
}
/** {@inheritDoc} */
@Override
public RTRecordValue mutateTextualField(String textualFieldName, RTValue fieldValue) {
int index = Arrays.binarySearch(textualNames, textualFieldName);
textualValues[index] = fieldValue;
return this;
}
/**
* @see org.openquark.cal.internal.runtime.lecc.RTValue#apply(org.openquark.cal.internal.runtime.lecc.RTValue)
*/
@Override
public RTValue apply(RTValue argument) {
//The meaning of application to a record value is pointwise application i.e.
//{field1 = f1, field2 = f2, ..., fieldk = fk} x == {field1 = f1 x, field2 = f2 x, ... fieldk = fk x}
//In normal circumstances, record values cannot be applied to values because the resulting types are
//not typeable in CAL source. However, this situation does arise in the case of hidden dictionary switching.
//
//For example:
//
//{field1 = dictOrdInt, field2 = dictOrdString} 0
//is changed to
//{field1 = dictOrdInt 0, field2 = dictOrdString 0}
//(which latter reduces to {field1 = dictEqInt, field2 = dictEqString})
//
//in other words, the Int value being applied is applied pointwise to each field value.
int nOrdinalFields = getNOrdinalFields();
RTValue[] newOrdinalValues = new RTValue[nOrdinalFields];
for (int i = 0; i < nOrdinalFields; ++i) {
newOrdinalValues[i] = getNthOrdinalValue(i).apply(argument);
}
int nTextualFields = getNTextualFields();
RTValue[] newTextualValues = new RTValue[nTextualFields];
for (int i = 0; i < nTextualFields; ++i) {
newTextualValues[i] = getNthTextualValue(i).apply(argument);
}
return new MixedRecord(this.ordinalNames, newOrdinalValues, this.textualNames, newTextualValues);
}
}
public static RTRecordValue makeTupleRecord(RTValue[] ordinalValues) {
return new TupleRecord(ordinalValues);
}
public static RTRecordValue makeOrdinalRecord(int[] ordinalNames, RTValue[] ordinalValues) {
return new OrdinalRecord(ordinalNames, ordinalValues);
}
public static RTRecordValue makeTextualRecord(String[] textualNames, RTValue[] textualValues) {
return new TextualRecord(textualNames, textualValues);
}
public static RTRecordValue makeTupleMixedRecord(RTValue[] ordinalValues, String[] textualNames, RTValue[] textualValues) {
return new TupleMixedRecord(ordinalValues, textualNames, textualValues);
}
public static RTRecordValue makeMixedRecord(int[] ordinalNames, RTValue[] ordinalValues, String[] textualNames, RTValue[] textualValues) {
return new MixedRecord(ordinalNames, ordinalValues, textualNames, textualValues);
}
/**
* Makes a new RTRecordValue having the same field names as this record value, but with the values obtained
* from ordinalValues and textualValues, which are assumed to be properly ordered by FieldName ordering.
* @param ordinalValues should be null if there are no ordinal values
* @param textualValues should be null if there are no textual values
* @return RTRecordValue
*/
abstract public RTRecordValue makeFromValues(RTValue[] ordinalValues, RTValue[] textualValues);
abstract public RTValue getTextualFieldValue(String textualFieldName);
abstract public RTValue getOrdinalFieldValue(int ordinal);
/**
* @return the number of fields that are in this record. This includes textual as well as ordinal fields.
*/
abstract public int getNFields();
/**
* Note that the fields of the record are ordered in field-name order i.e.
* ordinal fields first, in numeric order, followed by textual fields in alphabetical order.
* @param n
* @return the field name of the nth field in the record as a String e.g. "#1" or "orderDate".
*/
abstract public FieldName getNthFieldName(int n);
/**
* Returns the value of the field at index n, but also has the side effect of updating the record
* at index n to remove the top-most indirection chain. This is similar to field accessors for data
* constructors.
* @param n zero-based index into the fields of the record
* @return the RTValue corresponding to the field at index n.
*/
abstract public RTValue getNthValue(int n);
/**
* @return true if the record has consecutive ordinal fields #1, #2, ... #n, where n >= 1. The record
* may or may not have textual fields.
*/
abstract public boolean hasTupleOrdinalPart();
public final boolean hasOrdinalFields() {
return getNOrdinalFields() > 0;
}
/**
* @return the number of ordinal fields in this record
*/
abstract public int getNOrdinalFields();
/**
* @param n zero-based index into the ordinal fields
* @return the ordinal field name of the nth ordinal field
*/
abstract public int getNthOrdinalFieldName(int n);
/**
* @param n zero-based index into the ordinal fields
* @return the value corresponding to the ordinal field at index n.
*/
abstract public RTValue getNthOrdinalValue(int n);
/**
* @return true if this record has 1 or more textual fields
*/
public final boolean hasTextualFields() {
return getNTextualFields() > 0;
}
abstract public int getNTextualFields();
abstract public String getNthTextualFieldName(int n);
abstract public RTValue getNthTextualValue(int n);
/**
* @return boolean. True if the record has the fields #1, #2, ..., #n, with no gaps, n >= 2
* and there are no other fields.
*/
public boolean isTuple2OrMoreRecord() {
return false;
}
/**
* Used to implement the primitive Prelude.hasField function.
* @param calSourceFieldName fieldName as a String, e.g. "#2", "#454", "orderDate" etc.
* @return boolean whether this record has a mapping for the given ordinal.
*/
public final boolean hasField(String calSourceFieldName) {
//todoBI we should be able to implement hasField more efficiently in the
//case that the fieldName is given in literal form
//e.g. hasField r "#2" should be inlined directly at compile-time to
//hasOrdinalField r 2.
FieldName fieldName = FieldName.make(calSourceFieldName);
if (fieldName == null) {
return false;
}
if (fieldName instanceof FieldName.Textual) {
return hasTextualField(calSourceFieldName);
}
return hasOrdinalField(((FieldName.Ordinal)fieldName).getOrdinal());
}
/**
* Used to implement the primitive Prelude.hasField function.
* @param ordinal must be a valid ordinal i.e. >= 1.
* @return boolean whether this record has a mapping for the given ordinal.
*/
abstract public boolean hasOrdinalField(int ordinal);
/**
* Used to implement the primitive Prelude.hasField function.
* @param textualFieldName
* @return boolean whether this record has a mapping for the given textualFieldName.
*/
abstract public boolean hasTextualField(String textualFieldName);
/**
* Used to implement the primitive Prelude.recordFieldIndex function.
* @param fieldName String name of field
* @return int 0-based index of the specified field if this record has the specified field, or -1 otherwise
*/
abstract public int indexOfField(String fieldName);
/**
* Used to implement the primitive Prelude.fieldNamesPrimitive function.
*
* @return List (of Strings) the sorted list of fieldNames. The sort order is as specified by
* the FieldName.CalSourceFormComparator comparator i.e. ordinal field names before textual field names.
*/
abstract public List<String> fieldNames();
/**
* Used to implement the primitive Prelude.fieldValuesPrimitive function.
*
* @return List (of RTValue) the list of field values, sorted in field-name order.
*/
abstract public List<RTValue> fieldValues();
public final RTRecordValue makeTupleRecordRetraction(int retractedTupleSize) {
assert (retractedTupleSize != 0) :
"Illegal argument in TupleRecord.makeTupleRecordRetraction.";
return RTRecordValue.makeRecordFromFieldData(
retractTupleFields(retractedTupleSize),
getTextualData());
}
public final RTRecordValue makeOrdinalRecordRetraction(int[] retractedOrdinalNames) {
int nRetractedOrdinals = retractedOrdinalNames.length;
assert (retractedOrdinalNames[nRetractedOrdinals - 1] != nRetractedOrdinals) :
"Illegal argument in TupleRecord.makeOrdinalRecordRetraction.";
return RTRecordValue.makeRecordFromFieldData(
retractOrdinalFields(retractedOrdinalNames),
getTextualData());
}
public final RTRecordValue makeTextualRecordRetraction(String[] retractedTextualNames) {
assert (retractedTextualNames.length != 0) :
"Illegal argument in TupleRecord.makeTextualRecordRetraction.";
return RTRecordValue.makeRecordFromFieldData(
getOrdinalData(),
retractTextualFields(retractedTextualNames));
}
public final RTRecordValue makeTupleMixedRecordRetraction(int retractedTupleSize, String[] retractedTextualNames) {
assert (retractedTupleSize != 0 &&
retractedTextualNames.length != 0) :
"Illegal argument in TupleRecord.makeTupleMixedRecordRetraction.";
return RTRecordValue.makeRecordFromFieldData(
retractTupleFields(retractedTupleSize),
retractTextualFields(retractedTextualNames));
}
public final RTRecordValue makeMixedRecordRetraction(int[] retractedOrdinalNames, String[] retractedTextualNames) {
int nRetractedOrdinals = retractedOrdinalNames.length;
assert (retractedOrdinalNames[nRetractedOrdinals - 1] != nRetractedOrdinals &&
retractedTextualNames.length != 0) :
"Illegal argument in TupleRecord.makeMixedRecordRetraction.";
return RTRecordValue.makeRecordFromFieldData(
retractOrdinalFields(retractedOrdinalNames),
retractTextualFields(retractedTextualNames));
}
public final RTRecordValue makeTupleRecordExtension(RTValue[] extensionOrdinalValues) {
assert RTRecordValue.verifyTupleData(extensionOrdinalValues) :
"Invalid tuple data in RTRecordValue.makeTupleRecordExtension().";
return RTRecordValue.makeRecordFromFieldData(
mergeTupleFields(extensionOrdinalValues),
getTextualData());
}
public final RTRecordValue makeOrdinalRecordExtension(int[] extensionOrdinalNames, RTValue[] extensionOrdinalValues) {
assert RTRecordValue.verifyOrdinalData(extensionOrdinalNames, extensionOrdinalValues) :
"Invalid ordinal data in RTRecordValue.makeOrdinalRecordExtension().";
return RTRecordValue.makeRecordFromFieldData(
mergeOrdinalFields(extensionOrdinalNames, extensionOrdinalValues),
getTextualData());
}
public final RTRecordValue makeTextualRecordExtension(String[] extensionTextualNames, RTValue[] extensionTextualValues) {
assert RTRecordValue.verifyTextualData(extensionTextualNames, extensionTextualValues) :
"Invalid textual data in RTRecordValue.makeTextualRecordExtension().";
return RTRecordValue.makeRecordFromFieldData(
getOrdinalData(),
mergeTextualFields(extensionTextualNames, extensionTextualValues));
}
public final RTRecordValue makeTupleMixedRecordExtension(RTValue[] extensionOrdinalValues,
String[] extensionTextualNames, RTValue[] extensionTextualValues) {
assert RTRecordValue.verifyTupleData(extensionOrdinalValues) :
"Invalid tuple data in RTRecordValue.makeTupleMixedRecordExtension().";
assert RTRecordValue.verifyTextualData(extensionTextualNames, extensionTextualValues) :
"Invalid textual data in RTRecordValue.makeTupleMixedRecordExtension().";
return RTRecordValue.makeRecordFromFieldData(
mergeTupleFields(extensionOrdinalValues),
mergeTextualFields(extensionTextualNames, extensionTextualValues));
}
public final RTRecordValue makeMixedRecordExtension(int[] extensionOrdinalNames, RTValue[] extensionOrdinalValues,
String[] extensionTextualNames, RTValue[] extensionTextualValues) {
assert RTRecordValue.verifyOrdinalData(extensionOrdinalNames, extensionOrdinalValues) : "Invalid ordinal data in RTRecordValue.makeMixedRecordExtension().";
assert RTRecordValue.verifyTextualData(extensionTextualNames, extensionTextualValues) : "Invalid textual data in RTRecordValue.makeMixedRecordExtension().";
return RTRecordValue.makeRecordFromFieldData(
mergeOrdinalFields(extensionOrdinalNames, extensionOrdinalValues),
mergeTextualFields(extensionTextualNames, extensionTextualValues));
}
/**
* Non-destructive update of this RTRecordValue at a specified ordinal field name.
* Note that an exception will be thrown if the record does not have a field with the
* given ordinal field name.
* @param ordinal
* @param fieldValue
* @return a copy of this RTRecordValue with the ordinal field updated to hold 'fieldValue'.
*/
public abstract RTRecordValue updateOrdinalField(int ordinal, RTValue fieldValue);
/**
* Non-destructive update of this RTRecordValue at a specified ordinal field name.
* Note that an exception will be thrown if the record does not have a field with the
* given ordinal field name.
* This function differs from updateOrdinalField in that it allows for subseqent
* mutation of any textual fields.
*
* @param ordinal
* @param fieldValue
* @return a copy of this RTRecordValue with the ordinal field updated to hold 'fieldValue'.
*/
public abstract RTRecordValue updateMixedOrdinalField(int ordinal, RTValue fieldValue);
/**
* Non-destructive update of this RTRecordValue at a specified textual field name.
* Note that an exception will be thrown if the record does not have a field with the
* given textual field name.
* @param textualFieldName
* @param fieldValue
* @return a copy of this RTRecordValue with the textual field updated to hold 'fieldValue'.
*/
public abstract RTRecordValue updateTextualField(String textualFieldName, RTValue fieldValue);
/**
* Destructive update of this RTRecordValue at a specified ordinal field name.
* Note that an exception will be thrown if the record does not have a field with the
* given ordinal field name.
* @param ordinal
* @param fieldValue
* @return this RTRecordValue with the ordinal field updated to hold 'fieldValue' as a side-effect.
*/
public abstract RTRecordValue mutateOrdinalField(int ordinal, RTValue fieldValue);
/**
* Destructive update of this RTRecordValue at a specified textual field name.
* Note that an exception will be thrown if the record does not have a field with the
* given textual field name.
* @param textualFieldName
* @param fieldValue
* @return this RTRecordValue with the textual field updated to hold 'fieldValue' as a side-effect.
*/
public abstract RTRecordValue mutateTextualField(String textualFieldName, RTValue fieldValue);
/**
* Return a new record containing this record's fields plus the specified ordinal field.
* If this record already has the given ordinal field name, then it is replaced.
* @param ordinal the ordinal field name of the field to insert
* @param fieldValue Value to set the field to
* @return The new record with the new field value
*/
public RTRecordValue insertOrdinalField(int ordinal, RTValue fieldValue) {
if(ordinal == 1) {
if(hasOrdinalField(ordinal)) {
return updateOrdinalField(ordinal, fieldValue);
} else {
return RTRecordValue.makeRecordFromFieldData(mergeTupleFields(new RTValue[] {fieldValue}), getTextualData());
}
} else {
if(hasOrdinalField(ordinal)) {
return updateOrdinalField(ordinal, fieldValue);
} else {
return RTRecordValue.makeRecordFromFieldData(mergeOrdinalFields(new int[] {ordinal}, new RTValue[] {fieldValue}), getTextualData());
}
}
}
/**
* Return a new record containing this record's fields plus the specified textual field.
* If this record already has a field named 'textualFieldName', then it is replaced.
* @param textualFieldName name of the textual field to insert
* @param fieldValue Value to set the field to
* @return The new record with the new field value
*/
public RTRecordValue insertTextualField(String textualFieldName, RTValue fieldValue) {
if (hasTextualField(textualFieldName)) {
return updateTextualField(textualFieldName, fieldValue);
}
return RTRecordValue.makeRecordFromFieldData(getOrdinalData(), mergeTextualFields(new String[] {textualFieldName}, new RTValue[] {fieldValue}));
}
/**
* Return a new record containing this record's fields plus those of another record.
* Any fields that both records contain will be set to the value in this record rather
* than those of the other record (i.e., this record "wins").
* @param otherRecord The other record to copy fields from
* @return The new record containing the fields of both source records
*/
abstract public RTRecordValue appendRecord(RTRecordValue otherRecord);
/** {@inheritDoc} */
public final RecordType appendRecordType(RecordType otherRecordType) {
return appendRecord((RTRecordValue)otherRecordType);
}
/** {@inheritDoc} */
public final RecordType insertRecordTypeField(String fieldName, Object type) {
if(FieldName.Ordinal.isValidCalSourceForm(fieldName)) {
return insertOrdinalField(fieldOrdinal(fieldName), (RTValue)type);
} else {
return insertTextualField(fieldName, (RTValue)type);
}
}
/**
* {@inheritDoc}
*/
public CalValue getNthFieldValue(int n) {
return getNthValue(n);
}
/**
* {@inheritDoc}
*/
abstract public boolean sameFields(RecordType otherType);
/**
* Merge two OrdinalData objects, returning a new OrdinalData object containing the fields of both.
* The two objects may contain overlapping field names; in such cases, values are takes from the
* primaryOrdinalData object.
* @param primaryOrdinalData OrdinalData
* @param otherOrdinalData OrdinalData
* @return A new, merged OrdinalData object
*/
private static OrdinalData overlappingMergeOrdinalData(OrdinalData primaryOrdinalData, OrdinalData otherOrdinalData) {
if(!otherOrdinalData.hasOrdinalFields()) {
return primaryOrdinalData;
}
if(!primaryOrdinalData.hasOrdinalFields()) {
return otherOrdinalData;
}
final int nPrimaryFields = primaryOrdinalData.getNOrdinalNames();
final int nOtherFields = otherOrdinalData.getNOrdinalNames();
int nExtensionFields = nOtherFields;
// Step through once to determine how many fields will be extended (skipping all fields that already
// exist in the primary field data)
int primaryIdx = 0;
int otherIdx = 0;
while (primaryIdx < nPrimaryFields && otherIdx < nOtherFields) {
int primaryName = primaryOrdinalData.getNthOrdinalName(primaryIdx);
int otherName = otherOrdinalData.getNthOrdinalName(otherIdx);
if(primaryName < otherName) {
primaryIdx++;
} else if(otherName < primaryName) {
otherIdx++;
} else {
nExtensionFields--;
primaryIdx++;
otherIdx++;
}
}
// No non-conflicting names to extend with, so return unextended data
if(nExtensionFields == 0) {
return primaryOrdinalData;
}
// No conflicts, so extend with all the provided data
if(nExtensionFields == nOtherFields) {
if(otherOrdinalData.isTuple()) {
return mergeOrdinalWithTuple(primaryOrdinalData.getOrdinalNames(), primaryOrdinalData.getOrdinalValues(), otherOrdinalData.getOrdinalValues());
} else if(primaryOrdinalData.isTuple()) {
return mergeTupleWithOrdinal(primaryOrdinalData.getOrdinalValues(), otherOrdinalData.getOrdinalNames(), otherOrdinalData.getOrdinalValues());
} else {
return mergeOrdinalWithOrdinal(primaryOrdinalData.getOrdinalNames(), primaryOrdinalData.getOrdinalValues(), otherOrdinalData.getOrdinalNames(), otherOrdinalData.getOrdinalValues());
}
}
// Step through again setting up the data to extend with
RTValue[] otherValues = otherOrdinalData.getOrdinalValues();
int[] extensionNames = new int[nExtensionFields];
RTValue[] extensionValues = new RTValue[nExtensionFields];
int extensionIdx = 0;
primaryIdx = 0;
for (otherIdx = 0; otherIdx < nOtherFields; otherIdx++) {
// Scan forward in the primary-names list until the next potential conflict
while(primaryIdx < nPrimaryFields &&
primaryOrdinalData.getNthOrdinalName(primaryIdx) < otherOrdinalData.getNthOrdinalName(otherIdx)) {
primaryIdx++;
}
// Copy non-conflicting names and values
if(primaryIdx >= nPrimaryFields ||
otherOrdinalData.getNthOrdinalName(otherIdx) != primaryOrdinalData.getNthOrdinalName(primaryIdx)) {
extensionNames[extensionIdx] = otherOrdinalData.getNthOrdinalName(otherIdx);
extensionValues[extensionIdx] = otherValues[otherIdx];
extensionIdx++;
}
}
if (primaryOrdinalData.isTuple()) {
return mergeTupleWithOrdinal(primaryOrdinalData.getOrdinalValues(), extensionNames, extensionValues);
} else if(extensionNames[extensionNames.length - 1] == extensionNames.length) {
return mergeOrdinalWithTuple(primaryOrdinalData.getOrdinalNames(), primaryOrdinalData.getOrdinalValues(), extensionValues);
} else {
return mergeOrdinalWithOrdinal(primaryOrdinalData.getOrdinalNames(), primaryOrdinalData.getOrdinalValues(), extensionNames, extensionValues);
}
}
/**
* Merge two TextualData objects, returning a new TextualData object containing the fields of both.
* The two objects may contain overlapping field names; in such cases, values are takes from the
* primaryTextualData object.
* @param primaryTextualData TextualData
* @param otherTextualData TextualData
* @return A new, merged TextualData object
*/
private static TextualData overlappingMergeTextualData(TextualData primaryTextualData, TextualData otherTextualData) {
if(!otherTextualData.hasTextualFields()) {
return primaryTextualData;
}
if(!primaryTextualData.hasTextualFields()) {
return otherTextualData;
}
int nExtensionFields = otherTextualData.getTextualNames().length;
String[] primaryNames = primaryTextualData.getTextualNames();
String[] otherNames = otherTextualData.getTextualNames();
// Step through once to determine how many fields will be extended (skipping all fields that already
// exist in the primary field data)
int primaryIdx = 0;
int otherIdx = 0;
while(primaryIdx < primaryNames.length && otherIdx < otherNames.length) {
int comparison = primaryNames[primaryIdx].compareTo(otherNames[otherIdx]);
if(comparison < 0) {
primaryIdx++;
} else if(comparison > 0) {
otherIdx++;
} else {
nExtensionFields--;
primaryIdx++;
otherIdx++;
}
}
// No non-conflicting names to extend with, so return unextended data
if(nExtensionFields == 0) {
return primaryTextualData;
}
// No conflicts, so extend with all the provided data
if(nExtensionFields == otherNames.length) {
return mergeTextualWithTextual(primaryNames, primaryTextualData.getTextualValues(), otherNames, otherTextualData.getTextualValues());
}
// Step through again setting up the data to extend with
RTValue[] otherValues = otherTextualData.getTextualValues();
String[] extensionNames = new String[nExtensionFields];
RTValue[] extensionValues = new RTValue[nExtensionFields];
int extensionIdx = 0;
primaryIdx = 0;
for(otherIdx = 0; otherIdx < otherNames.length; otherIdx++) {
// Scan forward in the primary-names list until the next potential conflict
while(primaryIdx < primaryNames.length) {
if(primaryNames[primaryIdx].compareTo(otherNames[otherIdx]) >= 0) {
break;
}
primaryIdx++;
}
// Copy non-conflicting names and values
if(primaryIdx >= primaryNames.length || !otherNames[otherIdx].equals(primaryNames[primaryIdx])) {
extensionNames[extensionIdx] = otherNames[otherIdx];
extensionValues[extensionIdx] = otherValues[otherIdx];
extensionIdx++;
}
}
return mergeTextualWithTextual(primaryNames, primaryTextualData.getTextualValues(), extensionNames, extensionValues);
}
private static RTRecordValue makeRecordFromFieldData(OrdinalData ordinalData, TextualData textualData) {
if (ordinalData.hasOrdinalFields()) {
if (textualData.hasTextualFields()) {
if (ordinalData.isTuple()) {
return new TupleMixedRecord(ordinalData.getOrdinalValues(), textualData.textualNames, textualData.textualValues);
}
return new MixedRecord(ordinalData.getOrdinalNames(), ordinalData.getOrdinalValues(), textualData.textualNames, textualData.textualValues);
}
if (ordinalData.isTuple()) {
return new TupleRecord(ordinalData.getOrdinalValues());
}
return new OrdinalRecord(ordinalData.getOrdinalNames(), ordinalData.getOrdinalValues());
}
if (textualData.hasTextualFields()) {
return new TextualRecord(textualData.textualNames, textualData.textualValues);
}
return EMPTY_RECORD;
}
/**
* @param fieldName String an ordinal field name
* @return int The ordinal of the field name as an int
*/
private static int fieldOrdinal(String fieldName) {
return Integer.parseInt(fieldName.substring(1));
}
abstract OrdinalData getOrdinalData();
abstract TextualData getTextualData();
abstract OrdinalData retractTupleFields(int retractedTupleSize);
abstract OrdinalData retractOrdinalFields(int[] retractedOrdinalNames);
abstract TextualData retractTextualFields(String[] retractedTextualNames);
abstract OrdinalData mergeTupleFields(RTValue[] extensionOrdinalValues);
abstract OrdinalData mergeOrdinalFields(int[] extensionOrdinalNames, RTValue[] extensionOrdinalValues);
abstract TextualData mergeTextualFields(String[] extensionTextualNames, RTValue[] extensionTextualValues);
private static OrdinalData retractFromOrdinalUsingTuple(int[] ordinalNames, RTValue[] ordinalValues, int retractedTupleSize) {
//ordinalNames must be of the form
//[1, 2, 3, ..., retractedTupleSize, m1, m2, ..., mk]
//so we just lop off the first retractedTupleSize elements from ordinalNames and ordinalValues to get the retraction.
int nNewOrdinalFields = ordinalNames.length - retractedTupleSize;
int[] newOrdinalNames = new int[nNewOrdinalFields];
RTValue[] newOrdinalValues = new RTValue[nNewOrdinalFields];
System.arraycopy(ordinalNames, retractedTupleSize, newOrdinalNames, 0, nNewOrdinalFields);
System.arraycopy(ordinalValues, retractedTupleSize, newOrdinalValues, 0, nNewOrdinalFields);
return new OrdinalData.Ordinal(newOrdinalNames, newOrdinalValues);
}
private static OrdinalData retractFromOrdinalUsingOrdinal(int[] ordinalNames, RTValue[] ordinalValues, int[] retractedOrdinalNames) {
int nRetractedFields = retractedOrdinalNames.length;
int nNewOrdinalFields = ordinalNames.length - retractedOrdinalNames.length;
if (nNewOrdinalFields == 0) {
//retraction results in an empty ordinal part
return OrdinalData.EMPTY;
}
//if ordinalName = [1, 2, 3, 4, 7, 8] and retractedOrdinalNames = [7, 8] then the result is a tuple.
//we can efficiently check this beforehand and avoid allocating newOrdinalNames.
if (ordinalNames[nNewOrdinalFields - 1] == nNewOrdinalFields && retractedOrdinalNames[0] > nNewOrdinalFields) {
RTValue[] newOrdinalValues = new RTValue[nNewOrdinalFields];
System.arraycopy(ordinalValues, 0, newOrdinalValues, 0, nNewOrdinalFields);
return new OrdinalData.Tuple(newOrdinalValues);
}
int[] newOrdinalNames = new int[nNewOrdinalFields];
RTValue[] newOrdinalValues = new RTValue[nNewOrdinalFields];
int ordinalIndex = 0;
int retractedOrdinalIndex = 0;
for (int i = 0; i < nNewOrdinalFields; ++i) {
while(retractedOrdinalIndex < nRetractedFields &&
ordinalNames[ordinalIndex] == retractedOrdinalNames[retractedOrdinalIndex]) {
++ordinalIndex;
++retractedOrdinalIndex;
}
newOrdinalNames[i] = ordinalNames[ordinalIndex];
newOrdinalValues[i] = ordinalValues[ordinalIndex];
++ordinalIndex;
}
return new OrdinalData.Ordinal(newOrdinalNames, newOrdinalValues);
}
private static OrdinalData retractFromTupleUsingOrdinal(RTValue[] ordinalValues, int[] retractedOrdinalNames) {
int nRetractedFields = retractedOrdinalNames.length;
int nNewOrdinalFields = ordinalValues.length - nRetractedFields;
//nNewOrdinalFields will be > 0 because retractedOrdinalNames do not form a tuple
//if ordinalName = [1, 2, 3, 4, 5, 6, 7, 8] and retractedOrdinalNames = [7, 8] then the result is a tuple.
//we can efficiently check this beforehand and avoid allocating newOrdinalNames.
if (retractedOrdinalNames[0] == nNewOrdinalFields + 1) {
RTValue[] newOrdinalValues = new RTValue[nNewOrdinalFields];
System.arraycopy(ordinalValues, 0, newOrdinalValues, 0, nNewOrdinalFields);
return new OrdinalData.Tuple(newOrdinalValues);
}
int[] newOrdinalNames = new int[nNewOrdinalFields];
RTValue[] newOrdinalValues = new RTValue[nNewOrdinalFields];
int ordinalIndex = 0;
int retractedOrdinalIndex = 0;
for (int i = 0; i < nNewOrdinalFields; ++i) {
while(retractedOrdinalIndex < nRetractedFields &&
ordinalIndex + 1 == retractedOrdinalNames[retractedOrdinalIndex]) {
++ordinalIndex;
++retractedOrdinalIndex;
}
newOrdinalNames[i] = ordinalIndex + 1;
newOrdinalValues[i] = ordinalValues[ordinalIndex];
++ordinalIndex;
}
return new OrdinalData.Ordinal(newOrdinalNames, newOrdinalValues);
}
private static OrdinalData retractFromTupleUsingTuple(RTValue[] ordinalValues, int retractedTupleSize) {
//ordinalNames must be [1, 2, 3, ..., ordinalValue.length] and 0 < retractedTupleSize < ordinalValues.length
int nNewOrdinalFields = ordinalValues.length - retractedTupleSize;
if(nNewOrdinalFields == 0) {
return OrdinalData.EMPTY;
}
int[] newOrdinalNames = new int[nNewOrdinalFields];
RTValue[] newOrdinalValues = new RTValue[nNewOrdinalFields];
for (int i = 0; i < nNewOrdinalFields; ++i) {
newOrdinalNames[i] = retractedTupleSize + i + 1;
}
System.arraycopy(ordinalValues, retractedTupleSize, newOrdinalValues, 0, nNewOrdinalFields);
return new OrdinalData.Ordinal(newOrdinalNames, newOrdinalValues);
}
private static TextualData retractFromTextualUsingTextual(String[] textualNames, RTValue[] textualValues, String[] retractedTextualNames) {
int nRetractedFields = retractedTextualNames.length;
int nNewTextualFields = textualNames.length - nRetractedFields;
if (nNewTextualFields == 0) {
//retraction results in an empty textual part
return TextualData.EMPTY;
}
String[] newTextualNames = new String[nNewTextualFields];
RTValue[] newTextualValues = new RTValue[nNewTextualFields];
int textualIndex = 0;
int retractedTextualIndex = 0;
for (int i = 0; i < nNewTextualFields; ++i) {
while(retractedTextualIndex < nRetractedFields &&
textualNames[textualIndex].equals(retractedTextualNames[retractedTextualIndex])) {
++textualIndex;
++retractedTextualIndex;
}
newTextualNames[i] = textualNames[textualIndex];
newTextualValues[i] = textualValues[textualIndex];
++textualIndex;
}
return new TextualData(newTextualNames, newTextualValues);
}
private static OrdinalData mergeOrdinalWithTuple(int[] ordinalNames, RTValue[] ordinalValues, RTValue[] extensionOrdinalValues) {
return RTRecordValue.mergeTupleWithOrdinal(extensionOrdinalValues, ordinalNames, ordinalValues);
}
private static OrdinalData mergeTupleWithOrdinal(RTValue[] ordinalValues, int[] extensionOrdinalNames, RTValue[] extensionOrdinalValues) {
assert (ordinalValues.length > 0 && extensionOrdinalValues.length > 0);
final int nOrdinalFields = ordinalValues.length;
final int nExtensionFields = extensionOrdinalValues.length;
//ordinalNames are [1, 2, 3, ..., nOrdinalFields]
//so extensionOrdinalNames must all be > nOrdinalFields
//in particular,
//newOrdinalNames = ordinalNames ++ extensionOrdinalNames
//newOrdinalValues = ordinalValues ++ extensionOrdinalValues
final int nNewOrdinalFields = nOrdinalFields + nExtensionFields;
RTValue[] newOrdinalValues = new RTValue[nNewOrdinalFields];
System.arraycopy(ordinalValues, 0, newOrdinalValues, 0, nOrdinalFields);
System.arraycopy(extensionOrdinalValues, 0, newOrdinalValues, nOrdinalFields, nExtensionFields);
if (extensionOrdinalNames[nExtensionFields - 1] == nNewOrdinalFields) {
//in this case,
//extensionOrdinalNames = [1, 2, 3, ..., nNewOrdinalFields]
//i.e. the extension has a tuple ordinal part.
return new OrdinalData.Tuple(newOrdinalValues);
}
int[] newOrdinalNames = new int[nNewOrdinalFields];
for (int i = 0; i < nOrdinalFields; ++i) {
newOrdinalNames[i] = i + 1;
}
System.arraycopy(extensionOrdinalNames, 0, newOrdinalNames, nOrdinalFields, nExtensionFields);
return new OrdinalData.Ordinal(newOrdinalNames, newOrdinalValues);
}
private static OrdinalData mergeOrdinalWithOrdinal(int[] ordinalNames, RTValue[] ordinalValues, int[] extensionOrdinalNames, RTValue[] extensionOrdinalValues) {
final int nOrdinalFields = ordinalNames.length;
final int nExtensionFields = extensionOrdinalNames.length;
assert (nOrdinalFields > 0 && nExtensionFields > 0);
final int nNewOrdinalFields = nOrdinalFields + nExtensionFields;
//handle the situation where after the extension, we have a tuple.
//For example if ordinalNames = [1, 3, 5] and extensionOrdinalNames = [2, 4]. Then after the extension it is
//[1, 2, 3, 4, 5]
if (Math.max(ordinalNames[nOrdinalFields -1], extensionOrdinalNames[nExtensionFields - 1]) == nNewOrdinalFields) {
RTValue[] newOrdinalValues = new RTValue[nNewOrdinalFields];
int thisIndex = 0;
int extensionIndex = 0;
for (int i = 0; i < nNewOrdinalFields; ++i) {
if (thisIndex < nOrdinalFields) {
if (extensionIndex < nExtensionFields) {
if (ordinalNames[thisIndex] < extensionOrdinalNames[extensionIndex]) {
newOrdinalValues[i] = ordinalValues[thisIndex];
++thisIndex;
} else {
newOrdinalValues[i] = extensionOrdinalValues[extensionIndex];
++extensionIndex;
}
} else {
//remaining items are from (ordinalNames, ordinalValues) starting at thisIndex
final int nRemaining = nNewOrdinalFields - i;
System.arraycopy(ordinalValues, thisIndex, newOrdinalValues, i, nRemaining);
break;
}
} else {
//remaining items are from (extensionOrdinalNames, extensionOrdinalValues) starting at extensionIndex
final int nRemaining = nNewOrdinalFields - i;
System.arraycopy(extensionOrdinalValues, extensionIndex, newOrdinalValues, i, nRemaining);
break;
}
}
return new OrdinalData.Tuple(newOrdinalValues);
}
final int[] newOrdinalNames = new int[nNewOrdinalFields];
final RTValue[] newOrdinalValues = new RTValue[nNewOrdinalFields];
int thisIndex = 0;
int extensionIndex = 0;
for (int i = 0; i < nNewOrdinalFields; ++i) {
if (thisIndex < nOrdinalFields) {
if (extensionIndex < nExtensionFields) {
int thisName = ordinalNames[thisIndex];
int extensionName = extensionOrdinalNames[extensionIndex];
if (thisName < extensionName) {
newOrdinalNames[i] = thisName;
newOrdinalValues[i] = ordinalValues[thisIndex];
++thisIndex;
} else {
newOrdinalNames[i] = extensionName;
newOrdinalValues[i] = extensionOrdinalValues[extensionIndex];
++extensionIndex;
}
} else {
//remaining items are from (ordinalNames, ordinalValues) starting at thisIndex
final int nRemaining = nNewOrdinalFields - i;
System.arraycopy(ordinalNames, thisIndex, newOrdinalNames, i, nRemaining);
System.arraycopy(ordinalValues, thisIndex, newOrdinalValues, i, nRemaining);
break;
}
} else {
//remaining items are from (extensionOrdinalNames, extensionOrdinalValues) starting at extensionIndex
final int nRemaining = nNewOrdinalFields - i;
System.arraycopy(extensionOrdinalNames, extensionIndex, newOrdinalNames, i, nRemaining);
System.arraycopy(extensionOrdinalValues, extensionIndex, newOrdinalValues, i, nRemaining);
break;
}
}
return new OrdinalData.Ordinal(newOrdinalNames, newOrdinalValues);
}
private static TextualData mergeTextualWithTextual(String[] textualNames, RTValue[] textualValues, String[] extensionTextualNames, RTValue[] extensionTextualValues) {
final int nTextualFields = textualNames.length;
final int nExtensionFields = extensionTextualNames.length;
assert (nTextualFields > 0 && nExtensionFields > 0);
final int nNewTextualFields = nTextualFields + nExtensionFields;
String[] newTextualNames = new String[nNewTextualFields];
RTValue[] newTextualValues = new RTValue[nNewTextualFields];
int thisIndex = 0;
int extensionIndex = 0;
for (int i = 0; i < nNewTextualFields; ++i) {
if (thisIndex < nTextualFields) {
if (extensionIndex < nExtensionFields) {
String thisName = textualNames[thisIndex];
String extensionName = extensionTextualNames[extensionIndex];
if (thisName.compareTo(extensionName) < 0) {
newTextualNames[i] = thisName;
newTextualValues[i] = textualValues[thisIndex];
++thisIndex;
} else {
newTextualNames[i] = extensionName;
newTextualValues[i] = extensionTextualValues[extensionIndex];
++extensionIndex;
}
} else {
//remaining items are from (textualNames, textualValues) starting at thisIndex
final int nRemaining = nNewTextualFields - i;
System.arraycopy(textualNames, thisIndex, newTextualNames, i, nRemaining);
System.arraycopy(textualValues, thisIndex, newTextualValues, i, nRemaining);
break;
}
} else {
//remaining items are from (extensionTextualNames, extensionTextualValues) starting at extensionIndex
final int nRemaining = nNewTextualFields - i;
System.arraycopy(extensionTextualNames, extensionIndex, newTextualNames, i, nRemaining);
System.arraycopy(extensionTextualValues, extensionIndex, newTextualValues, i, nRemaining);
break;
}
}
return new TextualData(newTextualNames, newTextualValues);
}
/**
* A helper function that does an inexpensive consistency check for the (ordinalNames, ordinalValues) pair
* to ensure that we are instantiating the right RTRecordValue derived class. This is important for efficiency
* reasons since using the wrong class can in certain cases only be detected by a slowdown in runtime performance
* which might be hard to notice without systematic benchmarking.
*
* Terminates in a runtime exception unless:
* -ordinalNames and ordinalValues are both non-null and have the same length which is > 0.
* -ordinalNames is not of the form [1, 2, 3, ..., ordinalNames.length - 1] i.e. should be using
* a Tuple class where ordinalNames is elided. This is done via an inexpensive check on the last element value.
* -ordinalNames and ordinalValues are both non-null
* @param ordinalNames names of ordinal fields, in ascending ordinal order.
* @param ordinalValues
* @return true if the ordinal data is valid, false otherwise.
*/
static boolean verifyOrdinalData (int[] ordinalNames, RTValue[] ordinalValues) {
final int nOrdinalFields = ordinalNames.length;
if (nOrdinalFields == 0 ||
nOrdinalFields != ordinalValues.length ||
ordinalNames[nOrdinalFields - 1] == nOrdinalFields) {
return false;
}
return true;
}
/**
* A helper function that does an inexpensive consistency check for the ordinalValues data for a tuple
* to ensure that we are instantiating the right RTRecordValue derived class. This is important for efficiency
* reasons since using the wrong class can in certain cases only be detected by a slowdown in runtime performance
* which might be hard to notice without systematic benchmarking.
*
* Terminates in a runtime exception unless:
* -ordinalValues is non-null and has length > 0.
* @param ordinalValues
* @return true if the tuple data is valid, false otherwise.
*/
static boolean verifyTupleData (RTValue[] ordinalValues) {
if (ordinalValues.length == 0) {
return false;
}
return true;
}
/**
* A helper function that does an inexpensive consistency check for the (textualNames, textualValues) pair
* to ensure that we are instantiating the right RTRecordValue derived class.
*
* @param textualNames
* @param textualValues
* @return true if the textual data is valid, false otherwise
*/
static boolean verifyTextualData (String[] textualNames, RTValue[] textualValues) {
final int nTextualFields = textualNames.length;
if (nTextualFields == 0 ||
nTextualFields != textualValues.length) {
return false;
}
return true;
}
/* (non-Javadoc)
* @see org.openquark.cal.internal.runtime.lecc.RTValue#reduce(org.openquark.cal.internal.runtime.lecc.RTExecutionContext)
*/
@Override
protected final RTValue reduce(RTExecutionContext ec) {
return this;
}
/**
* {@inheritDoc}
* Return an application of deepSeq to subParts.
* @param deepSeq
* @param rhs
* @return the graph representing the result of the deepSeq application.
* @throws CALExecutorException
*/
@Override
public final RTValue buildDeepSeq(RTSupercombinator deepSeq, RTValue rhs) throws CALExecutorException {
RTValue result = this;
for (int i = getNFields() - 1; i >= 0; --i) {
result = deepSeq.apply(getNthValue(i)).apply(rhs);
rhs = result;
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public final int debug_getNChildren() {
return getNFields();
}
/**
* We can't just call getNthValue() since getNthValue has the side effect of
* attempting to shorten indirection chains and the debug_ methods cannot
* have any side effects.
* {@inheritDoc}
*/
@Override
public abstract CalValue debug_getChild(int childN);
/**
* {@inheritDoc}
*/
@Override
public String debug_getNodeStartText() {
return "{";
}
/**
* {@inheritDoc}
*/
@Override
public String debug_getNodeEndText() {
return "}";
}
/**
* {@inheritDoc}
*/
@Override
public String debug_getChildPrefixText(int childN) {
StringBuilder sb = new StringBuilder();
if (childN > 0) {
sb.append(", ");
}
sb.append(getNthFieldName(childN));
sb.append(" = ");
return sb.toString();
}
/**
* @param ordinalValues must have length > 0.
* @return RTRecordValue a copy of this RTRecordValue with the fields with ordinal field names #1, #2, ...,#(ordinalValues.length)
* updated by their corresponding values in the ordinalValues array.
*/
public final RTRecordValue makeTupleRecordUpdate(RTValue[] ordinalValues) {
RTRecordValue updatedRecord = updateOrdinalField(1, ordinalValues[0]);
for (int i = 1, nValues = ordinalValues.length; i < nValues; ++i) {
updatedRecord.mutateOrdinalField(i + 1, ordinalValues[i]);
}
return updatedRecord;
}
/**
* @param ordinalNames must have length > 0 and equal to the length of ordinalValues.
* @param ordinalValues
* @return RTRecordValue a copy of this RTRecordValue with the the ordinal fields with the given ordinalNames updated
* with their corresponding ordinalValues
*/
public final RTRecordValue makeOrdinalRecordUpdate(int[] ordinalNames, RTValue[] ordinalValues) {
RTRecordValue updatedRecord = updateOrdinalField(ordinalNames[0], ordinalValues[0]);
for (int i = 1, nValues = ordinalValues.length; i < nValues; ++i) {
updatedRecord.mutateOrdinalField(ordinalNames[i], ordinalValues[i]);
}
return updatedRecord;
}
/**
* @param textualNames must have length > 0 and equal to the length of textualValues.
* @param textualValues
* @return a copy of this RTRecordValue with the the textual fields with the given textualNames updated
* with their corresponding textualValues
*/
public final RTRecordValue makeTextualRecordUpdate(String[] textualNames, RTValue[] textualValues) {
RTRecordValue updatedRecord = updateTextualField(textualNames[0], textualValues[0]);
for (int i = 1, nValues = textualValues.length; i < nValues; ++i) {
updatedRecord.mutateTextualField(textualNames[i], textualValues[i]);
}
return updatedRecord;
}
/**
* @param ordinalValues must have length > 0.
* @param textualNames must have length > 0 and equal to the length of textualValues.
* @param textualValues
* @return a copy of this RTRecordValue with the the textual fields with the given textualNames updated
* with their corresponding textualValues and the fields with ordinal field names #1, #2, ...,#(ordinalValues.length)
* updated by their corresponding values in the ordinalValues array.
*/
public final RTRecordValue makeTupleMixedRecordUpdate(RTValue[] ordinalValues, String[] textualNames, RTValue[] textualValues) {
RTRecordValue updatedRecord = updateMixedOrdinalField(1, ordinalValues[0]);
for (int i = 1, nValues = ordinalValues.length; i < nValues; ++i) {
updatedRecord.mutateOrdinalField(i + 1, ordinalValues[i]);
}
for (int i = 0, nValues = textualValues.length; i < nValues; ++i) {
updatedRecord.mutateTextualField(textualNames[i], textualValues[i]);
}
return updatedRecord;
}
/**
* @param ordinalNames must have length > 0 and equal to the length of ordinalValues.
* @param ordinalValues
* @param textualNames must have length > 0 and equal to the length of textualValues.
* @param textualValues
* @return a copy of this RTRecordValue with the the ordinal fields with the given ordinalNames updated
* with their corresponding ordinalValues and the textual fields with the given textualNames updated
* with their corresponding textualValues.
*/
public final RTRecordValue makeMixedRecordUpdate(int[] ordinalNames, RTValue[] ordinalValues, String[] textualNames, RTValue[] textualValues) {
RTRecordValue updatedRecord = updateMixedOrdinalField(ordinalNames[0], ordinalValues[0]);
for (int i = 1, nValues = ordinalValues.length; i < nValues; ++i) {
updatedRecord.mutateOrdinalField(ordinalNames[i], ordinalValues[i]);
}
for (int i = 0, nValues = textualValues.length; i < nValues; ++i) {
updatedRecord.mutateTextualField(textualNames[i], textualValues[i]);
}
return updatedRecord;
}
/**
* {@inheritDoc}
*/
@Override
public final DataType getDataType() {
return DataType.OTHER;
}
}