001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.jsp;
030:
031: import com.caucho.jsp.java.JspTagFileSupport;
032: import com.caucho.log.Log;
033: import com.caucho.util.L10N;
034: import com.caucho.xml.QName;
035:
036: import javax.servlet.jsp.tagext.SimpleTag;
037: import javax.servlet.jsp.tagext.TagAttributeInfo;
038: import javax.servlet.jsp.tagext.TagInfo;
039: import javax.servlet.jsp.tagext.VariableInfo;
040: import java.util.ArrayList;
041: import java.util.Iterator;
042: import java.util.logging.Logger;
043:
044: /**
045: * Describes a single tag instance.
046: */
047: public class TagInstance {
048: static final L10N L = new L10N(TagInstance.class);
049: static final Logger log = Log.open(TagInstance.class);
050:
051: // Special object to note that the attribute varies
052: private static final Object VARIES = new Varies();
053:
054: public static final String TOP_TAG = null;
055: public static final String FRAGMENT_WITH_TAG_PARENT = "top_tag_fragment";
056: public static final String FRAGMENT_WITH_SIMPLE_PARENT = "top_simple_fragment";
057:
058: private ParseTagManager _manager;
059:
060: private TagInstance _top;
061: private TagInfo _tagInfo;
062: private int _maxId;
063:
064: private TagInstance _parent;
065: private ArrayList<TagInstance> _children = new ArrayList<TagInstance>();
066: private ArrayList<TagInstance> _tags = new ArrayList<TagInstance>();
067:
068: private String _tagId = null;
069: private QName _qname;
070: private Class _cl;
071: private VariableInfo[] _varInfo;
072: private boolean _needsAdapter;
073: private boolean _hasAdapterDeclaration;
074:
075: private AnalyzedTag _analyzedTag;
076:
077: private ArrayList<QName> _attributeNames = new ArrayList<QName>();
078: private ArrayList<Object> _attributeValues = new ArrayList<Object>();
079:
080: private boolean _hasBodyContent;
081:
082: public TagInstance(ParseTagManager manager) {
083: _manager = manager;
084:
085: _top = this ;
086: _tagId = null;
087: }
088:
089: public TagInstance(ParseTagManager manager, String id) {
090: _manager = manager;
091: _top = this ;
092: _tagId = id;
093: }
094:
095: TagInstance(TagInstance parent, TagInfo tagInfo, QName qname,
096: Class cl) {
097: _qname = qname;
098: _parent = parent;
099: _manager = parent._manager;
100: _tagInfo = tagInfo;
101: _cl = cl;
102:
103: _top = parent._top;
104: parent._children.add(this );
105: _top._tags.add(this );
106:
107: if (tagInfo != null) {
108: String className = tagInfo.getTagClassName();
109: int p = className.lastIndexOf('.');
110: if (p >= 0)
111: className = className.substring(p + 1);
112: _tagId = "_jsp_" + className + "_" + _top._maxId++;
113:
114: }
115:
116: _analyzedTag = _manager.analyzeTag(cl);
117: }
118:
119: /**
120: * Returns the tag name
121: */
122: public QName getQName() {
123: return _qname;
124: }
125:
126: /**
127: * Returns the tag name
128: */
129: public String getName() {
130: return _qname.getName();
131: }
132:
133: public String getId() {
134: return _tagId;
135: }
136:
137: public void setId(String id) {
138: _tagId = id;
139: }
140:
141: public boolean generateAdapterDeclaration() {
142: if (_hasAdapterDeclaration) {
143: return false;
144: } else {
145: _hasAdapterDeclaration = true;
146:
147: return true;
148: }
149: }
150:
151: public TagInstance getParent() {
152: return _parent;
153: }
154:
155: /**
156: * Returns true for a top tag.
157: */
158: public boolean isTop() {
159: return _tagId == null || _tagId.startsWith("top_");
160: }
161:
162: /**
163: * Returns the tag class.
164: */
165: public Class getTagClass() {
166: return _cl;
167: }
168:
169: /**
170: * Returns true if it's a simple tag.
171: */
172: public boolean isSimpleTag() {
173: return _cl != null && SimpleTag.class.isAssignableFrom(_cl);
174: }
175:
176: /**
177: * Returns true if it's a simple tag.
178: */
179: public boolean isTagFileTag() {
180: return _cl != null
181: && JspTagFileSupport.class.isAssignableFrom(_cl);
182: }
183:
184: public TagInfo getTagInfo() {
185: return _tagInfo;
186: }
187:
188: /**
189: * Returns the analyzed tag.
190: */
191: public AnalyzedTag getAnalyzedTag() {
192: return _analyzedTag;
193: }
194:
195: void setVarInfo(VariableInfo[] varInfo) {
196: _varInfo = varInfo;
197: }
198:
199: public VariableInfo[] getVarInfo() {
200: return _varInfo;
201: }
202:
203: public int size() {
204: return _top._maxId;
205: }
206:
207: /**
208: * Returns true if there are children.
209: */
210: public boolean hasChildren() {
211: return _children != null && _children.size() > 0;
212: }
213:
214: /**
215: * Set true if needs an adapter.
216: */
217: public boolean getNeedsAdapter() {
218: return _needsAdapter || isSimpleTag() && hasChildren();
219: }
220:
221: /**
222: * if needs an adapter.
223: */
224: public void setNeedsAdapter(boolean needsAdapter) {
225: _needsAdapter = needsAdapter;
226: }
227:
228: /**
229: * Iterates through the children.
230: */
231: public Iterator<TagInstance> iterator() {
232: return _children.iterator();
233: }
234:
235: /**
236: * Iterates through the children.
237: */
238: public TagInstance get(int i) {
239: return _top._tags.get(i);
240: }
241:
242: /**
243: * Returns the tag's attribute names.
244: */
245: public ArrayList<QName> getAttributeNames() {
246: return _attributeNames;
247: }
248:
249: /**
250: * Set true if the tag has a body content.
251: */
252: public void setBodyContent(boolean hasBodyContent) {
253: _hasBodyContent = hasBodyContent;
254: }
255:
256: /**
257: * Get true if the tag has a body content.
258: */
259: public boolean getBodyContent() {
260: return _hasBodyContent;
261: }
262:
263: /**
264: * Adds a child tag. If the tag exists, just reuse it.
265: *
266: * @param tagName the JSP name of the tag
267: * @param tagInfo the TagInfo structure for the tag
268: * @param cl the tag's implementation class
269: * @param names the array of attribute names
270: * @param values the array of attribute values
271: */
272: public TagInstance addTag(QName tagName, TagInfo tagInfo, Class cl,
273: ArrayList<QName> names, ArrayList<Object> values,
274: boolean hasBodyContent) {
275: TagInstance child = null;//findTag(tagName, names);
276: if (child == null)
277: child = new TagInstance(this , tagInfo, tagName, cl);
278:
279: child.setBodyContent(hasBodyContent);
280:
281: for (int i = 0; i < names.size(); i++) {
282: QName name = names.get(i);
283: Object value = values.get(i);
284:
285: if (value instanceof String) {
286: String strValue = (String) value;
287:
288: // runtime and EL expressions can't have shared values
289: if (strValue.startsWith("<%=")
290: || strValue.startsWith("%="))
291: value = null;
292: else if (strValue.indexOf("${") >= 0)
293: value = null;
294: else if (strValue.indexOf("#{") >= 0) {
295: // jsp/1cn1 - the expression can depend on the context
296: value = null;
297: }
298:
299: child.addAttribute(name, value);
300: } else {
301: child.addAttribute(name, null);
302: }
303: }
304:
305: return child;
306: }
307:
308: /**
309: * Adds a new tag. Always create a new tag.
310: */
311: public TagInstance addNewTag(QName tagName, TagInfo tagInfo,
312: Class cl, ArrayList<QName> names, ArrayList<String> values,
313: boolean hasBodyContent) {
314: TagInstance child = null;
315: if (child == null)
316: child = new TagInstance(this , tagInfo, tagName, cl);
317:
318: child.setBodyContent(hasBodyContent);
319:
320: for (int i = 0; i < names.size(); i++) {
321: QName name = names.get(i);
322: String value = values.get(i);
323:
324: if (value.startsWith("<%=") || value.startsWith("%="))
325: value = null;
326:
327: child.addAttribute(name, value);
328: }
329:
330: return child;
331: }
332:
333: /**
334: * Sets the attribute. Null values can't be pre-cached.
335: */
336: public void addAttribute(QName name, Object value) {
337: for (int i = 0; i < _attributeNames.size(); i++) {
338: QName attrName = _attributeNames.get(i);
339:
340: if (attrName.equals(name)) {
341: Object oldValue = _attributeValues.get(i);
342: if (value == null || oldValue == null
343: || !value.equals(oldValue))
344: _attributeValues.set(i, null);
345:
346: return;
347: }
348: }
349:
350: if (name == null)
351: throw new NullPointerException();
352:
353: _attributeNames.add(name);
354: _attributeValues.add(value);
355: }
356:
357: public String getAttribute(QName name) {
358: for (int i = 0; i < _attributeNames.size(); i++) {
359: if (name.equals(_attributeNames.get(i)))
360: return (String) _attributeValues.get(i);
361: }
362:
363: return null;
364: }
365:
366: boolean canBeRequestTime(String name) {
367: TagAttributeInfo attrs[] = _tagInfo.getAttributes();
368:
369: if (attrs == null)
370: return true;
371:
372: for (int i = 0; i < attrs.length; i++) {
373: if (name.equals(attrs[i].getName()))
374: return attrs[i].canBeRequestTime();
375: }
376:
377: return false;
378: }
379:
380: public TagAttributeInfo getAttributeInfo(String name) {
381: TagAttributeInfo attrs[] = _tagInfo.getAttributes();
382:
383: if (attrs == null)
384: return null;
385:
386: for (int i = 0; i < attrs.length; i++) {
387: if (name.equals(attrs[i].getName()))
388: return attrs[i];
389: }
390:
391: return null;
392: }
393:
394: /**
395: * Finds the matching tag.
396: */
397: public TagInstance findTag(QName tagName, ArrayList<QName> names,
398: boolean hasBodyContent) {
399: for (int i = 0; i < _children.size(); i++) {
400: TagInstance child = _children.get(i);
401:
402: if (child.match(tagName, names, hasBodyContent))
403: return child;
404: }
405:
406: return null;
407: }
408:
409: /**
410: * Returns true for matching instances.
411: */
412: boolean match(QName tagName, ArrayList<QName> names,
413: boolean hasBodyContent) {
414: if (!_qname.equals(tagName))
415: return false;
416:
417: if (_attributeNames.size() != names.size())
418: return false;
419:
420: if (_hasBodyContent != hasBodyContent)
421: return false;
422:
423: for (int i = 0; i < _attributeNames.size(); i++) {
424: QName attrName = _attributeNames.get(i);
425:
426: if (names.indexOf(attrName) < 0)
427: return false;
428: }
429:
430: for (int i = 0; i < names.size(); i++) {
431: QName name = names.get(i);
432:
433: if (_attributeNames.indexOf(name) < 0)
434: return false;
435: }
436:
437: return true;
438: }
439:
440: static class Varies {
441: public String toString() {
442: return "Value-Varies[]";
443: }
444: }
445: }
|