/*
* 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.*;
import com.caucho.config.*;
import com.caucho.util.*;
import com.caucho.xml.QName;
import com.caucho.vfs.*;
import javax.servlet.jsp.tagext.*;
import javax.faces.component.*;
import javax.faces.event.*;
import javax.el.*;
import java.lang.reflect.*;
import java.util.*;
import java.io.*;
/**
* Represents a custom tag.
*/
public class JsfTagNode extends JsfNode
{
private JspNode _next;
private Class _componentClass;
private String _facetName;
private Attr _idAttr;
private String _prevJsfId;
private Attr _bindingAttr;
private ArrayList<Attr> _attrList = new ArrayList<Attr>();
public void setComponentClass(Class cl)
{
_componentClass = cl;
}
public void setNext(JspNode next)
{
_next = next;
}
/**
* Adds an attribute.
*/
public void addAttribute(QName qName, String value)
throws JspParseException
{
String name = qName.getName();
String setterName = ("set" + Character.toUpperCase(name.charAt(0))
+ name.substring(1));
if (name.equals("action")
&& ActionSource2.class.isAssignableFrom(_componentClass))
setterName = "setActionExpression";
else if (name.equals("actionListener")
&& ActionSource2.class.isAssignableFrom(_componentClass))
setterName = "addActionListener";
else if (name.equals("escape") &&
UISelectItem.class.isAssignableFrom(_componentClass))
setterName = "setItemEscaped";
Method method = findSetter(_componentClass, setterName);
if (method != null) {
_attrList.add(new Attr(name, method, value));
}
else if (name.equals("binding")) {
if (! value.startsWith("#{"))
throw error(L.l("JSF binding attribute requires a deferred value at '{0}'",
value));
Attr attr = new Attr(name, method, value);
_bindingAttr = attr;
_attrList.add(attr);
}
else {
super.addAttribute(qName, value);
}
}
/**
* Adds a JspAttribute attribute.
*
* @param qName the name of the attribute.
* @param value the value of the attribute.
*/
public void addAttribute(QName qName, JspAttribute value)
throws JspParseException
{
if (value.isStatic()) {
addAttribute(qName, value.getStaticText().trim());
}
else {
String name = qName.getName();
String setterName = ("set" + Character.toUpperCase(name.charAt(0))
+ name.substring(1));
Method method = findSetter(_componentClass, setterName);
if (method != null) {
_attrList.add(new Attr(name, method, value));
}
else {
super.addAttribute(qName, value);
return;
}
}
}
/**
* Called after all the attributes from the tag.
*/
public void endAttributes()
throws JspParseException
{
if (_parent instanceof JsfFacetNode) {
_facetName = ((JsfFacetNode) _parent).getName();
}
else if (_parent instanceof CustomTag) {
final QName qName = _parent.getQName();
if ((qName.getNamespaceURI().indexOf("http://java.sun.com/jsf/core") > -1)
&& "facet".equals(qName.getLocalName())) {
final Object facetName = ((CustomTag) _parent).getAttribute("name");
if (facetName instanceof String) {
_facetName = facetName.toString();
}
}
}
_idAttr = findAttribute("id");
if (_idAttr != null)
addJsfId(_parent);
else if (isJsfIdRequired())
addJsfId(this);
if (_idAttr == null) {
_prevJsfId = getPrevJsfId();
}
}
public void endElement()
{
if (isJsfParentRequired()) {
addJsfId(this);
}
}
String getJsfId()
{
if (_idAttr != null)
return _idAttr.getValue();
else
return null;
}
private boolean isJsfIdRequired()
{
if (EditableValueHolder.class.isAssignableFrom(_componentClass))
return true;
else if (ActionSource.class.isAssignableFrom(_componentClass))
return true;
else if (ActionSource2.class.isAssignableFrom(_componentClass))
return true;
else if (_bindingAttr != null)
return true;
else if (UISelectItem.class.isAssignableFrom(_componentClass))
return true;
else if (UISelectItems.class.isAssignableFrom(_componentClass))
return true;
JspNode parent = _parent;
ArrayList<JspNode> children = parent.getChildren();
boolean isJsfIdRequired = false;
for (int i = 0; i < children.size(); i++) {
JspNode child = children.get(i);
if (child == this)
return isJsfIdRequired;
else if (child instanceof JsfTagNode) {
JsfTagNode jsfNode = (JsfTagNode) child;
String id = jsfNode.getJsfId();
if (id != null)
isJsfIdRequired = false;
}
else if (child.isJsfParentRequired())
isJsfIdRequired = true;
}
return false;
}
private String getPrevJsfId()
{
JspNode parent = _parent;
ArrayList<JspNode> children = parent.getChildren();
if (children != null) {
boolean isFound = false;
for (int i = children.size() - 1; i >= 0; i--) {
JspNode child = children.get(i);
if (child == this)
isFound = true;
else if (isFound && child instanceof JsfTagNode) {
JsfTagNode jsfNode = (JsfTagNode) child;
String id = jsfNode.getJsfId();
if (id != null)
return id;
}
}
}
return null;
}
private static void addJsfId(JspNode node)
{
if (node == null)
return;
addJsfId(node.getParent());
if (node instanceof JsfTagNode) {
JsfTagNode jsfNode = (JsfTagNode) node;
if (jsfNode._idAttr != null)
return;
jsfNode._idAttr = jsfNode.findAttribute("id");
if (jsfNode._idAttr == null) {
Method method = jsfNode.findSetter(jsfNode._componentClass, "setId");
if (method == null)
throw new IllegalStateException(jsfNode._componentClass + " id");
String id = "j_id_" + jsfNode._gen.generateJspId();
jsfNode._idAttr = new Attr("id", method, id);
jsfNode._attrList.add(jsfNode._idAttr);
}
}
}
private Attr findAttribute(String name)
{
for (int i = 0; i < _attrList.size(); i++) {
Attr attr = _attrList.get(i);
if (attr.getName().equals(name))
return attr;
}
return null;
}
/**
* Generates the XML text representation for the tag validation.
*
* @param os write stream to the generated XML.
*/
public void printXml(WriteStream os)
throws IOException
{
if (_next != null)
_next.printXml(os);
}
/**
* generates prologue data.
*/
@Override
public void generatePrologue(JspJavaWriter out)
throws Exception
{
_var = "_jsp_comp" + _gen.uniqueId();
super.generatePrologue(out);
}
/**
* Generates the code for a custom tag.
*
* @param out the output writer for the generated java.
*/
public void generate(JspJavaWriter out)
throws Exception
{
String contextVar = "null";
if (_gen.isJsfPrologueInit())
contextVar = "_jsp_faces_context";
String parentVar = _parent.getJsfVar();
String parentBodyVar = _parent.getJsfBodyVar();
String prevId;
String oldParentVar = null;
String bindingVar = null;
if (isJsfParentRequired()) {
oldParentVar = "_jsp_jsf_parent_" + _gen.uniqueId();
out.println("Object " + oldParentVar
+ " = request.getAttribute(\"caucho.jsf.parent\");");
}
if (_prevJsfId != null)
prevId = "\"" + _prevJsfId + "\"";
else
prevId = null;
if (parentBodyVar != null) {
out.println("com.caucho.jsp.jsf.JsfTagUtil.addVerbatim("
+ parentVar
+ ", " + prevId
+ ", " + parentBodyVar + ");");
}
if (! hasBodyContent()) {
}
else if (parentBodyVar != null)
_bodyVar = parentBodyVar;
else {
_bodyVar = "_jsp_body" + _gen.uniqueId();
out.println("com.caucho.jsp.BodyContentImpl " + _bodyVar
+ " = (com.caucho.jsp.BodyContentImpl) pageContext.pushBody();");
out.println("out = " + _bodyVar + ";");
}
String className;
if (_bindingAttr != null)
className = UIComponent.class.getName();
else
className = _componentClass.getName();
out.println(className + " " + _var + ";");
if (_bindingAttr != null) {
bindingVar = ("_caucho_value_expr_" +
_gen.addValueExpr(_bindingAttr.getValue(),
UIComponent.class.getName()));
}
if (_facetName != null) {
out.print(_var + " = (" + className + ")");
out.println(" com.caucho.jsp.jsf.JsfTagUtil.findFacet("
+ contextVar
+ ", request"
+ ", " + parentVar
+ ", \"" + _facetName + "\");");
out.println("if (" + _var + " == null) {");
out.pushDepth();
out.print(_var + " = (" + className + ")");
out.println(" com.caucho.jsp.jsf.JsfTagUtil.addFacet("
+ contextVar
+ ", request"
+ ", " + parentVar
+ ", \"" + _facetName + "\""
+ ", " + bindingVar
+ ", " + _componentClass.getName() + ".class);");
}
else if (_idAttr != null) {
out.print(_var + " = (" + className + ")");
out.println(" com.caucho.jsp.jsf.JsfTagUtil.findPersistent("
+ contextVar
+ ", request"
+ ", " + parentVar
+ ", \"" + _idAttr.getValue() + "\");");
out.println("if (" + _var + " == null) {");
out.pushDepth();
out.print(_var + " = (" + className + ")");
out.println(" com.caucho.jsp.jsf.JsfTagUtil.addPersistent("
+ contextVar
+ ", request"
+ ", " + parentVar
+ ", " + bindingVar
+ ", " + _componentClass.getName() + ".class);");
}
else {
out.print(_var + " = (" + className + ")");
out.println(" com.caucho.jsp.jsf.JsfTagUtil.addTransient("
+ contextVar
+ ", request"
+ ", " + parentVar
+ ", " + prevId
+ ", " + _componentClass.getName() + ".class);");
}
for (int i = 0; i < _attrList.size(); i++) {
Attr attr = (Attr) _attrList.get(i);
Method method = attr.getMethod();
Class type = null;
if (method != null)
type = method.getParameterTypes()[0];
else if (attr.getName().equals("binding"))
type = UIComponent.class;
JspAttribute jspAttr = attr.getAttr();
String value = attr.getValue();
if (jspAttr != null) {
generateSetParameter(out, _var, jspAttr, method,
true, null, false, false, null);
}
else if (ActionListener.class.isAssignableFrom(type)) {
String exprVar = "_caucho_method_expr_" + _gen.addMethodExpr(value, "void foo(javax.faces.event.ActionEvent)");
out.println(_var + ".addActionListener(new javax.faces.event.MethodExpressionActionListener(" + exprVar + "));");
}
else if ("validator".equals(attr.getName())
&& UIInput.class.isAssignableFrom(_componentClass)) {
out.print("((javax.faces.component.UIInput)" + _var + ").addValidator(new javax.faces.validator.MethodExpressionValidator(");
String signature
= "void foo(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.Object)";
String exprVar = "_caucho_method_expr_" +
_gen.addMethodExpr(value, signature);
out.println(exprVar + "));");
}
else if ("valueChangeListener".equals(attr.getName())
&& UIInput.class.isAssignableFrom(_componentClass)) {
out.print("((javax.faces.component.UIInput)" + _var + ").addValueChangeListener(new javax.faces.event.MethodExpressionValueChangeListener(");
String signature = "void foo(javax.faces.event.ValueChangeEvent)";
String exprVar = "_caucho_method_expr_" +
_gen.addMethodExpr(value, signature);
out.println(exprVar + "));");
}
else if (_bindingAttr != null && ! "id".equals(attr.getName())
|| (value.indexOf("#{") >= 0
&& ! ValueExpression.class.isAssignableFrom(type)
&& ! MethodExpression.class.isAssignableFrom(type))) {
// jsf/3153
out.print(_var + ".setValueExpression(\"" + attr.getName() + "\", ");
String exprVar = "_caucho_value_expr_" + _gen.addValueExpr(value, type.getName());
out.println(exprVar + ");");
}
else {
out.print(_var + "." + method.getName() + "(");
out.print(generateParameterValue(type, value, true, null, false));
out.println(");");
}
}
if (_idAttr != null || _facetName != null) {
if (oldParentVar != null)
out.println("request.setAttribute(\"caucho.jsf.parent\""
+ ", new com.caucho.jsp.jsf.JsfComponentTag("
+ _var + ", true, " + _bodyVar + "));");
out.popDepth();
out.println("}");
if (oldParentVar != null) {
out.println("else");
out.println(" request.setAttribute(\"caucho.jsf.parent\""
+ ", new com.caucho.jsp.jsf.JsfComponentTag("
+ _var + ", false, " + _bodyVar + "));");
}
}
generateChildren(out);
if (oldParentVar != null) {
out.println("request.setAttribute(\"caucho.jsf.parent\", "
+ oldParentVar + ");");
}
if (_bodyVar != null && parentBodyVar == null) {
out.println("out = pageContext.popBody();");
out.println("pageContext.releaseBody(" + _bodyVar + ");");
}
}
}