/*
* 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.
*/
/*
* RTRecordUpdate.java
* Created: May 3, 2006
* By: Bo Ilic
*/
package org.openquark.cal.internal.runtime.lecc;
import org.openquark.cal.runtime.CALExecutorException;
import org.openquark.cal.runtime.CalValue;
/**
* Models record update e.g.
* {r | f1 := 10.0, f2 := "True", f3 := expr1 ++ expr2}
* in a lazy context.
*
* @author Bo Ilic
*/
public abstract class RTRecordUpdate extends RTResultFunction {
private RTValue baseRecordExpr;
/**
* Represents update by a tuple-record field set 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, {r | #1 := "abc", #2 := 100.0, #3 := True}
*
* @author Bo Ilic
*/
private static final class TupleUpdate extends RTRecordUpdate {
/**
* the ith element of ordinalValues is the field value corresponding
* to the ordinal field name #i. Will have positive length.
*/
private RTValue[] ordinalValues;
private TupleUpdate(RTValue baseRecord, RTValue[] ordinalValues) {
super(baseRecord);
assert RTRecordValue.verifyTupleData(ordinalValues) :
"Invalid tuple data in TupleUpdate constructor.";
this.ordinalValues = ordinalValues;
}
/* (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) throws CALExecutorException {
// Update and return result
if (super.baseRecordExpr != null) {
setResult(((RTRecordValue)super.baseRecordExpr.evaluate(ec)).makeTupleRecordUpdate(ordinalValues));
clearMembers();
}
return result;
}
/*
* (non-Javadoc)
* @see org.openquark.cal.internal.runtime.lecc.RTResultFunction#clearMembers()
*/
@Override
public void clearMembers () {
super.baseRecordExpr = null;
ordinalValues = null;
}
/** {@inheritDoc} */
@Override
public int getNFields() {
return ordinalValues.length;
}
/** {@inheritDoc} */
@Override
public String getNthFieldName(int n) {
return "#" + (n + 1);
}
/** {@inheritDoc} */
@Override
public RTValue getNthValue(int n) {
return ordinalValues[n];
}
}
/**
* Represents update by an ordinal record field set i.e.
* -all the fields in the record are ordinal fields
* -not a TupleRecord or the EmptyRecord.
*
* For example,
* {r | #1 = "abc", #3 = True} is an OrdinalUpdate
* {r | #2 = 100.0, #3 = True} is an OrdinalUpdate
* {r| #1 = "abc", #2 = 100.0, #3 = True} is a TupleUpdate and not an OrdinalUpdate.
*
* @author Bo Ilic
*/
private static final class OrdinalUpdate extends RTRecordUpdate {
/**
* the ordinal field names (as int values) in ascending ordinal order. Will have positive length
* and not be [1, 2, 3, ..., ordinalValues.length]
*/
private 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 RTValue[] ordinalValues;
private OrdinalUpdate(RTValue baseRecord, int[] ordinalNames, RTValue[] ordinalValues) {
super(baseRecord);
assert RTRecordValue.verifyOrdinalData(ordinalNames, ordinalValues) : "Invalid ordinal data in OrdinalUpdate().";
this.ordinalNames = ordinalNames;
this.ordinalValues = ordinalValues;
}
/* (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) throws CALExecutorException {
// Update and return result
if (super.baseRecordExpr != null) {
setResult(((RTRecordValue)super.baseRecordExpr.evaluate(ec)).makeOrdinalRecordUpdate(ordinalNames, ordinalValues));
clearMembers();
}
return result;
}
/*
* (non-Javadoc)
* @see org.openquark.cal.internal.runtime.lecc.RTResultFunction#clearMembers()
*/
@Override
public void clearMembers () {
super.baseRecordExpr = null;
ordinalNames = null;
ordinalValues = null;
}
/** {@inheritDoc} */
@Override
public int getNFields() {
return ordinalNames.length;
}
/** {@inheritDoc} */
@Override
public String getNthFieldName(int n) {
return "#" + ordinalNames[n];
}
/** {@inheritDoc} */
@Override
public RTValue getNthValue(int n) {
return ordinalValues[n];
}
}
/**
* Represents update by a textual records field set i.e.
* -all the fields in the record are textual fields
* -not the EmptyUpdate (i.e. there is at least 1 textual field).
*
* For example,
* {r | name = "abc", flag = True} is a TextualUpdate
*
* @author Bo Ilic
*/
private static final class TextualUpdate extends RTRecordUpdate {
/** the textual field names of the record, in ascending alphabetical order. Will have positive length. */
private 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 RTValue[] textualValues;
private TextualUpdate(RTValue baseRecord, String[] textualNames, RTValue[] textualValues) {
super(baseRecord);
assert RTRecordValue.verifyTextualData(textualNames, textualValues) :
"Invalid textual data in TextualUpdate constructor.";
this.textualNames = textualNames;
this.textualValues = textualValues;
}
/* (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) throws CALExecutorException {
// Update and return result
if (super.baseRecordExpr != null) {
setResult(((RTRecordValue)super.baseRecordExpr.evaluate(ec)).makeTextualRecordUpdate(textualNames, textualValues));
clearMembers();
}
return result;
}
/*
* (non-Javadoc)
* @see org.openquark.cal.internal.runtime.lecc.RTResultFunction#clearMembers()
*/
@Override
public void clearMembers () {
super.baseRecordExpr = null;
textualNames = null;
textualValues = null;
}
/** {@inheritDoc} */
@Override
public int getNFields() {
return textualNames.length;
}
/** {@inheritDoc} */
@Override
public String getNthFieldName(int n) {
return textualNames[n];
}
/** {@inheritDoc} */
@Override
public RTValue getNthValue(int n) {
return textualValues[n];
}
}
/**
* Represents update by a record field set 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,
* {r| #1 = 2.0, #2 = "FooBar", name = "abc", flag = True} is a TupleMixedUpdate
*
* @author Bo Ilic
*/
private static final class TupleMixedUpdate extends RTRecordUpdate {
/**
* the ith element of ordinalValues is the field value corresponding to
* the ordinal field name #i. Will have positive length.
*/
private RTValue[] ordinalValues;
/**
* the textual field names of the record, in ascending alphabetical
* order. Will have positive length.
*/
private 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 RTValue[] textualValues;
private TupleMixedUpdate(RTValue baseRecord, RTValue[] ordinalValues, String[] textualNames, RTValue[] textualValues) {
super(baseRecord);
assert RTRecordValue.verifyTupleData(ordinalValues) :
"Invalid tuple data in TupleMixedUpdate constructor.";
assert RTRecordValue.verifyTextualData(textualNames, textualValues) :
"Invalid textual data in TupleMixedUpdate constructor.";
this.ordinalValues = ordinalValues;
this.textualNames = textualNames;
this.textualValues = textualValues;
}
/* (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) throws CALExecutorException {
// Update and return result
if (super.baseRecordExpr != null) {
setResult(((RTRecordValue)super.baseRecordExpr.evaluate(ec)).makeTupleMixedRecordUpdate(ordinalValues, textualNames, textualValues));
clearMembers();
}
return result;
}
/*
* (non-Javadoc)
* @see org.openquark.cal.internal.runtime.lecc.RTResultFunction#clearMembers()
*/
@Override
public void clearMembers () {
super.baseRecordExpr = null;
ordinalValues = null;
textualNames = null;
textualValues = null;
}
/** {@inheritDoc} */
@Override
public int getNFields() {
return ordinalValues.length + textualNames.length;
}
/** {@inheritDoc} */
@Override
public String getNthFieldName(int n) {
int nOrdinalFields = ordinalValues.length;
if (n < nOrdinalFields) {
return "#" + (n + 1);
}
return textualNames[n - nOrdinalFields];
}
/** {@inheritDoc} */
@Override
public RTValue getNthValue(int n) {
if (n < ordinalValues.length) {
return ordinalValues[n];
}
return textualValues[n - ordinalValues.length];
}
}
/**
* Represents update by a record field set 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,
* {r | #1 = 2.0, #3 = "FooBar", name = "abc", flag = True} is a MixedUpdate
*
* @author Bo Ilic
*/
private static final class MixedUpdate extends RTRecordUpdate {
/**
* the ordinal field names (as int values) in ascending ordinal order.
* Will have positive length and not be [1, 2, 3, ...,
* ordinalValues.length]
*/
private 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 RTValue[] ordinalValues;
/**
* the textual field names of the record, in ascending alphabetical
* order. Will have positive length.
*/
private 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 RTValue[] textualValues;
private MixedUpdate(RTValue baseRecord, int[] ordinalNames, RTValue[] ordinalValues, String[] textualNames, RTValue[] textualValues) {
super(baseRecord);
assert RTRecordValue.verifyOrdinalData(ordinalNames, ordinalValues) : "Invalid ordinal data in MixedUpdate constructor.";
assert RTRecordValue.verifyTextualData(textualNames, textualValues) : "Invalid textual data in MixedUpdate constructor.";
this.ordinalNames = ordinalNames;
this.ordinalValues = ordinalValues;
this.textualNames = textualNames;
this.textualValues = textualValues;
}
/* (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) throws CALExecutorException {
// Update and return result
if (super.baseRecordExpr != null) {
setResult(((RTRecordValue)super.baseRecordExpr.evaluate(ec)).makeMixedRecordUpdate(ordinalNames, ordinalValues, textualNames, textualValues));
clearMembers();
}
return result;
}
/*
* (non-Javadoc)
* @see org.openquark.cal.internal.runtime.lecc.RTResultFunction#clearMembers()
*/
@Override
public void clearMembers () {
super.baseRecordExpr = null;
ordinalNames = null;
ordinalValues = null;
textualNames = null;
textualValues = null;
}
/** {@inheritDoc} */
@Override
public int getNFields() {
return ordinalNames.length + textualNames.length;
}
/** {@inheritDoc} */
@Override
public String getNthFieldName(int n) {
int nOrdinalFields = ordinalNames.length;
if (n < nOrdinalFields) {
return "#" + ordinalNames[n];
}
return textualNames[n - nOrdinalFields];
}
/** {@inheritDoc} */
@Override
public RTValue getNthValue(int n) {
if (n < ordinalValues.length) {
return ordinalValues[n];
}
return textualValues[n - ordinalValues.length];
}
}
private RTRecordUpdate(RTValue baseRecordExpr) {
assert (baseRecordExpr != null);
this.baseRecordExpr = baseRecordExpr;
}
public static RTRecordUpdate makeTupleRecordUpdate(RTValue baseRecordExpr, RTValue[] ordinalValues) {
return new TupleUpdate(baseRecordExpr, ordinalValues);
}
public static RTRecordUpdate makeOrdinalRecordUpdate(RTValue baseRecordExpr, int[] ordinalNames, RTValue[] ordinalValues) {
return new OrdinalUpdate(baseRecordExpr, ordinalNames, ordinalValues);
}
public static RTRecordUpdate makeTextualRecordUpdate(RTValue baseRecordExpr, String[] textualNames, RTValue[] textualValues) {
return new TextualUpdate(baseRecordExpr, textualNames, textualValues);
}
public static RTRecordUpdate makeTupleMixedRecordUpdate(RTValue baseRecordExpr, RTValue[] ordinalValues, String[] textualNames, RTValue[] textualValues) {
return new TupleMixedUpdate(baseRecordExpr, ordinalValues, textualNames, textualValues);
}
public static RTRecordUpdate makeMixedRecordUpdate(RTValue baseRecordExpr, int[] ordinalNames, RTValue[] ordinalValues, String[] textualNames, RTValue[] textualValues) {
return new MixedUpdate(baseRecordExpr, ordinalNames, ordinalValues, textualNames, textualValues);
}
/**
* @return the number of fields that we are updating by. This includes textual as well as ordinal fields.
*/
abstract public int getNFields();
/**
* Note that the fields of the record update 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 update as a String e.g. "#1" or "orderDate".
*/
abstract public String getNthFieldName(int n);
/**
* The value of the nth record update field, with the fields of the update ordered in field-name order.
* @param n
* @return RTValue
*/
abstract public RTValue getNthValue(int n);
/**
* {@inheritDoc}
*/
@Override
public final int debug_getNChildren() {
if (result != null) {
return super.debug_getNChildren();
}
return getNFields() + 1;
}
/**
* {@inheritDoc}
*/
@Override
public final CalValue debug_getChild(int childN) {
if (result != null) {
return super.debug_getChild(childN);
}
if (childN == 0) {
return baseRecordExpr;
}
return getNthValue(childN - 1);
}
/**
* {@inheritDoc}
*/
@Override
public final String debug_getNodeStartText() {
if (result != null) {
return super.debug_getNodeStartText();
}
return "{";
}
/**
* {@inheritDoc}
*/
@Override
public final String debug_getNodeEndText() {
if (result != null) {
return super.debug_getNodeEndText();
}
return "}";
}
/**
* {@inheritDoc}
*/
@Override
public final String debug_getChildPrefixText(int childN) {
if (result != null) {
return super.debug_getChildPrefixText(childN);
}
if (childN == 0) {
//prefix to the record being extended
return "";
}
String fieldName = getNthFieldName(childN - 1);
StringBuilder sb = new StringBuilder();
if (childN == 1) {
sb.append(" | ");
} else {
sb.append(", ");
}
sb.append(fieldName);
sb.append(" := ");
return sb.toString();
}
}