/* * 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; import com.caucho.jsp.java.JspTagFileSupport; import com.caucho.util.L10N; import com.caucho.xml.QName; import javax.servlet.jsp.tagext.SimpleTag; import javax.servlet.jsp.tagext.TagAttributeInfo; import javax.servlet.jsp.tagext.TagInfo; import javax.servlet.jsp.tagext.VariableInfo; import java.util.ArrayList; import java.util.Iterator; import java.util.logging.Logger; /** * Describes a single tag instance. */ public class TagInstance { static final L10N L = new L10N(TagInstance.class); private static final Logger log = Logger.getLogger(TagInstance.class.getName()); // Special object to note that the attribute varies private static final Object VARIES = new Varies(); public static final String TOP_TAG = null; public static final String FRAGMENT_WITH_TAG_PARENT = "top_tag_fragment"; public static final String FRAGMENT_WITH_SIMPLE_PARENT = "top_simple_fragment"; private ParseTagManager _manager; private TagInstance _top; private TagInfo _tagInfo; private int _maxId; private TagInstance _parent; private ArrayList<TagInstance> _children = new ArrayList<TagInstance>(); private ArrayList<TagInstance> _tags = new ArrayList<TagInstance>(); private JspGenerator _gen; private String _tagId = null; private QName _qname; private Class _cl; private VariableInfo []_varInfo; private boolean _needsAdapter; private boolean _hasAdapterDeclaration; private AnalyzedTag _analyzedTag; private ArrayList<QName> _attributeNames = new ArrayList<QName>(); private ArrayList<Object> _attributeValues = new ArrayList<Object>(); private boolean _hasBodyContent; private boolean _isInjectFactory; public TagInstance(ParseTagManager manager) { _manager = manager; _top = this; _tagId = null; } public TagInstance(ParseTagManager manager, String id) { _manager = manager; _top = this; _tagId = id; } TagInstance(JspGenerator gen, TagInstance parent, TagInfo tagInfo, QName qname, Class cl) { _gen = gen; _qname = qname; _parent = parent; _manager = parent._manager; _tagInfo = tagInfo; _cl = cl; _top = parent._top; parent._children.add(this); _top._tags.add(this); if (tagInfo != null) { String className = tagInfo.getTagClassName(); int p = className.lastIndexOf('.'); if (p >= 0) className = className.substring(p + 1); _tagId = "_jsp_" + className + "_" + _gen.uniqueId(); } _analyzedTag = _manager.analyzeTag(cl); } /** * Returns the tag name */ public QName getQName() { return _qname; } /** * Returns the tag name */ public String getName() { return _qname.getName(); } public String getId() { return _tagId; } public void setId(String id) { _tagId = id; } public boolean generateAdapterDeclaration() { if (_hasAdapterDeclaration) { return false; } else { _hasAdapterDeclaration = true; return true; } } public TagInstance getParent() { return _parent; } /** * Returns true for a top tag. */ public boolean isTop() { return _tagId == null || _tagId.startsWith("top_"); } /** * Returns the tag class. */ public Class getTagClass() { return _cl; } /** * Returns true if it's a simple tag. */ public boolean isSimpleTag() { return _cl != null && SimpleTag.class.isAssignableFrom(_cl); } /** * Returns true if it's a simple tag. */ public boolean isTagFileTag() { return _cl != null && JspTagFileSupport.class.isAssignableFrom(_cl); } public TagInfo getTagInfo() { return _tagInfo; } /** * Returns the analyzed tag. */ public AnalyzedTag getAnalyzedTag() { return _analyzedTag; } void setVarInfo(VariableInfo []varInfo) { _varInfo = varInfo; } public VariableInfo []getVarInfo() { return _varInfo; } public int size() { return _top._maxId; } /** * Returns true if there are children. */ public boolean hasChildren() { return _children != null && _children.size() > 0; } /** * Set true if needs an adapter. */ public boolean getNeedsAdapter() { return _needsAdapter || isSimpleTag() && hasChildren(); } /** * if needs an adapter. */ public void setNeedsAdapter(boolean needsAdapter) { _needsAdapter = needsAdapter; } /** * Iterates through the children. */ public Iterator<TagInstance> iterator() { return _children.iterator(); } /** * Iterates through the children. */ public TagInstance get(int i) { return _top._tags.get(i); } /** * Returns the tag's attribute names. */ public ArrayList<QName> getAttributeNames() { return _attributeNames; } /** * Set true if the tag has a body content. */ public void setBodyContent(boolean hasBodyContent) { _hasBodyContent = hasBodyContent; } /** * Get true if the tag has a body content. */ public boolean getBodyContent() { return _hasBodyContent; } /** * Adds a child tag. If the tag exists, just reuse it. * * @param tagName the JSP name of the tag * @param tagInfo the TagInfo structure for the tag * @param cl the tag's implementation class * @param names the array of attribute names * @param values the array of attribute values */ public TagInstance addTag(JspGenerator gen, QName tagName, TagInfo tagInfo, Class cl, ArrayList<QName> names, ArrayList<Object> values, boolean hasBodyContent) { TagInstance child = null;//findTag(tagName, names); if (child == null) child = new TagInstance(gen, this, tagInfo, tagName, cl); child.setBodyContent(hasBodyContent); for (int i = 0; i < names.size(); i++) { QName name = names.get(i); Object value = values.get(i); if (value instanceof String) { String strValue = (String) value; // runtime and EL expressions can't have shared values if (strValue.startsWith("<%=") || strValue.startsWith("%=")) value = null; else if (strValue.indexOf("${") >= 0) value = null; else if (strValue.indexOf("#{") >= 0) { // jsp/1cn1 - the expression can depend on the context value = null; } child.addAttribute(name, value); } else { child.addAttribute(name, null); } } return child; } /** * Adds a new tag. Always create a new tag. */ public TagInstance addNewTag(JspGenerator gen, QName tagName, TagInfo tagInfo, Class cl, ArrayList<QName> names, ArrayList<String> values, boolean hasBodyContent) { TagInstance child = null; if (child == null) child = new TagInstance(gen, this, tagInfo, tagName, cl); child.setBodyContent(hasBodyContent); for (int i = 0; i < names.size(); i++) { QName name = names.get(i); String value = values.get(i); if (value.startsWith("<%=") || value.startsWith("%=")) value = null; child.addAttribute(name, value); } return child; } /** * Sets the attribute. Null values can't be pre-cached. */ public void addAttribute(QName name, Object value) { for (int i = 0; i < _attributeNames.size(); i++) { QName attrName = _attributeNames.get(i); if (attrName.equals(name)) { Object oldValue = _attributeValues.get(i); if (value == null || oldValue == null || ! value.equals(oldValue)) _attributeValues.set(i, null); return; } } if (name == null) throw new NullPointerException(); _attributeNames.add(name); _attributeValues.add(value); } public String getAttribute(QName name) { for (int i = 0; i < _attributeNames.size(); i++) { if (name.equals(_attributeNames.get(i))) return (String) _attributeValues.get(i); } return null; } boolean canBeRequestTime(String name) { TagAttributeInfo attrs[] = _tagInfo.getAttributes(); if (attrs == null) return true; for (int i = 0; i < attrs.length; i++) { if (name.equals(attrs[i].getName())) return attrs[i].canBeRequestTime(); } return false; } public TagAttributeInfo getAttributeInfo(String name) { if (_tagInfo == null) return null; TagAttributeInfo attrs[] = _tagInfo.getAttributes(); if (attrs == null) return null; for (int i = 0; i < attrs.length; i++) { if (name.equals(attrs[i].getName())) return attrs[i]; } return null; } /** * Finds the matching tag. */ public TagInstance findTag(QName tagName, ArrayList<QName> names, boolean hasBodyContent) { for (int i = 0; i < _children.size(); i++) { TagInstance child = _children.get(i); if (child.match(tagName, names, hasBodyContent)) return child; } return null; } /** * Returns true for matching instances. */ boolean match(QName tagName, ArrayList<QName> names, boolean hasBodyContent) { if (! _qname.equals(tagName)) return false; if (_attributeNames.size() != names.size()) return false; if (_hasBodyContent != hasBodyContent) return false; for (int i = 0; i < _attributeNames.size(); i++) { QName attrName = _attributeNames.get(i); if (names.indexOf(attrName) < 0) return false; } for (int i = 0; i < names.size(); i++) { QName name = names.get(i); if (_attributeNames.indexOf(name) < 0) return false; } return true; } public boolean getAndSetInjectFactory(boolean value) { boolean isSet = _isInjectFactory; _isInjectFactory = true; return isSet; } static class Varies { public String toString() { return "Value-Varies[]"; } } }