/*
* Copyright (C) 2000 - 2010 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.parser;
import java.io.IOException;
import java.io.StringReader;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.CommonTreeNodeStream;
import com.naryx.tagfusion.cfm.engine.cfArrayData;
import com.naryx.tagfusion.cfm.engine.cfData;
import com.naryx.tagfusion.cfm.engine.cfJavaArrayData;
import com.naryx.tagfusion.cfm.engine.cfQueryColumnData;
import com.naryx.tagfusion.cfm.engine.cfStringData;
import com.naryx.tagfusion.cfm.engine.cfmRunTimeException;
public class CFAssignmentExpression extends CFExpression {
private static final long serialVersionUID = 1L;
private CFExpression left;
private CFExpression right;
private int type;
public CFAssignmentExpression( Token t, CFExpression _left,
CFExpression _right ) {
super(t);
left = _left;
right = _right;
type = t.getType();
}
public byte getType() {
return CFExpression.ASSIGNMENT;
}
public void checkIndirect( String expr ) {
if ( left instanceof CFVarExpression ) {
String lhs = expr.substring(0, expr.indexOf('=')).trim();
// check for special case of "#foo#"="bar" or '#foo#'='bar'
if ( (lhs.startsWith("\"#") && lhs.endsWith("#\""))
|| (lhs.startsWith("'#") && lhs.endsWith("#'")) ) {
((CFVarExpression) left).setIndirect(true);
}
}
}
public void checkIndirectAssignments( String[] scriptSource ) {
if ( left instanceof CFVarExpression ) {
/*
* We know this is a valid assignment expression but is the LHS expression
* a # expression e.g. "#foo#" = "bar" Since the poundSignFilter removes
* the #'s for the parser we need to look at the original source. We can
* get the line from this CFExpression but the column isn't accurate
* enough for CFFullVarExpressions especially as more than one expression
* may occur on the same line.
*
* We look backwards from the '=' skipping any whitespace and looking for
* #", #' or # on it's own.
*/
// find the original expression in the script source code
String expr = scriptSource[left.getLine() - 1];
int lhsEnd = expr.indexOf("=", left.getColumn());
if ( lhsEnd == -1 ) {
// if the expression is split over 2 lines
// e.g. "#foo#"
// = "bar"
lhsEnd = expr.length();
}
char[] exprChars = expr.substring(0, lhsEnd).toCharArray();
int i = exprChars.length - 1;
// skip over whitespace
while (i >= 0 && (Character.isWhitespace(exprChars[i]))) {
i--;
}
// does LHS expression end in #" or #'?
if ( i >= 0 && (exprChars[i] == '\"' || exprChars[i] == '\'') ) {
i--;
if ( i >= 0 && exprChars[i] == '#' ) {
((CFVarExpression) left).setIndirect(true);
}
}
}
}
public cfData Eval( CFContext context ) throws cfmRunTimeException {
boolean copy = false; // used to decide, in the case of an assignment
// whether to copy the RHS
cfData leftVal = null;
cfData rightVal = null;
cfLData lval = null; // Non-null only when assignment is to be done
cfData val = null;
setLineCol(context);
leftVal = left.Eval(context);
setLineCol(context);
if ( leftVal.getDataType() == cfData.CFLDATA ) {
lval = (cfLData) leftVal;
} else if ( leftVal.getDataType() == cfData.CFSTRINGDATA ) {
cfData result = reparse((cfStringData) leftVal, context).Eval(context);
if ( result.getDataType() == cfData.CFLDATA ) {
lval = (cfLData) result;
} else {
throw new CFException(
"Left-hand side of assignment is not a variable. "
+ leftVal.getClass().getName(), context);
}
} else {
throw new CFException(
"Left-hand side of assignment is not a variable. "
+ leftVal.getClass().getName(), context);
}
if ( type != CFMLLexer.EQUALSOP && !lval.exists() ) {
throw new CFException("Left-hand side of assignment does not exist.",
context);
}
boolean queryField = false;
rightVal = right.Eval(context);
if ( rightVal.getDataType() == cfData.CFLDATA ) {
queryField = (rightVal instanceof indirectQueryReferenceData);
rightVal = ((cfLData) rightVal).Get(context);
copy = true;
}
if ( rightVal.getDataType() == cfData.CFARRAYDATA
&& rightVal instanceof cfQueryColumnData ) {
rightVal = ((cfQueryColumnData) rightVal).getData();
}
if ( copy ) {
if ( cfData.isSimpleValue(rightVal) ) {
rightVal = rightVal.duplicate();
} else if ( queryField ) {
if ( rightVal.getDataType() == cfData.CFARRAYDATA ) {
rightVal = cfArrayData.createFrom((cfArrayData) rightVal);
}
} else if ( rightVal.getDataType() == cfData.CFARRAYDATA
&& !(rightVal instanceof cfJavaArrayData)
&& !(right instanceof CFFunctionExpression) ) {
rightVal = ((cfArrayData) rightVal).copy();
}
}
val = rightVal;
if ( type != CFMLLexer.EQUALSOP ) { // +=, -=, etc
int op = 0;
switch (type) {
case CFMLLexer.PLUSEQUALS:
op = CFMLLexer.PLUS;
break;
case CFMLLexer.MINUSEQUALS:
op = CFMLLexer.MINUS;
break;
case CFMLLexer.STAREQUALS:
op = CFMLLexer.STAR;
break;
case CFMLLexer.SLASHEQUALS:
op = CFMLLexer.SLASH;
break;
case CFMLLexer.MODEQUALS:
op = CFMLLexer.MOD;
break;
case CFMLLexer.CONCATEQUALS:
op = CFMLLexer.CONCAT;
break;
}
val = CFBinaryExpression.evaluate(context, op, lval.Get(context),
rightVal);
}
lval.Set(val, context);
return context._lastExpr = val;
}
private CFExpression reparse( cfStringData _string, CFContext _context )
throws cfmRunTimeException {
// note, the fact that calling leftVal.getString() will not include the
// pound signs is what's req'd
// note addition of ';' at end of expression to make it parsable
try {
ANTLRNoCaseReaderStream input = new ANTLRNoCaseReaderStream(
new poundSignFilterStream(new StringReader(_string.getString())));
CFMLLexer lexer = new CFMLLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CFMLParser parser = new CFMLParser(tokens);
parser.scriptMode = false;
CFMLParser.expression_return r = parser.expression();
CommonTree tree = (CommonTree) r.getTree();
CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree);
nodes.setTokenStream(tokens);
CFMLTree p2 = new CFMLTree(nodes);
p2.scriptMode = false;
return p2.expression();
} catch (IOException ioe) { // shouldn't happen
throw new CFException("Invalid expression : " + left.Decompile(0),
_context);
} catch (RecognitionException pe) {
throw new CFException("Invalid expression : " + left.Decompile(0),
_context);
} catch (poundSignFilterStreamException e) {
throw new CFException("Invalid expression : " + left.Decompile(0),
_context);
}
}
public String Decompile( int indent ) {
return left.Decompile(0) + "=" + right.Decompile(0);
}
}