/*
* $Id: CustomTagStatement.java,v 1.12 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.core.Any;
import anvil.core.Array;
import anvil.codec.Code;
import anvil.codec.ConstantPool;
import anvil.codec.Source;
import anvil.codec.Target;
import anvil.codec.ExceptionHandler;
import anvil.Location;
import anvil.ErrorListener;
import anvil.script.compiler.ByteCompiler;
import anvil.script.Context;
import anvil.script.Name;
import anvil.script.StackFrame;
import anvil.script.expression.ArrayNode;
import anvil.script.expression.Node;
import anvil.script.expression.ConstantNode;
import anvil.script.expression.Expression;
import anvil.script.expression.ExpressionList;
import anvil.script.expression.LinkNode;
import anvil.script.expression.MappingNode;
import anvil.script.expression.Expression;
import anvil.script.expression.InvokeNode;
import anvil.script.parser.TemplateParser;
import anvil.script.parser.ExpressionParser;
import anvil.script.Grammar;
import anvil.script.statements.taglib.TagLibrary;
import anvil.script.statements.taglib.Attribute;
import anvil.script.statements.taglib.Tag;
import java.io.IOException;
import java.util.Enumeration;
import java.util.ArrayList;
import java.util.Iterator;
/**�
* class CustomTagStatement
*
* @author: Jani Lehtim�ki
*/
public class CustomTagStatement extends Statement
{
private String _namespace;
private String _name;
private Tag _tagdef = null;
private anvil.parser.Tag _tag = null;
private boolean _hasEnd = false;
private Statement _statement = null;
private Expression _init = null;
private Expression _start = null;
private Expression _end = null;
private Expression _release = null;
private ExceptionHandler _handler = null;
private int _framelocal = 0;
public CustomTagStatement(Statement parent, Location location, Tag tagdef, anvil.parser.Tag tag)
{
super(parent, location);
_namespace = tagdef.getLibrary().getNamespace();
_name = tag.getName();
_tagdef = tagdef;
_tag = tag;
_hasEnd = tagdef.hasContent() && !tag.hasEndSlash();
}
public int typeOf()
{
return Statement.ST_CUSTOM_TAG;
}
public String name()
{
return _name;
}
public void parse(TemplateParser parser, anvil.parser.Tag tag)
{
ArrayList nodes = new ArrayList();
Enumeration e = _tagdef.getAttributes();
while(e.hasMoreElements()) {
Attribute attr = (Attribute)e.nextElement();
String name = attr.getName();
String value = tag.getValue(attr.getName());
if (value == null) {
if (attr.isRequired()) {
parser.error(parser.getLocation(), "Required attribute '"+name+"' missing");
} else {
value = attr.getDefault();
}
}
if (value != null) {
Node node = ConstantNode.UNDEFINED;
switch(attr.getType()) {
case Attribute.TYPE_STRING:
node = new ConstantNode(Any.create(value));
break;
case Attribute.TYPE_DOUBLE:
try {
node = new ConstantNode(Any.create(Double.parseDouble(value)));
} catch (NumberFormatException nfe) {
parser.error(parser.getLocation(), "Value of attribute '"+name+"' is not a floating point number");
}
break;
case Attribute.TYPE_INT:
try {
node = new ConstantNode(Any.create(anvil.util.Conversions.parseNumberUnsafe(value)));
} catch (NumberFormatException nfe) {
parser.error(parser.getLocation(), "Value of attribute '"+name+"' is not an integer");
}
break;
case Attribute.TYPE_BOOLEAN:
node = new ConstantNode(Any.create(Any.IS_BOOLEAN, value));
break;
case Attribute.TYPE_EXPR:
value = value.trim();
if (value.length() > 0) {
ExpressionParser p = new ExpressionParser(parser, parser.getLocation(), value);
node = p.parseExpression(p.TYPE_VALUE).getChild();
} else {
node = ConstantNode.UNDEFINED;
}
break;
}
nodes.add(new MappingNode(new ConstantNode(name), node));
}
}
if (_tagdef.allowAnyAttributes()) {
Iterator iter = _tag.getAttributes();
while(iter.hasNext()) {
anvil.parser.Attribute attr = (anvil.parser.Attribute)iter.next();
String name = attr.getName();
if (!_tagdef.hasAttribute(name)) {
nodes.add(new MappingNode(
new ConstantNode(name),
new ConstantNode(attr.getValue())));
}
}
}
int n = nodes.size();
ExpressionList childs = new ExpressionList(n);
for(int i=0; i<n; i++) {
childs.setChild(i, (Node)nodes.get(i));
}
Location location = getLocation();
_init = buildExpr(location,
_tagdef.getInitHandler(),
null,
new ExpressionList(
new ParentNode(), new ConstantNode(_name),
new ArrayNode(childs)));
_start = buildExpr(location,
_tagdef.getStartHandler(),
_tagdef.getStartMethod(),
ARGS);
_end = buildExpr(location,
_tagdef.getEndHandler(),
_tagdef.getEndMethod(),
ARGS);
_release = buildExpr(location,
_tagdef.getReleaseHandler(),
_tagdef.getReleaseMethod(),
ARGS);
}
public boolean onTag(TemplateParser parser, int type, anvil.parser.Tag tag)
{
switch(type) {
case ST_TAG:
if (tag.isEndTagOf(_tag)) {
parser.pop();
break;
}
default:
return super.onTag(parser, type, tag);
}
return true;
}
private Expression buildExpr(Location location, String handler, String method, ExpressionList args)
{
if (handler != null) {
handler = handler.trim();
return new Expression(new LinkNode(this, location,
new Name().add(_namespace).add(handler), args,
LinkNode.GET) ,location);
} else if (method != null) {
return new Expression(new InvokeNode(FRAME, method, EMPTY_ARGS), location);
} else {
return null;
}
}
public boolean hasEnd()
{
return _hasEnd;
}
public Statement getChildStatement()
{
return _statement;
}
public void setChildStatement(Statement statement)
{
_statement = statement;
}
public void check(ErrorListener context)
{
if (_init != null) {
_init.check(context);
}
if (_start != null) {
_start.check(context);
}
if (_end != null) {
_end.check(context);
}
if (_release != null) {
_release.check(context);
}
if (_statement != null) {
_statement.check(context);
}
}
public Jumps eliminate(ErrorListener context)
{
if (_statement != null) {
Jumps jumps = _statement.eliminate(context);
jumps.setBlocked(false);
return jumps.shift();
} else {
return new Jumps();
}
}
public void compile(ByteCompiler context)
{
Code code = context.getCode();
ConstantPool pool = code.getPool();
boolean needloop =
(_start != null) ||
(_end != null) ||
(_release != null) ||
(_statement != null);
if (needloop) {
if (_init != null) {
_init.compile(context, Node.GET);
} else {
code.getstatic(pool.addFieldRef(context.TYPE_ANY, "UNDEFINED",
"Lanvil/core/Any;"));
}
code.dup();
int contextframe = pool.addMethodRef(context.TYPE_CONTEXT, "frame",
"()Lanvil/script/StackFrame;");
code.aload_first();
code.invokevirtual(contextframe);
code.swap();
code.invokevirtual(pool.addMethodRef("anvil/script/StackFrame",
"push", "(Lanvil/core/Any;)V"));
_framelocal = code.addLocal();
code.astore(_framelocal);
ExceptionHandler handler = code.startExceptionHandler(true);
_handler = handler;
Target tostart = code.getTarget();
if (_start != null) {
_start.compile(context, Node.GET_BOOLEAN);
Source istrue = code.if_ne();
handler.callFinally();
handler.jumpOut();
istrue.bind();
}
if (_statement != null) {
_statement.compile(context);
}
if (_end != null) {
_end.compile(context, Node.GET_BOOLEAN);
code.if_ne(tostart);
}
handler.endTry();
if (_statement == null || !_statement.isBlocked()) {
handler.callFinally();
handler.jumpOut();
}
handler.endProtectedRegion();
handler.startCatch(0);
int thrown = code.addLocal();
code.astore(thrown);
handler.callFinally();
code.aload(thrown);
code.athrow();
handler.endCatches();
_handler = null;
// finally
handler.startFinally();
int returnto = code.addLocal();
code.astore(returnto);
code.aload_first();
code.invokevirtual(contextframe);
code.invokevirtual(pool.addMethodRef("anvil/script/StackFrame",
"pop", "()Lanvil/core/Any;"));
code.pop();
if (_release != null) {
_release.compile(context, Node.GET);
code.pop();
}
code.ret(returnto);
handler.endFinally();
handler.end();
code.endLocal(_framelocal);
} else {
if (_init != null) {
_init.compile(context, Node.GET);
code.pop();
}
}
}
public boolean callFinalizer()
{
_handler.callFinally();
return false;
}
private Node FRAME = new FrameNode();
private ExpressionList ARGS = new ExpressionList(FRAME);
private static final ExpressionList EMPTY_ARGS = new ExpressionList(0);
private static class ParentNode extends Node
{
public void compile(ByteCompiler context, int operation)
{
Code code = context.getCode();
ConstantPool pool = code.getPool();
code.aload_first();
code.invokevirtual(pool.addMethodRef(context.TYPE_CONTEXT, "frame",
"()Lanvil/script/StackFrame;"));
code.invokevirtual(pool.addMethodRef("anvil/script/StackFrame", "peek",
"()Lanvil/core/Any;"));
}
}
private class FrameNode extends Node
{
public void compile(ByteCompiler context, int operation)
{
Code code = context.getCode();
ConstantPool pool = code.getPool();
code.aload(_framelocal);
}
}
}