/*
* 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 SoftwareFoundation, 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.util.CharBuffer;
import com.caucho.util.L10N;
import com.caucho.util.LineCompileException;
import com.caucho.vfs.WriteStream;
import com.caucho.xml.QName;
import com.caucho.xml.XmlChar;
import java.io.IOException;
import java.util.ArrayList;
/**
* A node which can contain other nodes.
*/
public abstract class JspContainerNode extends JspNode {
static final L10N L = new L10N(JspContainerNode.class);
protected ArrayList<QName> _attributeNames = new ArrayList<QName>();
protected ArrayList<Object> _attributeValues = new ArrayList<Object>();
protected ArrayList<JspNode> _children = new ArrayList<JspNode>();
private ArrayList<JspAttribute> _attrChildren;
protected boolean _hasJspAttribute;
/**
* Adds an attribute.
*
* @param name the name of the attribute.
* @param value the value of the attribute.
*/
@Override
public void addAttribute(QName name, String value)
throws JspParseException
{
if (name == null)
throw new NullPointerException();
if (_attributeNames.indexOf(name) >= 0)
throw error(L.l("'{0}' is a duplicate attribute name. Attributes must occur only once in a tag.", name.getName()));
_attributeNames.add(name);
_attributeValues.add(value);
}
/**
* Adds a JspAttribute attribute.
*
* @param name the name of the attribute.
* @param value the value of the attribute.
*/
@Override
public void addAttribute(QName name, JspAttribute value)
throws JspParseException
{
if (name == null)
throw new NullPointerException();
if (_attributeNames.indexOf(name) >= 0)
throw error(L.l("'{0}' is a duplicate attribute name. Attributes must occur only once in a tag.", name.getName()));
_attributeNames.add(name);
_attributeValues.add(value);
_gen.addFragment(value);
}
/**
* Returns the named attribute.
*/
public Object getAttribute(String name)
throws JspParseException
{
for (int i = 0; i < _attributeNames.size(); i++) {
if (name.equals(_attributeNames.get(i).getName()))
return _attributeValues.get(i);
}
return null;
}
/**
* Adds a child node.
*/
@Override
public void addChild(JspNode node)
throws JspParseException
{
node.setParent(this);
if (node instanceof JspAttribute) {
JspAttribute attr = (JspAttribute) node;
QName name = attr.getName();
addAttribute(name, attr);
_hasJspAttribute = true;
int i = 0;
while (_children != null && i < _children.size()) {
JspNode child = _children.get(i);
if (child instanceof StaticText) {
String text = ((StaticText) child).getText();
if (isWhitespace(text))
_children.remove(i);
else
throw child.error(L.l("tags using jsp:attribute must put body content in a jsp:body tag."));
}
else if (child instanceof JspBody)
i++;
else if (child instanceof JspAttribute)
i++;
else {
throw child.error(L.l("tags using jsp:attribute must put body content in a jsp:body tag"));
}
}
}
else if (_hasJspAttribute && ! (node instanceof JspBody)) {
throw node.error(L.l("tags using jsp:attribute must put body content in a jsp:body tag"));
}
else if (node instanceof StaticText) {
StaticText text = (StaticText) node;
String data = text.getText();
boolean isXml = _gen.isXml();
for (JspNode ptr = this; ptr != null; ptr = ptr.getParent()) {
if (ptr instanceof JspRoot)
isXml = true;
}
if (_children == null)
_children = new ArrayList<JspNode>();
for (int i = 0;
i < data.length();
i++) {
if (! XmlChar.isWhitespace(data.charAt(i))) {
_children.add(node);
return;
}
}
if (! isXml)
_children.add(node);
}
else if (node instanceof JspBody && hasJspBody()) {
throw node.error(L.l("tags may only have a single jsp:body tag"));
}
else {
if (_children == null)
_children = new ArrayList<JspNode>();
_children.add(node);
}
}
protected void addAttributeChild(JspAttribute attr)
{
if (_attrChildren == null)
_attrChildren = new ArrayList<JspAttribute>();
_attrChildren.add(attr);
}
private boolean hasJspBody()
{
if (_children == null)
return false;
for (int i = _children.size() - 1; i >= 0; i--) {
if (_children.get(i) instanceof JspBody)
return true;
}
return false;
}
/**
* Adds a child node after its done initializing.
*/
@Override
public void addChildEnd(JspNode node)
throws JspParseException
{
}
/**
* True if the jsf-parent setting is required.
*/
@Override
public boolean isJsfParentRequired()
{
if (_children == null)
return false;
for (int i = _children.size() - 1; i >= 0; i--) {
if (_children.get(i).isJsfParentRequired())
return true;
}
return false;
}
/**
* Returns true if the node is empty
*/
public boolean isEmpty()
{
if (_children == null || _children.size() == 0)
return true;
for (int i = 0; i < _children.size(); i++) {
JspNode child = _children.get(i);
if (child instanceof JspBody) {
JspBody body = (JspBody) child;
return body.isEmpty();
}
else if (child instanceof StaticText) {
StaticText text = (StaticText) child;
if (! text.isWhitespace())
return false;
}
else
return false;
}
return false;
}
/**
* Returns the static text.
*/
@Override
public void getStaticText(CharBuffer cb)
{
if (_children == null)
return;
for (int i = 0; i < _children.size(); i++)
_children.get(i).getStaticText(cb);
}
/**
* Set true if the node contains a child tag.
*/
@Override
public boolean hasCustomTag()
{
for (int i = 0; _children != null && i < _children.size(); i++) {
JspNode child = _children.get(i);
if (child instanceof CustomTag)
return true;
if (child.hasCustomTag())
return true;
}
for (int i = 0;
_attributeValues != null && i < _attributeValues.size();
i++) {
Object value = _attributeValues.get(i);
if (value instanceof CustomTag)
return true;
else if (value instanceof JspNode &&
((JspNode) value).hasCustomTag())
return true;
}
return false;
}
/**
* Set true if the node contains a child tag.
*/
public boolean hasTag()
{
for (int i = 0; _children != null && i < _children.size(); i++) {
JspNode child = _children.get(i);
if (child instanceof CustomTag ||
child instanceof CustomSimpleTag)
return true;
if (child.hasTag())
return true;
}
for (int i = 0;
_attributeValues != null && i < _attributeValues.size();
i++) {
Object value = _attributeValues.get(i);
if (value instanceof CustomTag || value instanceof CustomSimpleTag)
return true;
else if (value instanceof JspNode &&
((JspNode) value).hasTag())
return true;
}
return false;
}
/**
* Adds a text node.
*/
@Override
public JspNode addText(String text)
throws JspParseException
{
if (! _hasJspAttribute) {
JspNode node = new StaticText(_gen, text, this);
addChild(node);
return node;
}
else if (! isWhitespace(text)) {
throw error(L.l("tags using jsp:attribute must put body content in a jsp:body tag"));
}
return null;
}
protected boolean isWhitespace(String text)
{
for (int i = 0; i < text.length(); i++) {
if (! Character.isWhitespace(text.charAt(i)))
return false;
}
return true;
}
/**
* Returns the children.
*/
public ArrayList<JspNode> getChildren()
{
return _children;
}
/**
* Has children.
*/
public boolean hasChildren()
{
return ! isEmpty();
}
/**
* True if the node has scripting
*/
public boolean hasScripting()
{
for (int i = 0; _children != null && i < _children.size(); i++) {
if (_children.get(i).hasScripting()) {
return true;
}
}
return false;
}
/**
* True if the node has scripting element (i.e. not counting rtexpr values)
*/
@Override
public boolean hasScriptingElement()
{
for (int i = 0; _children != null && i < _children.size(); i++) {
if (_children.get(i).hasScriptingElement()) {
return true;
}
}
return false;
}
/**
* Finds the first scripting node
*/
@Override
public JspNode findScriptingNode()
{
for (int i = 0; _children != null && i < _children.size(); i++) {
JspNode node = _children.get(i).findScriptingNode();
if (node != null)
return node;
}
if (hasScripting())
return this;
else
return null;
}
/**
* True if the node has scripting element (i.e. not counting rtexpr values)
*/
public boolean isSingleExpression()
{
for (int i = 0; _children != null && i < _children.size(); i++) {
JspNode node = _children.get(i);
if (node instanceof JspExpression) {
continue;
}
if (node.isStatic() && node.getStaticText().trim().length() == 0) {
continue;
}
return false;
}
return true;
}
/**
* Returns true if the children are static.
*/
public boolean isChildrenStatic()
{
if (_children == null)
return true;
for (int i = 0; i < _children.size(); i++) {
if (! _children.get(i).isStatic())
return false;
}
return true;
}
/**
* Generates the XML text representation for the tag validation.
*
* @param os write stream to the generated XML.
*/
public void printXmlChildren(WriteStream os)
throws IOException
{
if (_children == null)
return;
for (int i = 0; i < _children.size(); i++) {
JspNode child = _children.get(i);
child.printXml(os);
}
}
/**
* generates data for prologue children.
*/
@Override
public void generatePrologueChildren(JspJavaWriter out)
throws Exception
{
if (_children == null)
return;
for (int i = 0; i < _children.size(); i++) {
JspNode child = _children.get(i);
child.generatePrologue(out);
}
}
/**
* generates data for declaration children.
*/
public void generateDeclarationChildren(JspJavaWriter out)
throws IOException
{
if (_children == null)
return;
for (int i = 0; i < _children.size(); i++) {
JspNode child = _children.get(i);
child.generateDeclaration(out);
}
}
/**
* generates data for tag state children.
*/
public void generateTagStateChildren(JspJavaWriter out)
throws Exception
{
if (_children != null) {
for (int i = 0; i < _children.size(); i++) {
JspNode child = _children.get(i);
child.generateTagState(out);
}
}
if (_attrChildren != null) {
for (JspNode child : _attrChildren) {
child.generateTagState(out);
}
}
}
/**
* generates data for tag state children.
*/
public void generateTagReleaseChildren(JspJavaWriter out)
throws Exception
{
if (_children != null) {
for (int i = 0; i < _children.size(); i++) {
JspNode child = _children.get(i);
child.generateTagRelease(out);
}
}
if (_attrChildren != null) {
for (JspNode child : _attrChildren) {
child.generateTagRelease(out);
}
}
}
/**
* Generates the code for the children.
*
* @param out the output writer for the generated java.
*/
public void generateChildren(JspJavaWriter out)
throws Exception
{
if (_children == null)
return;
for (int i = 0; i < _children.size(); i++) {
JspNode child = _children.get(i);
child.generateStartLocation(out);
try {
child.generate(out);
} catch (Exception e) {
if (e instanceof LineCompileException)
throw e;
else
throw child.error(e);
}
child.generateEndLocation(out);
}
}
/**
* Generates the code for the children.
*/
public void generateChildrenEmpty()
throws Exception
{
if (_children == null)
return;
for (int i = 0; i < _children.size(); i++) {
JspNode child = _children.get(i);
child.generateEmpty();
}
}
/**
* Generates static text.
*
* @param out the output writer for the generated java.
*/
public void generateStatic(JspJavaWriter out)
throws Exception
{
if (_children == null)
return;
for (int i = 0; i < _children.size(); i++) {
JspNode child = _children.get(i);
out.setLocation(child.getFilename(), child.getStartLine());
child.generateStatic(out);
// out.setLocation(child.getFilename(), child.getEndLine());
}
}
/**
* generates data for tag state children.
*/
@Override
public void generateClassEpilogueChildren(JspJavaWriter out)
throws IOException
{
if (_children != null) {
for (int i = 0; i < _children.size(); i++) {
JspNode child = _children.get(i);
child.generateClassEpilogue(out);
}
}
if (_attrChildren != null) {
for (JspNode child : _attrChildren) {
child.generateClassEpilogue(out);
}
}
}
}