/*
* 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 java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.teiid.common.buffer.BufferManager;
import org.teiid.core.TeiidComponentException;
import org.teiid.core.TeiidProcessingException;
import org.teiid.logging.LogManager;
import org.teiid.logging.MessageLevel;
import org.teiid.query.mapping.xml.ResultSetInfo;
import org.teiid.query.processor.ProcessorDataManager;
import org.teiid.query.processor.ProcessorPlan;
import org.teiid.query.sql.symbol.GroupSymbol;
import org.teiid.query.util.CommandContext;
public class XMLProcessorEnvironment {
/** XML result documents should be in String form */
public static final String STRING_RESULT = "String"; //$NON-NLS-1$
/* Stack <ProgramState> */
private LinkedList<ProgramState> programStack = new LinkedList<ProgramState>();
private Set<String> loadedStagingTables = Collections.synchronizedSet(new HashSet<String>());
private DocumentInProgress documentInProgress;
private String xmlFormat;
private GroupSymbol documentGroup;
private ProcessorDataManager dataMgr;
private BufferManager bufferMgr;
private CommandContext commandContext;
protected XMLProcessorEnvironment(){
}
public XMLProcessorEnvironment(Program mainProgram, GroupSymbol docGroup){
pushProgram(mainProgram);
this.documentGroup = docGroup;
}
/**
* @see ProcessorEnvironment#start()
*/
public void initialize(CommandContext context, ProcessorDataManager dataMgr, BufferManager bufferMgr) {
this.dataMgr = dataMgr;
this.bufferMgr = bufferMgr;
this.commandContext = context;
}
/**
* An object to hold state about a Program. Programs are
* immutable, therefore their program counter needs to be held
* in this wrapper object.
*/
private static class ProgramState {
private Program program;
private int programCounter = 0;
private int lookaheadCounter;
private int recursionCount = NOT_RECURSIVE;
private static final int NOT_RECURSIVE = 0;
public String toString() {
return program.toString() + ", counter " + programCounter + ", recursionCount " + (recursionCount == NOT_RECURSIVE? "not recursive": "" + recursionCount); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
}
public Program getCurrentProgram() {
// jh case 5266
if ( this.programStack.size() > 0 ) {
ProgramState programState = this.programStack.getFirst();
return programState.program;
}
return null;
}
/**
* Indicates if there is a recursive program anywhere in the
* current program stack (not just at the top).
* @return whether there is a recursive program anywhere in the
* program stack
*/
public boolean isRecursiveProgramInStack() {
Iterator<ProgramState> stackedPrograms = this.programStack.iterator();
// Always at least one program in the stack
ProgramState programState = stackedPrograms.next();
while (programState.recursionCount == ProgramState.NOT_RECURSIVE && stackedPrograms.hasNext()) {
programState = stackedPrograms.next();
}
return (programState.recursionCount > ProgramState.NOT_RECURSIVE);
}
public void incrementCurrentProgramCounter() {
ProgramState programState = this.programStack.getFirst();
programState.programCounter++;
// Always leave one Program in the Program stack, even if it is finished
while (this.programStack.size() > 1 &&
programState.programCounter >= programState.program.getProcessorInstructions().size()) {
this.programStack.removeFirst();
if(LogManager.isMessageToBeRecorded(org.teiid.logging.LogConstants.CTX_XML_PLAN, MessageLevel.TRACE)) {
LogManager.logTrace(org.teiid.logging.LogConstants.CTX_XML_PLAN, new Object[]{"Processor Environment popped program w/ recursion count " + programState.recursionCount, "; " + this.programStack.size(), " programs left."}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
programState = this.programStack.getFirst();
}
}
public void pushProgram(Program program) {
pushProgram(program, false);
}
public void pushProgram(Program program, boolean isRecursive) {
ProgramState programState = new ProgramState();
programState.program = program;
if (isRecursive) {
ProgramState previousState = getProgramState(program);
if (previousState != null) {
programState.recursionCount = previousState.recursionCount + 1;
} else {
programState.recursionCount = ProgramState.NOT_RECURSIVE + 1;
}
LogManager.logTrace(org.teiid.logging.LogConstants.CTX_XML_PLAN, new Object[]{"Pushed recursive program w/ recursion count " + programState.recursionCount}); //$NON-NLS-1$
} else {
LogManager.logTrace(org.teiid.logging.LogConstants.CTX_XML_PLAN, new Object[]{"Pushed non-recursive program w/ recursion count " + programState.recursionCount}); //$NON-NLS-1$
}
this.programStack.addFirst(programState);
}
public ProcessorInstruction getCurrentInstruction(XMLContext context) throws TeiidComponentException, TeiidProcessingException {
ProgramState programState = this.programStack.getFirst();
//Case 5266: account for an empty program on to the stack;
//this is needed to handle an empty sequence or an excluded Choice properly.
if (programState != null && programState.program.getProcessorInstructions().isEmpty()) {
incrementCurrentProgramCounter();
programState = this.programStack.getFirst();
}
if (programState == null) {
return null;
}
//start all siblings
List<ProcessorInstruction> instrs = programState.program.getProcessorInstructions();
if (programState.programCounter >= programState.lookaheadCounter && instrs.size() > programState.programCounter + 1) {
for (programState.lookaheadCounter = programState.programCounter; programState.lookaheadCounter < instrs.size(); programState.lookaheadCounter++) {
ProcessorInstruction pi = instrs.get(programState.lookaheadCounter);
boolean staging = false;
if (pi instanceof ExecStagingTableInstruction) {
staging = true;
ExecStagingTableInstruction esti = (ExecStagingTableInstruction)pi;
if (!esti.info.isAutoStaged()) {
//need to load staging tables prior to source queries
break;
}
}
if (pi instanceof ExecSqlInstruction) {
ExecSqlInstruction esi = (ExecSqlInstruction)pi;
if (!staging && esi.info.isAutoStaged() && esi.info.getTempTable() == null) {
continue; //derived load
}
PlanExecutor pe = esi.getPlanExecutor(this, context);
pe.execute(context.getReferenceValues(), true);
}
}
}
return programState.program.getInstructionAt(programState.programCounter);
}
public int getProgramRecursionCount(Program program){
ProgramState programState = getProgramState(program);
if (programState == null) {
return ProgramState.NOT_RECURSIVE;
}
return programState.recursionCount;
}
private ProgramState getProgramState(Program program) {
ProgramState result = null;
Iterator<ProgramState> stackedPrograms = this.programStack.iterator();
while (stackedPrograms.hasNext()) {
ProgramState programState = stackedPrograms.next();
Program stackedProgram = programState.program;
if (stackedProgram == program) {
result = programState;
break;
}
}
return result;
}
public PlanExecutor createResultExecutor(ResultSetInfo info) {
// cloning the plan inside the resultset is not possible
// because of the dependencies.
ResultSetInfo clone = (ResultSetInfo)info.clone();
ProcessorPlan plan = clone.getPlan();
plan = plan.clone();
clone.setPlan(plan);
return new RelationalPlanExecutor(clone, this.commandContext, this.dataMgr, this.bufferMgr);
}
public DocumentInProgress getDocumentInProgress() {
return this.documentInProgress;
}
public void setDocumentInProgress(DocumentInProgress documentInProgress) {
this.documentInProgress = documentInProgress;
}
public String getXMLFormat() {
return this.xmlFormat;
}
public void setXMLFormat(String xmlFormat) {
this.xmlFormat = xmlFormat;
}
public ProcessorDataManager getDataManager() {
return this.dataMgr;
}
public CommandContext getProcessorContext() {
return this.commandContext;
}
public XMLProcessorEnvironment clone() {
XMLProcessorEnvironment clone = new XMLProcessorEnvironment();
copyIntoClone(clone);
return clone;
}
/**
* Utility method to copy cloned state into newly-instantiated
* (empty) clone. Clone will appear as if it were reset.
* @param clone new but empty
*/
protected void copyIntoClone(XMLProcessorEnvironment clone) {
// Programs - just get the one at the bottom of the stack
ProgramState initialProgramState = this.programStack.getLast();
ProgramState newState = new ProgramState();
newState.program = initialProgramState.program;
clone.programStack.addFirst(newState);
// XML results form and format
clone.setXMLFormat(this.getXMLFormat());
}
public GroupSymbol getDocumentGroup() {
return this.documentGroup;
}
boolean isStagingTableLoaded(String tableName) {
return this.loadedStagingTables.contains(tableName);
}
void markStagingTableAsLoaded(String tableName) {
this.loadedStagingTables.add(tableName);
}
public BufferManager getBufferManager() {
return bufferMgr;
}
}