/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.query.processor.xml;
import static org.teiid.query.analysis.AnalysisRecord.*;
import java.util.ArrayList;
import java.util.List;
import org.teiid.client.plan.PlanNode;
import org.teiid.common.buffer.BlockedException;
import org.teiid.core.TeiidComponentException;
import org.teiid.core.TeiidProcessingException;
import org.teiid.logging.LogManager;
/**
* <p>This instruction holds a List of Criteria, and for each Criteria there is a
* {@link Program} that will be executed. Therefore, this ProcessorInstruction
* implements an arbitrarily deep if-else if-....else block.</p>
*
* <p>During processing, the Criteria are iterated through, in order. When one
* evaluates to true, that Criteria's Program is placed on to the
* Program {@link ProcessorEnvironment#getProgramStack stack}, and the loop
* is broken - this simulates the if-else if-... behavior. An optional
* "else" or "default" Program can be added via the
* {@link #addDefaultChoiceSubProgram addDefaultChoiceSubProgram}
* method - this is the optional "else"
* block at the end of an if-else if-... construct.</p>
*/
public class IfInstruction extends ProcessorInstruction {
private List thenBlocks;
private DefaultCondition defaultCondition;
// State if condition evaluation blocked
private int conditionIndex = 0;
/**
* Constructor for IfInstruction.
* @param endIf see {@link #getEndIf}
*/
public IfInstruction() {
super();
this.thenBlocks = new ArrayList();
}
public void addCondition(Condition condition) {
thenBlocks.add(condition);
}
public void setDefaultCondition(DefaultCondition defaultCondition) {
this.defaultCondition = defaultCondition;
}
/**
* Return the number of "if/else if" clauses and
* corresponding "then" sub programs in this If Instruction.
* (Does not include the optional
* {@link #getElseProgram "else" sub program}).
* @return number of "then" sub programs
*/
int getThenCount(){
return this.thenBlocks.size();
}
/**
* Return the Criteria object representing the "if" or
* "else if" condition, at the indicated "then" index.
* Use {@link #getThenCount getThenCount} method to get the
* number of then sub programs.
* @param thenCount index into the ordered list of "then" sub programs
* @return Criteria for the sub program, or null if it is an invalid index
*/
Condition getThenCondition(int thenCount){
if (thenCount >=0 && thenCount < this.thenBlocks.size()){
return (Condition)this.thenBlocks.get(thenCount);
}
return null;
}
/**
* Return the sub Program object representing a "then" clause
* of this "if then else if..." block, at the indicated "then" index.
* Use {@link #getThenCount getThenCount} method to get the
* number of then sub programs.
* @param thenCount index into the ordered list of "then" sub programs
* @return the "thenCount"th "then" sub program, or null if it is an invalid index
*/
Program getThenProgram(int thenCount){
if (thenCount >=0 && thenCount < this.thenBlocks.size()){
Condition condition = (Condition)this.thenBlocks.get(thenCount);
return condition.getThenProgram();
}
return null;
}
/**
* Returns the optional "else" sub program, which has no criteria
* but is executed if no other "if/else if" condition evaluates to
* true.
* @return else Program, may be null
*/
Program getElseProgram(){
return this.defaultCondition.getThenProgram();
}
/**
* This instruction will evaluate it's criteria, one by one. If any evaluate
* to true, it will push the corresponding sub-program on to the top of the
* program stack, and break from the loop. Regardless if whether any criteria
* evaluate to true, this instruction will increment the program counter of the
* current program.
* @see ProcessorInstruction#process(ProcessorEnvironment)
*/
public XMLContext process(XMLProcessorEnvironment env, XMLContext context)
throws BlockedException, TeiidComponentException, TeiidProcessingException{
List thens = this.thenBlocks;
if (this.defaultCondition != null){
//add default choice to the end of the List - this will be
//the "else" at the end of the "if-then-else-if..." structure,
//and its rsNames will be null
//the original List must not be modified, so make a copy
thens = new ArrayList(thenBlocks);
thens.add(defaultCondition);
}
Condition condition = null;
boolean foundTrueCondition = false;
for(; conditionIndex < thens.size(); conditionIndex++){
condition = (Condition)thens.get(conditionIndex);
// evaluate may block if criteria evaluation blocks
if(condition.evaluate(env, context)) {
foundTrueCondition = true;
//break from the loop; only the first "then" Program
//whose criteria evaluates to true will be executed
break;
}
}
conditionIndex = 0;
// This IF instruction should be processed exactly once, so the
// program containing the IF instruction needs to have it's
// counter incremented.
// NOTE: At this point, if any of the conditions (above) evaluated
// to true, then a sub program will be pushed (below) onto the
// stack above this current program. That's okay. Still need to
// increment program counter so this IF instruction is only
// executed once
env.incrementCurrentProgramCounter();
if (foundTrueCondition) {
//push the "then" Program onto the stack
Program thenProgram = condition.getThenProgram();
env.pushProgram(thenProgram, condition.isProgramRecursive());
LogManager.logTrace(org.teiid.logging.LogConstants.CTX_XML_PLAN, new Object[]{"IF: true condition", condition, "- then program:", thenProgram}); //$NON-NLS-1$ //$NON-NLS-2$
}
return context;
}
public String toString() {
return "IF BLOCK:"; //$NON-NLS-1$
}
public PlanNode getDescriptionProperties() {
PlanNode props = new PlanNode("CHOICE"); //$NON-NLS-1$
for (int i = 0; i < this.thenBlocks.size(); i++) {
Condition condition = (Condition) thenBlocks.get(i);
PlanNode node = null;
if(condition instanceof RecurseProgramCondition) {
node = new PlanNode("RECURSIVE"); //$NON-NLS-1$
node.addProperty(PROP_CONDITION, condition.toString());
} else {
node = condition.getThenProgram().getDescriptionProperties();
node.addProperty(PROP_CONDITION, ((CriteriaCondition)condition).criteria.toString());
}
props.addProperty("Condition " + i, node); //$NON-NLS-1$
}
if (defaultCondition != null && defaultCondition.getThenProgram() != null){
props.addProperty(PROP_DEFAULT_PROGRAM, defaultCondition.getThenProgram().getDescriptionProperties());
}
return props;
}
}