/**
* Copyright (C) 2010 Orbeon, Inc.
*
* This program 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 program 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.
*
* The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
*/
package org.orbeon.oxf.processor.sql.interpreters;
import org.orbeon.oxf.common.ValidationException;
import org.orbeon.oxf.processor.sql.SQLFunctionLibrary;
import org.orbeon.oxf.processor.sql.SQLProcessor;
import org.orbeon.oxf.processor.sql.SQLProcessorInterpreterContext;
import org.orbeon.oxf.xml.DeferredXMLReceiver;
import org.orbeon.oxf.xml.DeferredXMLReceiverImpl;
import org.orbeon.oxf.xml.SAXStore;
import org.orbeon.oxf.xml.dom4j.LocationData;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class RowIteratorInterpreter extends SQLProcessor.InterpreterContentHandler {
private DeferredXMLReceiver savedOutput;
private boolean hiding;
private int rowNum = 1;
private int groupCount = 0;
private List groups;
public RowIteratorInterpreter(SQLProcessorInterpreterContext interpreterContext) {
// Repeating interpreter
super(interpreterContext, true);
setForward(true);
}
public void start(String uri, String localname, String qName, Attributes attributes) throws SAXException {
addAllDefaultElementHandlers();
final SQLProcessorInterpreterContext interpreterContext = getInterpreterContext();
final ResultSet resultSet = interpreterContext.getResultSet();
try {
boolean hasNext = !interpreterContext.isEmptyResultSet();
if (SQLProcessor.logger.isDebugEnabled())
SQLProcessor.logger.debug("Preparing to execute row: " +
"statement = " + interpreterContext.getStatementSHA() + ", " +
"hasNext = " + hasNext);
// Iterate through the result set
while (hasNext) {
final SQLFunctionLibrary.SQLFunctionContext functionContextOrNull = interpreterContext.getFunctionContextOrNull();
interpreterContext.pushFunctionContext(
new SQLFunctionLibrary.SQLFunctionContext(
functionContextOrNull == null ? null : functionContextOrNull.currentNode(),
rowNum,
functionContextOrNull == null ? null : functionContextOrNull.getColumn()
)
);
try {
// Output footers that need it
if (groups != null) {
for (int i = groups.size() - 1; i >= 0; i--) {
Group g1 = (Group) groups.get(i);
if (columnChanged(resultSet, groups, i)) {
g1.getFooter().replay(interpreterContext.getOutput());
g1.getFooter().clear();
}
}
groupCount = 0;
}
if (SQLProcessor.logger.isDebugEnabled())
SQLProcessor.logger.debug("Execute row: " +
"statement = " + interpreterContext.getStatementSHA() + ", " +
"rowNum = " + rowNum);
// Interpret row
repeatBody();
} finally {
interpreterContext.popFunctionContext();
}
// Go to following row
hasNext = resultSet.next();
rowNum++;
}
final SQLFunctionLibrary.SQLFunctionContext functionContextOrNull = interpreterContext.getFunctionContextOrNull();
interpreterContext.pushFunctionContext(
new SQLFunctionLibrary.SQLFunctionContext(
functionContextOrNull == null ? null : functionContextOrNull.currentNode(),
rowNum,
functionContextOrNull == null ? null : functionContextOrNull.getColumn()
)
);
try {
// Output last footers
if (groups != null) {
for (int i = groups.size() - 1; i >= 0; i--) {
Group group = (Group) groups.get(i);
group.getFooter().replay(interpreterContext.getOutput());
group.getFooter().clear();
}
}
} finally {
interpreterContext.popFunctionContext();
}
} catch (Exception e) {
throw new ValidationException(e, new LocationData(getDocumentLocator()));
}
}
public void end(String uri, String localname, String qName) throws SAXException {
// Restore state
hiding = false;
rowNum = 1;
groupCount = 0;
groups = null;
}
public void startPrefixMapping(String prefix, String uri) throws SAXException {
super.startPrefixMapping(prefix, uri);
getInterpreterContext().declarePrefix(prefix, uri);
}
public void startElement(String uri, String localname, String qName, Attributes attributes) throws SAXException {
if (!isInElementHandler() && SQLProcessor.SQL_NAMESPACE_URI.equals(uri)) {
// if (SQLProcessor.SQL_NAMESPACE_URI.equals(uri)) {
if (localname.equals("group")) {
if (groups == null)
groups = new ArrayList();
try {
ResultSet resultSet = getInterpreterContext().getResultSet();
// Save group information if first row
if (rowNum == 1) {
final String columnName = (attributes.getValue("column-name") != null) ? attributes.getValue("column-name") : attributes.getValue("column");
groups.add(new Group(columnName, resultSet));
}
// Get current group information
Group currentGroup = (Group) groups.get(groupCount);
if (rowNum == 1 || columnChanged(resultSet, groups, groupCount)) {
// Need to display group's header and footer
currentGroup.setShowHeader(true);
hiding = false;
currentGroup.setColumnValue(resultSet);
} else {
// Hide group's header
currentGroup.setShowHeader(false);
hiding = true;
}
groupCount++;
} catch (SQLException e) {
throw new ValidationException(e, new LocationData(getDocumentLocator()));
}
} else if (localname.equals("member")) {
hiding = false;
} else if (!hiding) {
super.startElement(uri, localname, qName, attributes);
}
} else if (!hiding) {
super.startElement(uri, localname, qName, attributes);
}
}
public void endElement(String uri, String localname, String qName) throws SAXException {
if (!isInElementHandler() && SQLProcessor.SQL_NAMESPACE_URI.equals(uri)) {
// if (SQLProcessor.SQL_NAMESPACE_URI.equals(uri)) {
final SQLProcessorInterpreterContext interpreterContext = getInterpreterContext();
if (localname.equals("group")) {
groupCount--;
// Restore sending to the regular output
Group currentGroup = (Group) groups.get(groupCount);
if (currentGroup.isShowHeader())
interpreterContext.setOutput(savedOutput);
} else if (localname.equals("member")) {
Group currentGroup = (Group) groups.get(groupCount - 1);
// The first time, everything is sent to the footer SAXStore
if (currentGroup.isShowHeader()) {
savedOutput = interpreterContext.getOutput();
interpreterContext.setOutput(new DeferredXMLReceiverImpl(currentGroup.getFooter()));
hiding = false;
} else
hiding = true;
} else if (!hiding) {
super.endElement(uri, localname, qName);
}
} else if (!hiding) {
super.endElement(uri, localname, qName);
}
}
public void characters(char[] chars, int start, int length) throws SAXException {
if (!hiding) {
// Output only if the string is non-blank [FIXME: Incorrect white space handling!]
// String s = new String(chars, start, length);
// if (!StringUtils.trimAllToEmpty(s).equals(""))
super.characters(chars, start, length);
}
}
private boolean columnChanged(ResultSet resultSet, List groups, int level) throws SQLException {
for (int i = level; i >= 0; i--) {
Group group = (Group) groups.get(i);
if (group.columnChanged(resultSet))
return true;
}
return false;
}
private class Group {
private String columnName;
private String columnValue;
private SAXStore footer = new SAXStore();
private boolean showHeader;
public Group(String columnName, ResultSet resultSet) throws SQLException {
this.columnName = columnName;
this.columnValue = resultSet.getString(columnName);
}
public boolean columnChanged(ResultSet resultSet) throws SQLException {
String newValue = resultSet.getString(columnName);
return (columnValue != null && !columnValue.equals(newValue)) || (newValue != null && !newValue.equals(columnValue));
}
public void setColumnValue(ResultSet resultSet) throws SQLException {
this.columnValue = resultSet.getString(columnName);
}
public boolean isShowHeader() {
return showHeader;
}
public void setShowHeader(boolean showHeader) {
this.showHeader = showHeader;
}
public SAXStore getFooter() {
return footer;
}
}
}