/*
* 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.
*/
/*
* NRecordUpdate.java
* Created: May 4, 2006
* By: Bo Ilic
*/
package org.openquark.cal.internal.machine.g;
import java.util.Collection;
import java.util.Iterator;
import org.openquark.cal.runtime.CALExecutorException;
import org.openquark.cal.runtime.CalValue;
/**
* Represents updating a record e.g.
* {r | field1 := e1, field2 := e2}
* in a lazy context i.e. where we don't want to evalaute r right away.
*
* @author Bo Ilic
*/
class NRecordUpdate extends NRecordValue {
/** Graph node representing the record being updated. */
private final Node baseRecord;
/** Code representing the actions of this node. */
private Code code;
/**
* Create a node representing the application of a virtual
* update function to a base record.
* @param baseRecord
*/
NRecordUpdate (Node baseRecord) {
super();
if (baseRecord == null) {
throw new NullPointerException();
}
this.baseRecord = baseRecord;
}
/* (non-Javadoc)
* @see org.openquark.cal.internal.runtime.g.Node#toString(int)
*/
@Override
public String toString(int n) {
StringBuilder is = new StringBuilder();
for (int i = 0; i < n; ++i) {
is.append(" ");
}
return is + idString(0);
}
/** {@inheritDoc} */
@Override
public boolean isTuple2OrMoreRecord() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public final int debug_getNChildren() {
if (hasIndirection()) {
return super.debug_getNChildren();
}
//1 for the record update
return super.debug_getNChildren() + 1;
}
/**
* {@inheritDoc}
*/
@Override
public final CalValue debug_getChild(int childN) {
if (hasIndirection()) {
return super.debug_getChild(childN);
}
if (childN == 0) {
return baseRecord;
}
return super.debug_getChild(childN - 1);
}
/**
* {@inheritDoc}
*/
@Override
public final String debug_getNodeStartText() {
if (hasIndirection()) {
return super.debug_getNodeStartText();
}
return "{";
}
/**
* {@inheritDoc}
*/
@Override
public final String debug_getNodeEndText() {
if (hasIndirection()) {
return super.debug_getNodeEndText();
}
return "}";
}
/**
* {@inheritDoc}
*/
@Override
public final String debug_getChildPrefixText(int childN) {
if (hasIndirection()) {
return super.debug_getChildPrefixText(childN);
}
if (childN < 0 || childN > fieldToValueMap.size()) {
throw new IndexOutOfBoundsException();
}
if (childN == 0) {
return "";
}
StringBuilder sb = new StringBuilder();
if (childN > 1) {
sb.append(", ");
} else {
sb.append(" | ");
}
String fieldName = getNthFieldName(childN - 1).getCalSourceForm();
sb.append(fieldName);
sb.append(" := ");
return sb.toString();
}
/* (non-Javadoc)
* @see org.openquark.cal.internal.runtime.g.Node#addChildren(java.util.Collection)
*/
@Override
protected void addChildren(Collection<Node> c) {
super.addChildren (c);
if (!c.contains(baseRecord)) {
c.add(baseRecord);
baseRecord.addChildren(c);
}
}
/* (non-Javadoc)
* @see org.openquark.cal.internal.runtime.g.Node#i_eval(org.openquark.cal.internal.runtime.g.Executor)
*/
@Override
protected void i_eval(Executor e) throws CALExecutorException {
e.pushDumpItem();
i_unwind (e);
}
/**
* Generate the code for the virtual function represented
* by this node.
* @return the code.
*/
private Code getCode () {
if (code == null) {
Instruction i[] = new Instruction [6 +(fieldToValueMap.size() * 2)];
int inst = 0;
// First evaluate the base record.
i[inst++] = new Instruction.I_Push(0);
i[inst++] = Instruction.I_Eval;
// Create a copy of the base record.
i[inst++] = new Instruction.I_ExtendRecord();
// Add the additional fields to the base record.
Iterator<String> it = fieldToValueMap.keySet().iterator();
int pos = 2;
while (it.hasNext ()) {
String fieldName = it.next();
i[inst++] = new Instruction.I_Push(pos++);
i[inst++] = new Instruction.I_PutRecordField(fieldName);
}
// Update with the result.
i[inst++] = new Instruction.I_Update(fieldToValueMap.size() + 1);
i[inst++] = new Instruction.I_Pop(fieldToValueMap.size() + 1);
i[inst] = Instruction.I_Unwind;
code = new Code(i);
}
return code;
}
/**
* Do Unwind state transition.
*/
@Override
protected void i_unwind (Executor e) throws CALExecutorException {
// Put the 'arguments' on the stack in the right order.
Iterator<String> it = fieldToValueMap.keySet().iterator();
Node[] nodes = new Node [fieldToValueMap.size()];
int pos = 0;
while (it.hasNext ()) {
nodes[pos++] = getValue(it.next());
}
for (int i = nodes.length - 1; i >= 0; --i) {
e.stack.push (nodes[i]);
}
e.stack.push (baseRecord);
// Set the instruction pointer.
e.setIP (getCode());
}
}