/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.jsp.java;
import com.caucho.jsp.JspParseException;
import com.caucho.jsp.TagInstance;
import com.caucho.vfs.WriteStream;
import com.caucho.xml.QName;
import javax.servlet.jsp.tagext.TagInfo;
import java.io.IOException;
import java.util.ArrayList;
/**
* Special generator for a JSTL c:forEach tag.
*/
public class JstlCoreForEach extends JstlNode {
private static final QName VAR = new QName("var");
private static final QName VAR_STATUS = new QName("varStatus");
private static final QName ITEMS = new QName("items");
private static final QName BEGIN = new QName("begin");
private static final QName END = new QName("end");
private static final QName STEP = new QName("step");
private String _var;
private String _varStatus;
private String _items;
private JspAttribute _itemsAttr;
private String _begin;
private JspAttribute _beginAttr;
private String _end;
private JspAttribute _endAttr;
private String _step;
private JspAttribute _stepAttr;
private boolean _isInteger;
private int _depth;
private String _tagVar;
private TagInstance _tag;
private boolean _isDeclaration;
/**
* Adds an attribute.
*/
public void addAttribute(QName name, String value)
throws JspParseException
{
if (VAR.equals(name))
_var = value;
else if (VAR_STATUS.equals(name))
_varStatus = value;
else if (ITEMS.equals(name)) {
_items = value;
_attributeNames.add(name);
_attributeValues.add(value);
}
else if (BEGIN.equals(name))
_begin = value;
else if (END.equals(name))
_end = value;
else if (STEP.equals(name))
_step = value;
else
throw error(L.l("'{0}' is an unknown attribute for <{1}>.",
name.getName(), getTagName()));
}
/**
* Adds an attribute.
*/
public void addAttribute(QName name, JspAttribute value)
throws JspParseException
{
if (ITEMS.equals(name))
_itemsAttr = value;
else if (BEGIN.equals(name))
_beginAttr = value;
else if (END.equals(name))
_endAttr = value;
else if (STEP.equals(name))
_stepAttr = value;
else
throw error(L.l("'{0}' is an unknown jsp:attribute for <{1}>.",
name.getName(), getTagName()));
}
/**
* Returns true if the tag has scripting values.
*/
public boolean hasScripting()
{
return (super.hasScripting() ||
hasScripting(_items) || hasScripting(_itemsAttr) ||
hasScripting(_begin) || hasScripting(_beginAttr) ||
hasScripting(_end) || hasScripting(_endAttr) ||
hasScripting(_step) || hasScripting(_stepAttr));
}
/**
* Returns true for an integer forEach.
*/
public boolean isInteger()
{
return _items == null && _itemsAttr == null;
}
public TagInstance getTag()
{
return _tag;
}
/**
* Returns the tag name for the current tag.
*/
public String getCustomTagName()
{
if (_tag == null)
return null;
else
return _tag.getId();
}
/**
* Returns true for a simple tag.
*/
public boolean isSimpleTag()
{
return false;
}
/**
* Generates the XML text representation for the tag validation.
*
* @param os write stream to the generated XML.
*/
public void printXml(WriteStream os)
throws IOException
{
os.print("<c:forEach");
if (_itemsAttr != null) {
os.print(" items=\"");
_itemsAttr.printXml(os);
os.print("\"");
}
else if (_items != null) {
os.print(" items=\"");
printXmlText(os, _items);
os.print("\"");
}
if (_beginAttr != null) {
os.print(" begin=\"");
_beginAttr.printXml(os);
os.print("\"");
}
else if (_begin != null) {
os.print(" begin=\"");
printXmlText(os, _begin);
os.print("\"");
}
if (_endAttr != null) {
os.print(" end=\"");
_endAttr.printXml(os);
os.print("\"");
}
else if (_end != null) {
os.print(" end=\"");
printXmlText(os, _end);
os.print("\"");
}
if (_stepAttr != null) {
os.print(" step=\"");
_stepAttr.printXml(os);
os.print("\"");
}
else if (_step != null) {
os.print(" step=\"");
printXmlText(os, _step);
os.print("\"");
}
os.print(">");
printXmlChildren(os);
os.print("</c:forEach>");
}
/**
* Generates the prologue for the c:forEach tag.
*/
public void generatePrologue(JspJavaWriter out)
throws Exception
{
TagInstance parent = getParent().getTag();
_tag = parent.findTag(getQName(), _attributeNames, false);
if (_tag != null) {
_tagVar = _tag.getId();
}
else {
_isDeclaration = true;
TagInfo tagInfo = _gen.getTag(getQName());
_tag = parent.addTag(_gen, getQName(), tagInfo, null,
_attributeNames, _attributeValues, false);
String id = "_jsp_loop_" + _gen.uniqueId();
_tag.setId(id);
_tagVar = _tag.getId();
if (isInteger())
out.println("com.caucho.jsp.IntegerLoopSupportTag " + _tagVar + " = null;");
else
out.println("com.caucho.jsp.IteratorLoopSupportTag " + _tagVar + " = null;");
}
generatePrologueChildren(out);
}
private boolean hasDeclaration()
{
return (_varStatus != null || hasTag());
}
/**
* Returns the depth of the loop tags.
*/
private int getDepth()
{
JspNode node = this;
int depth = 0;
for (; ! (node instanceof JspSegmentNode); node = node.getParent()) {
if (node instanceof JstlCoreForEach) {
JstlCoreForEach forEach = (JstlCoreForEach) node;
if (forEach.isInteger() == isInteger())
depth++;
}
}
return depth;
}
/**
* Returns true if this is the first declaration for the forEach
*/
private boolean isFirst()
{
JspNode node = this;
for (; ! (node instanceof JspSegmentNode); node = node.getParent()) {
}
return isFirst(node, getDepth()) == 1;
}
/**
* Returns true if this is the first declaration for the forEach
*/
private int isFirst(JspNode node, int depth)
{
if (node == this)
return 1;
else if (node instanceof JstlCoreForEach) {
JstlCoreForEach forEach = (JstlCoreForEach) node;
if (forEach.isInteger() == isInteger()
&& forEach.getDepth() == depth
&& forEach.hasDeclaration())
return 0;
}
if (node instanceof JspContainerNode) {
ArrayList<JspNode> children = ((JspContainerNode) node).getChildren();
if (children == null)
return -1;
for (int i = 0; i < children.size(); i++) {
JspNode child = children.get(i);
int result = isFirst(child, depth);
if (result >= 0)
return result;
}
}
return -1;
}
/**
* Generates the code for the c:forEach tag.
*/
@Override
public void generate(JspJavaWriter out)
throws Exception
{
if (_items == null && _itemsAttr == null)
generateIntegerForEach(out);
else
generateCollectionForEach(out);
}
/**
* Generates the code for the c:forEach tag.
*/
public void generateIntegerForEach(JspJavaWriter out)
throws Exception
{
if (_begin == null && _beginAttr == null)
throw error(L.l("required attribute 'begin' missing from <{0}>",
getTagName()));
if (_end == null && _endAttr == null)
throw error(L.l("required attribute 'end' missing from <{0}>",
getTagName()));
int uniqueId = _gen.uniqueId();
String oldStatusVar = "_jsp_status_" + uniqueId;
if (_tagVar != null) {
out.println(_tagVar + " = _jsp_state.get" + _tagVar
+ "(pageContext, _jsp_parent_tag);");
}
String beginVar = "_jsp_begin_" + uniqueId;
String endVar = "_jsp_end_" + uniqueId;
String iVar = "_jsp_i_" + uniqueId;
out.print("int " + beginVar + " = ");
if (_beginAttr != null)
out.print(_beginAttr.generateValue(int.class));
else
out.print(generateValue(int.class, _begin));
out.println(";");
out.print("int " + endVar + " = ");
if (_endAttr != null)
out.print(_endAttr.generateValue(int.class));
else
out.print(generateValue(int.class, _end));
out.println(";");
String stepVar = null;
if (_step != null || _stepAttr != null) {
stepVar = "_jsp_step_" + uniqueId;
out.print("int " + stepVar + " = ");
if (_stepAttr != null)
out.print(_stepAttr.generateValue(int.class));
else
out.print(generateValue(int.class, _step));
out.println(";");
}
else
stepVar = "1";
if (_tagVar != null)
out.println(_tagVar +
".init(" +
beginVar +
", " +
endVar +
", " +
stepVar +
", " +
(_begin != null) +
", " +
(_end != null) +
", " +
(_step != null) +
");");
if (_varStatus != null) {
out.print("Object " + oldStatusVar + " = pageContext.putAttribute(\"");
out.print(escapeJavaString(_varStatus));
out.println("\", " + _tagVar + ");");
}
out.print("for (int " + iVar + " = " + beginVar + "; ");
out.print(iVar + " <= " + endVar + "; ");
out.println(iVar + " += " + stepVar + ") {");
out.pushDepth();
if (_var != null) {
out.print("pageContext.setAttribute(\"" + escapeJavaString(_var) + "\"");
out.println(", new Integer(" + iVar + "));");
}
if (_tagVar != null) {
out.println(_tagVar + ".setCurrent(" + iVar + ");");
}
generateChildren(out);
out.popDepth();
out.println("}");
if (_var != null) {
out.print("pageContext.pageSetOrRemove(\"");
out.print(escapeJavaString(_var));
out.println("\", null);");
}
if (_varStatus != null) {
out.print("if (" + oldStatusVar + " instanceof javax.servlet.jsp.jstl.core.LoopTagStatus)");
out.pushDepth();
out.print("pageContext.pageSetOrRemove(\"");
out.print(escapeJavaString(_varStatus));
out.println("\", "+oldStatusVar+");");
out.popDepth();
out.println("else");
out.pushDepth();
out.print("pageContext.pageSetOrRemove(\"");
out.print(escapeJavaString(_varStatus));
out.println("\", null);");
out.popDepth();
}
}
/**
* Generates the code for the c:forEach tag.
*/
public void generateCollectionForEach(JspJavaWriter out)
throws Exception
{
int uniqueId = _gen.uniqueId();
String oldStatusVar = "_jsp_status_" + uniqueId;
if (_tagVar != null) {
out.println(_tagVar + " = _jsp_state.get" + _tagVar
+ "(pageContext, _jsp_parent_tag);");
}
String itemsVar = "_jsp_items_" + uniqueId;
out.print("java.lang.Object " + itemsVar + " = ");
if (_itemsAttr != null)
out.print(_itemsAttr.generateValue(Object.class));
else
out.print(generateParameterValue(Object.class,
_items,
true,
_tag.getAttributeInfo("items"),
_parseState.isELIgnored()));
out.println(";");
String mapperVar = "_jsp_vm_" + uniqueId;
String deferredValue = null;
if (_items != null && _items.contains("#{")) {
deferredValue = "_caucho_value_expr_" + _gen.addValueExpr(_items, "");
}
if (deferredValue != null && _var != null) {
out.print("javax.el.ValueExpression " + mapperVar
+ " = _jsp_env.getVariableMapper().resolveVariable(\"");
out.print(escapeJavaString(_var));
out.println("\");");
}
String iterVar = "_jsp_iter_" + uniqueId;
String iVar = "_jsp_i_" + uniqueId;
out.println("java.util.Iterator " + iterVar
+ " = com.caucho.jstl.rt.CoreForEachTag.getIterator("
+ itemsVar + ");");
String beginVar = null;
if (_beginAttr != null || _begin != null) {
beginVar = "_jsp_begin_" + uniqueId;
out.print("int " + beginVar + " = ");
if (_beginAttr != null)
out.print(_beginAttr.generateValue(int.class));
else
out.print(generateValue(int.class, _begin));
out.println(";");
}
String intVar = "_jsp_int_" + uniqueId;
if (beginVar != null) {
out.print("for (int " + intVar + " = " + beginVar + ";");
out.println(intVar + " > 0; " + intVar + "--)");
out.println(" if (" + iterVar + ".hasNext()) " + iterVar + ".next();");
}
String endVar = null;
if (_endAttr != null || _end != null) {
endVar = "_jsp_end_" + uniqueId;
out.print("int " + endVar + " = ");
if (_endAttr != null)
out.print(_endAttr.generateValue(int.class));
else
out.print(generateValue(int.class, _end));
out.println(";");
}
String stepVar = null;
if (_step != null || _stepAttr != null) {
stepVar = "_jsp_step_" + uniqueId;
out.print("int " + stepVar + " = ");
if (_stepAttr != null)
out.print(_stepAttr.generateValue(int.class));
else
out.print(generateValue(int.class, _step));
out.println(";");
}
else
stepVar = "1";
if (_tagVar != null) {
out.print(_tagVar + ".init(");
if (beginVar != null)
out.print(beginVar + ", ");
else
out.print("0, ");
if (endVar != null)
out.print(endVar + ", ");
else
out.print("Integer.MAX_VALUE, ");
out.print(stepVar + ", ");
out.print((_begin != null) + ", ");
out.print((_end != null) + ", ");
out.println((_step != null) + ");");
}
if (_varStatus != null) {
out.print("Object " + oldStatusVar + " = pageContext.putAttribute(\"");
out.print(escapeJavaString(_varStatus));
out.println("\", " + _tagVar + ");");
}
if (endVar != null) {
String begin = beginVar == null ? "0" : beginVar;
out.print("for (int " + intVar + " = " + begin + "; ");
out.print(intVar + " <= " + endVar);
out.print(" && " + iterVar + ".hasNext(); ");
out.println(intVar + " += " + stepVar + ") {");
}
else
out.println("while (" + iterVar + ".hasNext()) {");
out.pushDepth();
out.println("Object " + iVar + " = " + iterVar + ".next();");
if (_tagVar != null) {
out.println(_tagVar + ".setCurrent(" + iVar + ", " + iterVar + ".hasNext());");
}
if (_var != null) {
if (deferredValue != null) {
out.print("_jsp_env.getVariableMapper().setVariable(\"");
out.print(escapeJavaString(_var));
out.print("\", ");
out.print("com.caucho.jstl.rt.CoreForEachTag.getExpr(");
out.print(deferredValue + ", " + _tagVar + ".getIndex(), " + itemsVar);
out.println(", \",\"));");
} else {
out.print("pageContext.setAttribute(\"" + escapeJavaString(_var) + "\"");
out.println(", " + iVar + ");");
}
}
generateChildren(out);
if (! stepVar.equals("1")) {
String stepI = "_jsp_si_" + uniqueId;
out.print("for (int " + stepI + " = " + stepVar + "; ");
out.println(stepI + " > 1; " + stepI + "--)");
out.println(" if (" + iterVar + ".hasNext()) " + iterVar + ".next();");
out.println("if (! " + iterVar + ".hasNext())");
out.println(" break;");
}
out.popDepth();
out.println("}");
if (_var != null) {
// restore EL variable
if (deferredValue != null) {
out.print("_jsp_env.getVariableMapper().setVariable(\"");
out.print(escapeJavaString(_var));
out.println("\", " + mapperVar + ");");
} else {
out.print("pageContext.pageSetOrRemove(\"");
out.print(escapeJavaString(_var));
out.println("\", null);");
}
}
if (_varStatus != null) {
out.print("if (" + oldStatusVar + " instanceof javax.servlet.jsp.jstl.core.LoopTagStatus)");
out.pushDepth();
out.print("pageContext.pageSetOrRemove(\"");
out.print(escapeJavaString(_varStatus));
out.println("\", "+oldStatusVar+");");
out.popDepth();
out.println("else");
out.pushDepth();
out.print("pageContext.pageSetOrRemove(\"");
out.print(escapeJavaString(_varStatus));
out.println("\", null);");
out.popDepth();
}
}
@Override
public boolean hasCustomTag()
{
// uses TagState directly
return true;
}
/**
* Generates code before the actual JSP.
*/
@Override
public void generateTagState(JspJavaWriter out)
throws Exception
{
if (! _isDeclaration) {
super.generateTagState(out);
return;
}
JspNode parentTagNode = getParent().getParentTagNode();
String tagClass;
if (_items == null && _itemsAttr == null)
tagClass = "com.caucho.jsp.IntegerLoopSupportTag";
else
tagClass = "com.caucho.jsp.IteratorLoopSupportTag";
out.print("private ");
out.print(tagClass);
out.println(" " + _tagVar + ";");
out.println();
out.print("final ");
out.print(tagClass);
out.println(" get" + _tagVar + "(PageContext pageContext, javax.servlet.jsp.tagext.JspTag _jsp_parent_tag) throws Throwable");
out.println("{");
out.pushDepth();
out.println("if (" + _tagVar + " == null) {");
out.pushDepth();
out.println(_tagVar + " = new " + tagClass + "();");
if (parentTagNode == null) {
out.println(_tagVar + ".setParent((javax.servlet.jsp.tagext.Tag) null);");
}
else {
out.println(_tagVar + ".setParent(" + parentTagNode.getCustomTagName() + ");");
}
out.popDepth();
out.println("}");
out.println();
out.println("return " + _tagVar + ";");
out.popDepth();
out.println("}");
super.generateTagState(out);
}
}