/*
* 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 java.util.ArrayList;
import javax.servlet.jsp.tagext.BodyTag;
import javax.servlet.jsp.tagext.IterationTag;
import javax.servlet.jsp.tagext.JspIdConsumer;
import javax.servlet.jsp.tagext.TagAttributeInfo;
import javax.servlet.jsp.tagext.TryCatchFinally;
import javax.servlet.jsp.tagext.VariableInfo;
import com.caucho.jsp.AnalyzedTag;
import com.caucho.xml.QName;
/**
* Represents a custom tag.
*/
public class CustomTag extends GenericTag
{
/**
* Generates code before the actual JSP.
*/
@Override
public void generateTagState(JspJavaWriter out)
throws Exception
{
if (isDeclaringInstance()) {
out.print("private ");
out.printClass(_tagClass);
out.println(" " + _tag.getId() + ";");
out.println();
out.print("final ");
out.printClass(_tagClass);
out.println(" get" + _tag.getId() + "(PageContext pageContext, javax.servlet.jsp.tagext.JspTag _jsp_parent_tag) throws Throwable");
out.println("{");
out.pushDepth();
out.println("if (" + _tag.getId() + " == null) {");
out.pushDepth();
generateTagInit(out);
out.popDepth();
out.println("}");
out.println();
out.println("return " + _tag.getId() + ";");
out.popDepth();
out.println("}");
}
super.generateTagState(out);
}
/**
* Generates code before the actual JSP.
*/
@Override
public void generateTagRelease(JspJavaWriter out)
throws Exception
{
if (isDeclaringInstance()) {
out.println("if (" + _tag.getId() + " != null)");
out.println(" " + _tag.getId() + ".release();");
}
super.generateTagRelease(out);
}
/**
* Generates the code for a custom tag.
*
* @param out the output writer for the generated java.
*/
@Override
public void generate(JspJavaWriter out)
throws Exception
{
String name = _tag.getId();
String tagHackVar = "_jsp_endTagHack" + _gen.uniqueId();
Class<?> cl = _tagClass;
AnalyzedTag analyzedTag = _tag.getAnalyzedTag();
boolean isIterator = (IterationTag.class.isAssignableFrom(cl)
|| BodyTag.class.isAssignableFrom(cl));
boolean isBodyTag = BodyTag.class.isAssignableFrom(cl);
boolean isCatch = TryCatchFinally.class.isAssignableFrom(cl);
boolean isEmpty = isEmpty();
boolean usesTagBody = (isBodyTag && ! isEmpty
&& analyzedTag.getStartReturnsBuffered());
boolean hasEndTag = analyzedTag.getDoEnd();
if ("empty".equalsIgnoreCase(getBodyContent())) {
if (! isEmpty)
throw error(L.l("<{0}> expects an empty body", getTagName()));
}
if (usesTagBody && hasEndTag)
out.println("com.caucho.jsp.BodyContentImpl " + tagHackVar + " = null;");
else
tagHackVar = "out";
if (! isDeclared()) {
out.println(name + " = _jsp_state.get" + name + "(pageContext, _jsp_parent_tag);");
}
if (JspIdConsumer.class.isAssignableFrom(_tag.getTagClass())) {
out.println(name + ".setJspId(\"jsp" + _gen.generateJspId() + "\");");
/*
String shortName = className;
int p = shortName.lastIndexOf('.');
if (p >= 0)
shortName = shortName.substring(p + 1);
out.println(name + ".setJspId(\"" + shortName + "-" + _gen.generateJspId() + "\");");
*/
}
fillAttributes(out, name);
printVarDeclare(out, VariableInfo.AT_BEGIN);
String oldTag = "_jsp_writer" + _gen.uniqueId();
if (analyzedTag.getDoCatch())
out.println("javax.servlet.jsp.JspWriter " + oldTag + " = out;");
if (analyzedTag.getDoCatch() || analyzedTag.getDoFinally()) {
out.println("try {");
out.pushDepth();
}
boolean hasStartTag = analyzedTag.getDoStart();
int startCount = ((analyzedTag.getStartReturnsSkip() ? 1 : 0)
+ (analyzedTag.getStartReturnsInclude() ? 1 : 0)
+ (analyzedTag.getStartReturnsBuffered() ? 1 : 0));
int thisId = _gen.uniqueId();
if (! hasStartTag) {
}
else if (startCount == 1) {
out.println(name + ".doStartTag();");
}
else {
out.println("int _jspEval" + thisId + " = " + name + ".doStartTag();");
}
printVarAssign(out, VariableInfo.AT_BEGIN);
if (analyzedTag.getStartReturnsSkip()
&& ! analyzedTag.getStartReturnsInclude()
&& ! analyzedTag.getStartReturnsBuffered()) {
// jsp/18cp
generateChildrenEmpty();
}
else if (isEmpty) {
// jsp/18kc
/*
if (isBodyTag)
out.println(" " + name + ".setBodyContent((javax.servlet.jsp.tagext.BodyContent) null);");
*/
}
else {
if (startCount > 1 && analyzedTag.getStartReturnsSkip()) {
out.println("if (_jspEval" + thisId + " != javax.servlet.jsp.tagext.Tag.SKIP_BODY) {");
out.pushDepth();
}
else if ((hasVarDeclaration(VariableInfo.NESTED)
|| childHasScriptlet())
&& ! (analyzedTag.getDoCatch()
|| analyzedTag.getDoFinally()
|| (analyzedTag.getDoAfter()
&& analyzedTag.getAfterReturnsAgain()))) {
out.println("{");
out.pushDepth();
}
if (usesTagBody) {
if (analyzedTag.getStartReturnsBuffered()
&& analyzedTag.getStartReturnsInclude()) {
out.println("if (_jspEval" + thisId + " == javax.servlet.jsp.tagext.BodyTag.EVAL_BODY_BUFFERED) {");
out.pushDepth();
}
out.println("out = pageContext.pushBody();");
if (hasEndTag) {
out.println(tagHackVar + " = (com.caucho.jsp.BodyContentImpl) out;");
out.println(name + ".setBodyContent(" + tagHackVar + ");");
}
else
out.println(name + ".setBodyContent((javax.servlet.jsp.tagext.BodyContent) " + tagHackVar + ");");
if (analyzedTag.getDoInit())
out.println(name + ".doInitBody();");
if (analyzedTag.getStartReturnsBuffered()
&& analyzedTag.getStartReturnsInclude()) {
out.popDepth();
out.println("}");
// jsp/18kf - req by JSP TCK
/*
if (_tag.getBodyContent()) {
out.println("else");
out.println(" " + name + ".setBodyContent((javax.servlet.jsp.tagext.BodyContent) null);");
}
*/
}
}
else if (isBodyTag && _tag.getBodyContent())
out.println(name + ".setBodyContent((javax.servlet.jsp.tagext.BodyContent) null);");
if (analyzedTag.getDoAfter() && analyzedTag.getAfterReturnsAgain()) {
out.println("do {");
out.pushDepth();
}
out.setLocation(getFilename(), getStartLine());
if (_children != null)
printVarDeclaration(out, VariableInfo.NESTED);
out.setLocation(getFilename(), getStartLine());
generateChildren(out);
out.setLocation(getFilename(), getEndLine());
if (analyzedTag.getDoAfter() && analyzedTag.getAfterReturnsAgain()) {
out.popDepth();
out.println("} while (" + name + ".doAfterBody() == javax.servlet.jsp.tagext.IterationTag.EVAL_BODY_AGAIN);");
}
else if (analyzedTag.getDoAfter()) {
out.println(name + ".doAfterBody();");
}
if (usesTagBody) {
if (analyzedTag.getStartReturnsBuffered()
&& analyzedTag.getStartReturnsInclude()) {
out.println("if (_jspEval" + thisId + " == javax.servlet.jsp.tagext.BodyTag.EVAL_BODY_BUFFERED)");
if (hasEndTag)
out.println(" out = pageContext.popBody();");
else
out.println(" out = pageContext.popAndReleaseBody();");
}
else if (analyzedTag.getStartReturnsBuffered()) {
if (hasEndTag)
out.println("out = pageContext.popBody();");
else
out.println("out = pageContext.popAndReleaseBody();");
}
}
if (startCount > 1 && analyzedTag.getStartReturnsSkip()) {
out.popDepth();
out.println("}");
}
else if (isEmpty) {
}
else if ((hasVarDeclaration(VariableInfo.NESTED)
|| childHasScriptlet())
&& ! (analyzedTag.getDoCatch()
|| analyzedTag.getDoFinally()
|| (analyzedTag.getDoAfter()
&& analyzedTag.getAfterReturnsAgain()))) {
out.popDepth();
out.println("}");
}
}
out.setLocation(getFilename(), getEndLine());
int endCount = ((analyzedTag.getEndReturnsSkip() ? 1 : 0)
+ (analyzedTag.getEndReturnsEval() ? 1 : 0));
String endVar = "_jsp_end_" + _gen.uniqueId();
if (! hasEndTag) {
}
else if (endCount > 1)
out.println("int " + endVar + " = " + name + ".doEndTag();");
else
out.println(name + ".doEndTag();");
if (! hasEndTag || ! usesTagBody) {
}
else if (hasStartTag
&& (analyzedTag.getStartReturnsSkip()
|| analyzedTag.getStartReturnsInclude())) {
out.println("if (" + tagHackVar + " != null) {");
out.println(" pageContext.releaseBody(" + tagHackVar + ");");
out.println(" " + tagHackVar + " = null;");
out.println("}");
}
else {
out.println("pageContext.releaseBody(" + tagHackVar + ");");
}
if (analyzedTag.getEndReturnsSkip()) {
if (hasEndTag && endCount > 1)
out.println("if (" + endVar + " == javax.servlet.jsp.tagext.Tag.SKIP_PAGE)");
else
out.println("if (true)");
if (_gen.isTag() || isInFragment())
out.println(" throw new SkipPageException();");
else
out.println(" return;");
}
if (analyzedTag.getDoCatch()) {
String t = "_jsp_exn_" + _gen.uniqueId();
out.popDepth();
out.println("} catch (Throwable " + t + ") {");
out.println(" pageContext.setWriter(" + oldTag + ");");
out.println(" out = " + oldTag + ";");
out.println(" " + name + ".doCatch(" + t + ");");
out.pushDepth();
}
if (analyzedTag.getDoFinally()) {
out.popDepth();
out.println("} finally {");
out.println(" " + name + ".doFinally();");
out.pushDepth();
}
if (analyzedTag.getDoCatch() || analyzedTag.getDoFinally()) {
out.popDepth();
out.println("}");
}
printVarDeclaration(out, VariableInfo.AT_END);
out.setLocation(getFilename(), getEndLine());
}
/**
* Generates the initialization code for the tag.
*
* @param out the output stream
*/
private void generateTagInit(JspJavaWriter out)
throws Exception
{
String var = _tag.getId();
if (_tag.getAnalyzedTag().getHasInjection()) {
out.print(var + " = (");
out.printClass(_tag.getTagClass());
out.println(") _jsp_inject_" + _tag.getId() + ".create();");
}
else {
out.print(var + " = new ");
out.printClass(_tag.getTagClass());
out.println("();");
}
JspNode parentTagNode = getParent().getParentTagNode();
out.println(var + ".setPageContext(pageContext);");
if (parentTagNode == null) {
// jsp/100h
out.println("if (_jsp_parent_tag instanceof javax.servlet.jsp.tagext.Tag)");
out.println(" " + var + ".setParent((javax.servlet.jsp.tagext.Tag) _jsp_parent_tag);");
out.println("else if (_jsp_parent_tag instanceof javax.servlet.jsp.tagext.SimpleTag)");
out.println(" " + var + ".setParent(new javax.servlet.jsp.tagext.TagAdapter((javax.servlet.jsp.tagext.SimpleTag) _jsp_parent_tag));");
out.println("else");
out.println(" " + var + ".setParent((javax.servlet.jsp.tagext.Tag) null);");
}
else if (parentTagNode.isSimpleTag()) {
String parentName = parentTagNode.getCustomTagName();
out.println("if (" + parentName + "_adapter == null)");
out.println(" " + parentName + "_adapter = new javax.servlet.jsp.tagext.TagAdapter(" + parentName + ");");
out.println(var + ".setParent(" + parentName + "_adapter);");
}
else {
String parentName = parentTagNode.getCustomTagName();
out.println(var + ".setParent((javax.servlet.jsp.tagext.Tag) " + parentName + ");");
}
ArrayList<QName> names = _tag.getAttributeNames();
for (int i = 0; i < names.size(); i++) {
QName name = names.get(i);
String value = _tag.getAttribute(name);
if (value == null)
continue;
TagAttributeInfo attrInfo = _tag.getAttributeInfo(name.getLocalName());
boolean isRequestTime = true;
if (attrInfo != null)
isRequestTime = attrInfo.canBeRequestTime();
generateSetAttribute(out, var, name, value,
isRequestTime,
false, attrInfo);
}
}
/**
* Returns true if the node or one of its children is a scriptlet
*/
private boolean childHasScriptlet()
{
ArrayList<JspNode> children = getChildren();
if (children == null)
return false;
for (int i = 0; i < children.size(); i++) {
JspNode child = children.get(i);
if (child instanceof JspScriptlet || child instanceof JspExpression)
return true;
}
return false;
}
}