/*
* Copyright (C) 2000 - 2008 TagServlet Ltd
*
* This file is part of Open BlueDragon (OpenBD) CFML Server Engine.
*
* OpenBD is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* Free Software Foundation,version 3.
*
* OpenBD 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenBD. If not, see http://www.gnu.org/licenses/
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this Program, or any covered work, by linking or combining
* it with any of the JARS listed in the README.txt (or a modified version of
* (that library), containing parts covered by the terms of that JAR, the
* licensors of this Program grant you additional permission to convey the
* resulting work.
* README.txt @ http://www.openbluedragon.org/license/README.txt
*
* http://www.openbluedragon.org/
*/
package com.naryx.tagfusion.cfm.wddx;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Stack;
import java.util.TimeZone;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import com.nary.util.FastMap;
import com.nary.util.string;
import com.naryx.tagfusion.cfm.engine.cfArrayData;
import com.naryx.tagfusion.cfm.engine.cfBinaryData;
import com.naryx.tagfusion.cfm.engine.cfBooleanData;
import com.naryx.tagfusion.cfm.engine.cfData;
import com.naryx.tagfusion.cfm.engine.cfDateData;
import com.naryx.tagfusion.cfm.engine.cfNumberData;
import com.naryx.tagfusion.cfm.engine.cfQueryResultData;
import com.naryx.tagfusion.cfm.engine.cfSession;
import com.naryx.tagfusion.cfm.engine.cfStringData;
import com.naryx.tagfusion.cfm.engine.cfStructData;
import com.naryx.tagfusion.cfm.engine.cfmRunTimeException;
import com.naryx.tagfusion.cfm.engine.dataNotSupportedException;
/**
* Handler used in WDDX2CFML for handling SAX events
*/
public class wddxHandler extends DefaultHandler {
cfSession session;
String current;
String columnList[];
String varName = null;
String columnName = null;
wddxStruct struct = null;
String boolValue;
char[] globalBuffer;
int globalOffset, globalLen;
wddxArray array, innerArray, outerArray = null;
int rowNum = 0;
Stack context;
wddxQueryResult resultSet;
wddxStruct innerStruct, outerStruct;
StringBuilder content;
cfData result = null;
/**
* Constructor for the wddxHandler object
*
*@param _Session Description of Parameter
*@param _output Description of Parameter
*/
public wddxHandler( cfSession _Session ){
session = _Session;
context = new Stack();
content = new StringBuilder();
}
public cfData getResult(){
return result;
}
/**
* Description of the Method
*
*@param namespaceURI Description of Parameter
*@param eName Description of Parameter
*@param qName Description of Parameter
*@param attrs Description of Parameter
*@exception SAXException Description of Exception
*/
public void startElement( String namespaceURI, String eName, String qName, Attributes attrs ) throws SAXException {
current = qName.toLowerCase();
if ( !qName.equals( "char" ) ){
content.setLength( 0 );
rowNum++;
}
if ( qName.equals( "struct" ) ) {
struct = new wddxStruct( varName, new FastMap( FastMap.CASE_INSENSITIVE ) );
context.push( struct );
} else if ( qName.equals( "recordset" ) ) {
List<String> tokens = string.split( attrs.getValue( "fieldNames" ), "," );
this.columnList = new String[tokens.size()];
int counter = 0;
for ( int i = 0; i < tokens.size(); i++ ) {
columnList[counter] = tokens.get(i).toString();
counter++;
}
cfQueryResultData queryData = new cfQueryResultData( columnList, "SQL Query" );
int rowCount = com.nary.util.string.convertToInteger( attrs.getValue( "rowCount" ), 0 );
if ( rowCount > 0 )
queryData.addRow( rowCount );
resultSet = new wddxQueryResult( varName, queryData );
context.push( resultSet );
} else if ( qName.equals( "field" ) ) {
rowNum = 0;
columnName = attrs.getValue( "name" );
} else if ( qName.equals( "array" ) ) {
array = new wddxArray( varName, cfArrayData.createArray( 1 ) );
context.push( array );
} else if ( qName.equals( "var" ) ) {
varName = attrs.getValue( "name" );
} else if ( qName.equals( "boolean" ) ) {
boolValue = attrs.getValue( "value" );
if ( !context.empty() ) {
if ( context.peek() instanceof wddxStruct ) {
struct = ( wddxStruct ) context.pop();
struct.put( varName, cfBooleanData.getcfBooleanData( boolValue ) );
context.push( struct );
varName = null;
} else if ( context.peek() instanceof wddxArray ) {
outerArray = ( wddxArray ) context.pop();
try {
outerArray.addElement( cfBooleanData.getcfBooleanData( boolValue ) );
} catch (cfmRunTimeException ex) { throw new SAXException (ex); }
context.push( outerArray );
} else if ( context.peek() instanceof cfQueryResultData ) {
resultSet = ( wddxQueryResult ) context.pop();
resultSet.setCell( rowNum, columnName, cfBooleanData.getcfBooleanData( boolValue ) );
context.push( resultSet );
}
} else {
result = cfBooleanData.getcfBooleanData( boolValue );
}
} else if ( qName.equals( "char") ){
String code = attrs.getValue( "code" );
try{
int chCode = Integer.parseInt( code, 16 );
content.append( (char) chCode );
}catch( NumberFormatException e ){
throw new SAXException( new Exception( "Invalid character code specified in char element: " + code + ".") );
}
}else if ( qName.equals( "null" ) ){
// If current is not in the list of valid names then throw an exception.
}else {
if ( !current.equals( "wddxpacket" ) && !current.equals( "string" ) && !current.equals( "datetime" ) && !current.equals( "number" ) && !current.equals( "header" ) && !current.equals( "binary" ) && !current.equals( "datetime" ) && !current.equals( "data" ) ) {
throw new SAXException( new Exception( current + " is not a valid WDDX element." ) );
}
}
}
//--------------------------------------------------------------------------------
// Called everytime characters between tags are encountered
//--------------------------------------------------------------------------------
/**
* Description of the Method
*
*@param buf Description of Parameter
*@param offset Description of Parameter
*@param len Description of Parameter
*@exception SAXException Description of Exception
*/
public void characters( char buf[], int offset, int len ) {
globalBuffer = buf;
if ( !current.equals( "char" ) ){
content.append( buf, offset, len );
}
globalOffset = offset;
globalLen = len;
}
/**
* Description of the Method
*
*@param namespaceURI The Namespace URI, or the empty string if the element has no Namespace URI
* or if Namespace processing is not being performed.
*@param sName The local name (without prefix), or the empty string if Namespace processing is not being performed.
*@param qName The qualified XML 1.0 name (with prefix), or the empty string if qualified names are not available.
*@exception SAXException
*/
public void endElement( String namespaceURI, String sName, String qName ) throws SAXException {
current = qName.toLowerCase();
if ( globalBuffer != null ) {
getCharacterData( content );
globalBuffer = null;
} else {
if ( qName.equalsIgnoreCase( "string" ) || current.equals( "null" ) ) {
getCharacterData( content );
}
}
if ( qName.equalsIgnoreCase( "recordset" ) ) {
if ( !context.empty() ) {
resultSet = ( wddxQueryResult ) context.pop();
if ( !context.empty() ) {
if ( context.peek() instanceof wddxArray ) {
outerArray = ( wddxArray ) context.pop();
try {
outerArray.addElement( resultSet.getData() );
} catch (cfmRunTimeException ex) { throw new SAXException (ex); }
context.push( outerArray );
} else if ( context.peek() instanceof wddxStruct ) {
outerStruct = ( wddxStruct ) context.pop();
outerStruct.put( resultSet.getName(), resultSet.getData() );
context.push( outerStruct );
}
} else {
result = resultSet.getData();
}
}
}else if ( qName.equalsIgnoreCase( "array" ) ) {
if ( !context.empty() ) {
innerArray = ( wddxArray ) context.pop();
if ( !context.empty() ) {
if ( context.peek() instanceof wddxStruct ) {
outerStruct = ( wddxStruct ) context.pop();
outerStruct.put( innerArray.getName(), innerArray.getData() );
context.push( outerStruct );
} else if ( context.peek() instanceof wddxArray ) {
outerArray = ( wddxArray ) context.pop();
try {
outerArray.addElement( innerArray.getData() );
} catch (cfmRunTimeException ex) { throw new SAXException (ex); }
context.push( outerArray );
}
} else {
result = innerArray.getData();
}
}
}else if ( qName.equalsIgnoreCase( "struct" ) ) {
if ( !context.empty() ) {
innerStruct = ( wddxStruct ) context.pop();
if ( !context.empty() ) {
if ( context.peek() instanceof wddxStruct ) {
outerStruct = ( wddxStruct ) context.pop();
outerStruct.put( innerStruct.getName(), new cfStructData( innerStruct.getData() ) );
context.push( outerStruct );
} else if ( context.peek() instanceof wddxArray ) {
outerArray = ( wddxArray ) context.pop();
try {
outerArray.addElement( new cfStructData( innerStruct.getData() ) );
} catch (cfmRunTimeException ex) { throw new SAXException (ex); }
context.push( outerArray );
}
} else {
result = new cfStructData( innerStruct.getData() );
}
}
}else if ( qName.equalsIgnoreCase( "char" ) ) {
current = "string";
}
}
/**
* Gets the characterData attribute of the wddxHandler object
*
*@param buf Description of Parameter
*@exception SAXException Description of Exception
*/
private void getCharacterData( StringBuilder buf ) throws SAXException {
cfData data = null;
boolean usedData = false; // set to true if anything useful was obtained from the buffer
if ( current.equals( "number" ) ) {
try {
data = new cfStringData( buf.toString() ).getNumber();
usedData = true;
} catch ( dataNotSupportedException de ) {
throw new SAXException( de );
}
}
if ( current.equals( "binary" ) ) {
try {
String temp = buf.toString();
byte b[] = temp.getBytes();
data = new cfBinaryData( com.nary.net.Base64.base64Decode( b ) );
usedData = true;
} catch ( Exception e ) {
throw new SAXException( e );
}
}
if ( current.equals( "string" ) ) {
data = new cfStringData( buf.toString() );
usedData = true;
} else if ( current.equals( "datetime" ) ) {
try {
data = dateFromWddx( buf.toString() );
usedData = true;
} catch ( Exception e ) {
throw new SAXException( e );
}
}else if ( current.equals("null") ){
data = new cfStringData("");
usedData = true;
}
if ( !context.empty() && usedData ){
if ( context.peek() instanceof wddxStruct ) {
struct = ( wddxStruct ) context.pop();
struct.put( varName, data );
context.push( struct );
varName = null;
} else if ( context.peek() instanceof wddxArray ) {
array = ( wddxArray ) context.pop();
try {
array.addElement( data );
} catch (cfmRunTimeException ex) { throw new SAXException (ex); }
context.push( array );
} else if ( context.peek() instanceof wddxQueryResult ) {
resultSet = ( wddxQueryResult ) context.pop();
resultSet.setCell( rowNum, columnName, data );
context.push( resultSet );
}
} else {
try{
if ( current.equals( "string" ) ) {
result = new cfStringData( data.getString() );
} else if ( current.equals( "number" ) ) {
result = new cfNumberData( data.getNumber() );
} else if ( current.equals( "binary" ) ) {
result = data;
} else if ( current.equals( "datetime" ) ) {
result = dateFromWddx( buf.toString() );
}
} catch ( cfmRunTimeException e ) {
throw new SAXException( e );
}
}
}
/**
* Description of the Method
*
*@param _date Description of Parameter
*@return Description of the Returned Value
*@exception dataNotSupportedException Description of Exception
*@exception cfmRunTimeException Description of Exception
*/
private static cfDateData dateFromWddx( String _date ) {
int firstSeparator = _date.indexOf( "-" );
int secondSeparator = _date.indexOf( "-", firstSeparator + 1 );
int thirdSeparator = _date.indexOf( ":" );
int fourthSeparator = _date.indexOf( ":", thirdSeparator + 1 );
int fifthSeparator = _date.length();
int T = _date.indexOf( "T" );
int offsetFactor = 1;
if ( _date.indexOf( "+" ) != -1 ) {
fifthSeparator = _date.indexOf( "+" );
} else if ( _date.lastIndexOf( "-" ) != secondSeparator ) {
fifthSeparator = _date.lastIndexOf( "-" );
offsetFactor = -1;
}
int year = Integer.parseInt( _date.substring( 0, firstSeparator ) );
int month = Integer.parseInt( _date.substring( firstSeparator + 1, secondSeparator ) );
int day = Integer.parseInt( _date.substring( secondSeparator + 1, T ) );
int hours = Integer.parseInt( _date.substring( T + 1, thirdSeparator ) );
int minutes = Integer.parseInt( _date.substring( thirdSeparator + 1, fourthSeparator ) );
int seconds = Integer.parseInt( _date.substring( fourthSeparator + 1, fifthSeparator ) );
GregorianCalendar gc = new GregorianCalendar( year, month - 1, day, hours, minutes, seconds );
if ( fifthSeparator != _date.length() ){
String [] options = _date.substring( fifthSeparator+1 ).split(":");
int offsetHours = Integer.parseInt( options[0] );
int offsetMins = Integer.parseInt( options[1] );
int totalOffset = ( (offsetHours * 60) + offsetMins ) * 60 * 1000 * offsetFactor;
// Get current timezone offset
int currentTimezoneOffset = TimeZone.getDefault().getRawOffset();
// Calculate net timezone offset
int netTimezoneOffset = currentTimezoneOffset - totalOffset;
gc.add( Calendar.MILLISECOND, netTimezoneOffset );
}
return new cfDateData( gc.getTime() );
}
public void ignorableWhitespace( char[] arg0, int arg1, int arg2 ) {
//super.ignorableWhitespace(arg0, arg1, arg2);
}
}