/*
* $Id: Statement.java,v 1.47 2002/09/16 08:05:06 jkl Exp $
*
* Copyright (c) 2002 Njet Communications Ltd. All Rights Reserved.
*
* Use is subject to license terms, as defined in
* Anvil Sofware License, Version 1.1. See LICENSE
* file, or http://njet.org/license-1.1.txt
*/
package anvil.script.statements;
import anvil.Location;
import anvil.core.Any;
import anvil.core.Array;
import anvil.codec.Source;
import anvil.codec.Target;
import anvil.codec.ExceptionHandler;
import anvil.parser.Tag;
import anvil.ErrorListener;
import anvil.script.compiler.ByteCompiler;
import anvil.script.Context;
import anvil.script.expression.Expression;
import anvil.script.parser.TemplateParser;
import anvil.script.Grammar;
import anvil.script.Scope;
import anvil.script.Type;
import anvil.java.util.Holder;
import java.io.IOException;
import java.util.Hashtable;
/**
* class Statement
*
* @author: Jani Lehtim�ki
*/
public abstract class Statement
{
static final public Statement EMPTY = new Statement(null, null)
{
};
static final public String DEFAULT_NAMESPACE = "anvil";
public static final int CONTENT_PRESERVE = 0;
public static final int CONTENT_COMPRESS = 1;
public static final int CONTENT_PACK = 2;
public static final int CONTENT_SILENT = 3;
public static final String[] CONTENT_PROCESSING = {
"preserve",
"compress",
"pack",
"silent"
};
static final public int ST_INVALID = -1;
static final public int ST_NONE = 0;
static final public int ST_BREAK = 1;
static final public int ST_CALL = 2;
static final public int ST_CASE = 3;
static final public int ST_CATCH = 4;
static final public int ST_CDATA = 5;
static final public int ST_CLASS = 6;
static final public int ST_COMPRESS = 7;
static final public int ST_CONST = 8;
static final public int ST_CONTINUE = 9;
static final public int ST_DEFAULT = 10;
static final public int ST_DO = 11;
static final public int ST_DOCUMENT = 12;
static final public int ST_ELSE = 13;
static final public int ST_ELSEIF = 14;
static final public int ST_ENDCASE = 15;
static final public int ST_ENDDEFAULT = 16;
static final public int ST_ENDEVAL = 17;
static final public int ST_ENDFOR = 18;
static final public int ST_ENDFOREACH = 19;
static final public int ST_ENDFUNCTION = 20;
static final public int ST_ENDIF = 21;
static final public int ST_ENDMODULE = 22;
static final public int ST_ENDREPLACE = 23;
static final public int ST_ENDSWITCH = 24;
static final public int ST_ENDWHILE = 25;
static final public int ST_EVAL = 27;
static final public int ST_FINALLY = 28;
static final public int ST_FOR = 29;
static final public int ST_FOREACH = 30;
static final public int ST_FUNCTION = 31;
static final public int ST_IF = 32;
static final public int ST_IMPORT = 33;
static final public int ST_INTERFACE = 34;
static final public int ST_INVOKE = 35;
static final public int ST_MODULE = 36;
static final public int ST_PRESERVE = 37;
static final public int ST_PRINT = 38;
static final public int ST_REPLACE = 39;
static final public int ST_RETURN = 40;
static final public int ST_SILENT = 41;
static final public int ST_SWITCH = 42;
static final public int ST_TAG = 43;
static final public int ST_THROW = 44;
static final public int ST_TRY = 45;
static final public int ST_VAR = 46;
static final public int ST_WHILE = 47;
static final public int ST_ASSERT = 49;
static final public int ST_EXIT = 50;
static final public int ST_BLOCK = 51;
static final public int ST_CUSTOM_TAG = 52;
static final public int ST_SYNCHRONIZED = 53;
static final public int ST_YIELD = 54;
static final public int ST_ENDCLASS = 55;
static final public int ST_NAMESPACE = 56;
static final public int ST_ENDNAMESPACE = 57;
static final public int ST_IMPLICIT_BLOCK = 58;
static final public int ST_PACK = 59;
static final private Hashtable _tags = new Hashtable();
static
{
_tags.put("module", new Integer(ST_MODULE));
_tags.put("/module", new Integer(ST_ENDMODULE));
_tags.put("import", new Integer(ST_IMPORT));
_tags.put("var", new Integer(ST_VAR));
_tags.put("const", new Integer(ST_CONST));
_tags.put("function", new Integer(ST_FUNCTION));
_tags.put("/function", new Integer(ST_ENDFUNCTION));
_tags.put("print", new Integer(ST_PRINT));
_tags.put("replace", new Integer(ST_REPLACE));
_tags.put("/replace", new Integer(ST_ENDREPLACE));
_tags.put("if", new Integer(ST_IF));
_tags.put("else", new Integer(ST_ELSE));
_tags.put("elseif", new Integer(ST_ELSEIF));
_tags.put("/if", new Integer(ST_ENDIF));
_tags.put("while", new Integer(ST_WHILE));
_tags.put("/while", new Integer(ST_ENDWHILE));
_tags.put("return", new Integer(ST_RETURN));
_tags.put("break", new Integer(ST_BREAK));
_tags.put("continue", new Integer(ST_CONTINUE));
_tags.put("switch", new Integer(ST_SWITCH));
_tags.put("case", new Integer(ST_CASE));
_tags.put("/case", new Integer(ST_ENDCASE));
_tags.put("default", new Integer(ST_DEFAULT));
_tags.put("/default", new Integer(ST_ENDDEFAULT));
_tags.put("/switch", new Integer(ST_ENDSWITCH));
_tags.put("foreach", new Integer(ST_FOREACH));
_tags.put("/foreach", new Integer(ST_ENDFOREACH));
_tags.put("for", new Integer(ST_FOR));
_tags.put("/for", new Integer(ST_ENDFOR));
_tags.put("eval", new Integer(ST_EVAL));
_tags.put("/eval", new Integer(ST_ENDEVAL));
_tags.put("preserve", new Integer(ST_PRESERVE));
_tags.put("compress", new Integer(ST_COMPRESS));
_tags.put("silent", new Integer(ST_SILENT));
_tags.put("call", new Integer(ST_CALL));
_tags.put("invoke", new Integer(ST_INVOKE));
_tags.put("class", new Integer(ST_CLASS));
_tags.put("/class", new Integer(ST_ENDCLASS));
_tags.put("namespace", new Integer(ST_NAMESPACE));
_tags.put("/namespace", new Integer(ST_ENDNAMESPACE));
}
public static int getTagId(Tag tag)
{
Integer id = (Integer)_tags.get(tag.getName().toLowerCase());
if (id != null) {
return id.intValue();
} else {
return ST_INVALID;
}
}
private Location _location;
private Statement _parent;
public Statement(Statement parent)
{
_parent = parent;
}
public Statement(Location location)
{
_location = location;
}
public Statement(Statement parent, Location location)
{
_parent = parent;
_location = location;
}
public int typeOf()
{
return ST_NONE;
}
public String name()
{
return "statement";
}
public void setLocation(Location location)
{
_location = location;
}
public Location getLocation()
{
return _location;
}
public Statement getParentStatement()
{
return _parent;
}
public void setParentStatement(Statement parent)
{
_parent = parent;
}
public boolean hasEnd()
{
return false;
}
public boolean isStaticRegion()
{
return getParentStatement().isStaticRegion();
}
public boolean hasStaticContent()
{
return false;
}
public String getStaticContent()
{
return "";
}
public BlockStatement getBlockStatement()
{
Statement stmt = getChildStatement();
if (stmt instanceof BlockStatement) {
return (BlockStatement)stmt;
} else {
throw new RuntimeException("Child '"+stmt+"' is not instance of BlockStatement");
}
}
public void addChild(Statement statement)
{
getBlockStatement().add(statement);
}
public Statement getChildStatement()
{
return null;
}
public void setChildStatement(Statement child)
{
//throw new RuntimeException("Statement.setChildStatement called at "+this.getClass());
}
public void parse(TemplateParser parser, Tag tag)
{
}
public void importExternals(ErrorListener context)
{
}
public void check(ErrorListener context)
{
}
public Jumps eliminate(ErrorListener context)
{
return new Jumps();
}
private void doAddCharacters(TemplateParser parser, String cdata)
{
FunctionStatement function = getFunctionStatement();
if (function != null) {
switch(function.getContentState()) {
case CONTENT_SILENT:
return;
case CONTENT_PRESERVE:
break;
case CONTENT_COMPRESS:
cdata = compress(cdata);
break;
case CONTENT_PACK:
cdata = pack(cdata);
break;
}
}
if (cdata.length() == 0) {
return;
}
addChild(new CharacterDataStatement(this, parser.getLocation(), cdata));
}
public void onCharacters(TemplateParser parser, String cdata)
{
if ((cdata.length() >= 11) &&
cdata.startsWith("<!--/*") && cdata.endsWith("*/-->")) {
return;
}
if (cdata.indexOf("${") >= 0) {
StringBuffer buffer = new StringBuffer();
int n = cdata.length();
for(int i=0; i<n; i++) {
char ch = cdata.charAt(i);
if ((ch == '\\') && (i+1<n)) {
buffer.append(cdata.charAt(++i));
} else if ((ch == '$') && (i+2<n)) {
ch = cdata.charAt(++i);
if (ch != '{') {
buffer.append('$');
buffer.append(ch);
continue;
}
if (buffer.length()>0) {
doAddCharacters(parser, buffer.toString());
}
buffer.setLength(0);
boolean singleQuote = false;
boolean doubleQuote = false;
int nesting = 1;
while(i+1<n && nesting>0) {
ch = cdata.charAt(++i);
switch(ch) {
case '{':
if (!singleQuote || !doubleQuote) {
nesting++;
}
buffer.append(ch);
break;
case '}':
if (!singleQuote || !doubleQuote) {
nesting--;
}
if (nesting>0) {
buffer.append(ch);
}
break;
case '\\':
if (i+1<n) {
char nextch = cdata.charAt(++i);
if (nextch == '}' || nextch == '{') {
buffer.append(nextch);
} else {
buffer.append(ch);
buffer.append(nextch);
}
}
break;
case '\'':
if (!doubleQuote) {
singleQuote = !singleQuote;
}
buffer.append(ch);
break;
case '"':
if (!singleQuote) {
doubleQuote = !doubleQuote;
}
buffer.append(ch);
break;
default:
buffer.append(ch);
}
}
Expression expr = Grammar.parseExpression(buffer.toString(), getLocation(), parser);
if (expr.isConstant()) {
addChild(new CharacterDataStatement(this, parser.getLocation(), expr.eval().toString()));
} else {
addChild(new PrintStatement(this, parser.getLocation(), expr));
}
buffer.setLength(0);
} else {
buffer.append(ch);
}
}
if (buffer.length()>0) {
doAddCharacters(parser, buffer.toString());
}
} else {
doAddCharacters(parser, cdata);
}
}
public anvil.script.statements.taglib.Tag getTag(String ns, String name)
{
Statement parent = getParentStatement();
if (parent != null) {
return parent.getTag(ns, name);
} else {
return null;
}
}
public boolean onTag(TemplateParser parser, int type, Tag tag)
{
Statement statement = null;
boolean addblock = false;
BlockStatement block = getBlockStatement();
switch(type) {
case ST_TAG:
{
String ns = tag.getNamespace();
String name = tag.getName();
anvil.script.statements.taglib.Tag tagdef = getTag(ns, name);
if (tagdef != null) {
statement = new CustomTagStatement(block, parser.getLocation(), tagdef, tag);
addblock = statement.hasEnd();
break;
}
FunctionStatement function = getFunctionStatement();
if (function == null || function.getContentState() != Statement.CONTENT_SILENT) {
onCharacters(parser, tag.toString());
}
return true;
}
case ST_EVAL:
statement = new EvalStatement(block, parser.getLocation());
addblock = true;
break;
case ST_PRINT:
statement = new PrintStatement(block, parser.getLocation());
break;
case ST_CALL:
statement = new CallStatement(block, parser.getLocation());
break;
case ST_INVOKE:
statement = new InvokeStatement(block, parser.getLocation());
break;
case ST_RETURN:
statement = new ReturnStatement(block, parser.getLocation());
break;
case ST_PACK:
statement = new PackStatement(block, parser.getLocation());
break;
case ST_PRESERVE:
statement = new PreserveStatement(block, parser.getLocation());
break;
case ST_COMPRESS:
statement = new CompressStatement(block, parser.getLocation());
break;
case ST_SILENT:
statement = new SilentStatement(block, parser.getLocation());
break;
case ST_IF:
statement = new IfStatement(block, parser.getLocation());
addblock = true;
break;
case ST_REPLACE:
statement = new ReplaceStatement(block, parser.getLocation());
break;
case ST_WHILE:
statement = new WhileStatement(block, parser.getLocation());
addblock = true;
break;
case ST_FOREACH:
statement = new ForeachStatement(block, parser.getLocation());
addblock = true;
break;
case ST_FOR:
statement = new ForStatement(block, parser.getLocation());
addblock = true;
break;
case ST_SWITCH:
statement = new SwitchStatement(block, parser.getLocation());
break;
case ST_BREAK:
if (!allowBreak()) {
parser.error(getLocation(), "Useless break, no valid enclosing statements open");
} else {
statement = new BreakStatement(block, parser.getLocation());
}
break;
case ST_CONTINUE:
if (!allowContinue()) {
parser.error(getLocation(), "Useless continue, no valid enclosing statements open");
} else {
statement = new ContinueStatement(block, parser.getLocation());
}
break;
}
if (statement == null) {
return false;
}
statement.parse(parser, tag);
addChild(statement);
if (statement.hasEnd()) {
parser.push(statement);
if (addblock) {
statement.setChildStatement(
new ImplicitBlockStatement(statement, statement.getLocation()));
}
}
return true;
}
public boolean onProcessingInstruction(TemplateParser parser, String data)
{
onCharacters(parser, "<?" + data + "?>");
return true;
}
public CatchStatement getCatchStatement()
{
Statement stmt = this;
while(stmt != null) {
if (stmt.typeOf() == ST_CATCH) {
return (CatchStatement)stmt;
}
stmt = stmt.getParentStatement();
}
return null;
}
public ModuleStatement getModuleStatement()
{
Statement stmt = this;
while(stmt != null) {
if (stmt.typeOf() == ST_MODULE) {
return (ModuleStatement)stmt;
}
stmt = stmt.getParentStatement();
}
return null;
}
public ClassStatement getClassStatement()
{
Statement stmt = this;
while(stmt != null) {
if (stmt.typeOf() == ST_CLASS) {
return (ClassStatement)stmt;
}
stmt = stmt.getParentStatement();
}
return null;
}
public InterfaceStatement getInterfaceStatement()
{
Statement stmt = this;
while(stmt != null) {
if (stmt.typeOf() == ST_INTERFACE) {
return (InterfaceStatement)stmt;
}
stmt = stmt.getParentStatement();
}
return null;
}
public FunctionStatement getFunctionStatement()
{
Statement stmt = this;
while(stmt != null) {
if (stmt.typeOf() == ST_FUNCTION) {
return (FunctionStatement)stmt;
}
stmt = stmt.getParentStatement();
}
return null;
}
public DefinitionStatement getDefinitionStatement()
{
Statement stmt = this;
while(stmt != null) {
if (stmt instanceof DefinitionStatement) {
return (DefinitionStatement)stmt;
}
stmt = stmt.getParentStatement();
}
return null;
}
public DefinitionStatement getScopeStatement()
{
Statement stmt = this;
while(stmt != null) {
if (stmt instanceof DefinitionStatement && stmt.typeOf() != ST_FUNCTION) {
return (DefinitionStatement)stmt;
}
stmt = stmt.getParentStatement();
}
return null;
}
public Type lookupAnyDeclaration(String name)
{
Statement stmt = this;
while(stmt != null) {
switch(stmt.typeOf()) {
case Statement.ST_MODULE:
case Statement.ST_CLASS:
case Statement.ST_INTERFACE:
case Statement.ST_NAMESPACE:
case Statement.ST_FUNCTION:
{
DefinitionStatement defstmt = (DefinitionStatement)stmt;
Type type = defstmt.lookupDeclaration(name);
if (type != null) {
return type;
}
}
break;
}
stmt = stmt.getParentStatement();
}
return null;
}
public boolean isDeclared(String name)
{
return (lookupAnyDeclaration(name) != null);
}
public boolean allowBreak()
{
Statement stmt = this;
while(stmt != null) {
switch(stmt.typeOf()) {
case Statement.ST_BLOCK:
case Statement.ST_WHILE:
case Statement.ST_DO:
case Statement.ST_FOREACH:
case Statement.ST_FOR:
case Statement.ST_SWITCH:
return true;
case Statement.ST_IF:
case Statement.ST_TRY:
case Statement.ST_CATCH:
case Statement.ST_FINALLY:
case Statement.ST_SYNCHRONIZED:
case Statement.ST_IMPLICIT_BLOCK:
break;
default:
return false;
}
stmt = stmt.getParentStatement();
}
return false;
}
public boolean allowContinue()
{
Statement stmt = this;
while(stmt != null) {
switch(stmt.typeOf()) {
case Statement.ST_BLOCK:
case Statement.ST_WHILE:
case Statement.ST_DO:
case Statement.ST_FOREACH:
case Statement.ST_FOR:
return true;
case Statement.ST_SWITCH:
case Statement.ST_IF:
case Statement.ST_TRY:
case Statement.ST_CATCH:
case Statement.ST_FINALLY:
case Statement.ST_SYNCHRONIZED:
case Statement.ST_IMPLICIT_BLOCK:
break;
default:
return false;
}
stmt = stmt.getParentStatement();
}
return false;
}
public boolean allowYield()
{
Statement stmt = this;
while(stmt != null) {
switch(stmt.typeOf()) {
case ST_FUNCTION:
return true;
case Statement.ST_FINALLY:
case Statement.ST_SYNCHRONIZED:
return false;
case Statement.ST_TRY:
{
TryStatement trystmt = (TryStatement)stmt;
if (trystmt.getFinally() != null) {
return false;
}
}
break;
default:
break;
}
stmt = stmt.getParentStatement();
}
return true;
}
public boolean allowLabel(String label)
{
Statement stmt = this;
while(stmt != null) {
if (stmt instanceof Labeled) {
String candidate = ((Labeled)stmt).getLabel();
if ((candidate != null) && (label.equals(candidate))) {
return true;
}
}
stmt = stmt.getParentStatement();
}
return false;
}
public int getLabelDepth(String label)
{
int depth = 0;
Statement stmt = this;
while(stmt != null) {
if (stmt instanceof Labeled) {
String candidate = ((Labeled)stmt).getLabel();
if ((candidate != null) && (label.equals(candidate))) {
return depth;
}
depth++;
}
stmt = stmt.getParentStatement();
}
return -1;
}
public Labeled getLabeled(String label, boolean iscont)
{
Statement stmt = this;
while(stmt != null) {
if (stmt instanceof Labeled) {
Labeled labeled = (Labeled)stmt;
if (label != null) {
String candidate = labeled.getLabel();
if ((candidate != null) && (label.equals(candidate))) {
if (!iscont || stmt.typeOf() != ST_SWITCH) {
return labeled;
}
}
} else {
return labeled;
}
}
stmt = stmt.getParentStatement();
}
return null;
}
public SwitchStatement getSwitch(String label)
{
Statement stmt = this;
while(stmt != null) {
if (stmt.typeOf() == ST_SWITCH) {
SwitchStatement select = (SwitchStatement)stmt;
if (label != null) {
String candidate = select.getLabel();
if ((candidate != null) && (label.equals(candidate))) {
return select;
}
} else {
return select;
}
}
stmt = stmt.getParentStatement();
}
return null;
}
public void compile(ByteCompiler context)
{
}
public boolean isBlocked()
{
return false;
}
public boolean callFinalizer()
{
return false;
}
public int getTypeRef(anvil.codec.ConstantPool pool, int type)
{
return 0;
}
public static final String compress(String cdata)
{
if (cdata.charAt(0) == '<') {
return cdata;
}
int n = cdata.length();
int i = 0;
char ch;
char pad = ' ';
StringBuffer buffer = new StringBuffer(n);
while(i<n) {
ch = cdata.charAt(i);
if (Character.isWhitespace(ch)) {
pad = (ch == '\n') ? '\n' : ' ';
while( (++i) < n ) {
ch = cdata.charAt(i);
if (!Character.isWhitespace(ch)) {
break;
}
if (ch == '\n') {
pad = '\n';
}
}
buffer.append(pad);
} else {
buffer.append(ch);
i++;
}
}
return buffer.toString();
}
public static final String pack(String cdata)
{
if (cdata.charAt(0) == '<') {
return cdata;
}
int length = cdata.length();
int end = length;
for(; end > 0; end--) {
if (!Character.isWhitespace(cdata.charAt(end-1))) {
break;
}
}
int start = 0;
for(; start<end; start++) {
if (!Character.isWhitespace(cdata.charAt(start))) {
break;
}
}
StringBuffer buffer = new StringBuffer(end - start + 1);
int i = start;
while(i < end) {
char ch = cdata.charAt(i);
if (Character.isWhitespace(ch)) {
char pad = ' ';
while(true) {
if (ch == '\n') {
pad= '\n';
}
if (i++ >= end) {
break;
}
ch = cdata.charAt(i);
if (!Character.isWhitespace(ch)) {
break;
}
}
buffer.append(pad);
} else {
buffer.append(ch);
i++;
}
}
return buffer.toString();
}
public final String parseLabel(TemplateParser parser, Tag tag)
{
String label = tag.getValue("label");
if (label != null) {
if (!Grammar.isValidIdentifier(label)) {
parser.error(getLocation(), "Label '"+label+"' is not a valid identifier");
return null;
}
if (allowLabel(label)) {
parser.error(getLocation(), "Label '"+label+"' is already declared");
return null;
}
}
return label;
}
public int getContentState()
{
return getParentStatement().getContentState();
}
}