KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > bridge > jsp > taglib > util > Attribute


1 /*
2
3 This software is OSI Certified Open Source Software.
4 OSI Certified is a certification mark of the Open Source Initiative.
5
6 The license (Mozilla version 1.0) can be read at the MMBase site.
7 See http://www.MMBase.org/license
8
9 */

10 package org.mmbase.bridge.jsp.taglib.util;
11
12 import java.util.*;
13
14 import javax.servlet.jsp.JspTagException JavaDoc;
15
16 import org.mmbase.bridge.jsp.taglib.ContextReferrerTag;
17 import org.mmbase.cache.Cache;
18 import org.mmbase.util.*;
19 import org.mmbase.util.logging.*;
20
21 /**
22  * MMBase Taglib attributes can contain $-variables. Parsing of these $-variables is
23  * cached. Evaluation of these vars must be postponed until doStartTag because servlet containers can
24  * decide not to call the set-function of the attribute (in case of tag-instance-reuse).
25  *
26  * @author Michiel Meeuwissen
27  * @version $Id: Attribute.java,v 1.28 2005/08/25 08:25:40 michiel Exp $
28  * @since MMBase-1.7
29  */

30
31 public class Attribute {
32     private static final Logger log = Logging.getLoggerInstance(Attribute.class);
33
34     private final static AttributeCache cache = new AttributeCache();
35     // not sure the cache is actually useful for performance, perhaps it can as well be switched off.
36

37     public final static Attribute NULL = new NullAttribute();
38
39     static {
40         // put the cache in the mmbase cache repository (making it configurable)
41
cache.putCache();
42     }
43
44     /**
45      * This is the function for public use. It takes the string and returns an Attribute, creating
46      * a new one if it is not in the Attribute cache.
47      */

48     public static final Attribute getAttribute(final Object JavaDoc at) throws JspTagException JavaDoc {
49         if (at == null) return NULL;
50         return cache.getAttribute(at);
51     }
52
53     /**
54      * Whether the attribute contains any $-vars.
55      */

56     private boolean containsVars;
57
58     final boolean containsVars() {
59         return containsVars;
60     }
61
62     /**
63      * The unparsed attribute.
64      */

65     private Object JavaDoc attribute;
66
67     /**
68      * List of AttributeParts (the parsed attribute). This can be null
69      * if containsVars is false (then simply 'attribute' can be returned
70      * as value).
71      */

72     private List attributeParts;
73
74     /**
75      * The constructor is protected, construction is done by the cache.
76      */

77     protected Attribute(Object JavaDoc at) throws JspTagException JavaDoc {
78         attribute = at;
79         parse();
80     }
81
82     protected Attribute() {}
83
84     /**
85      * Appends the evaluated Attribute to StringBuffer
86      *
87      * @param tag The tag relative to which the variable evalutations must be done
88      * (normally 'this' in a Tag implementation)
89      */

90
91     public void appendValue(ContextReferrerTag tag, StringBuffer JavaDoc buffer) throws JspTagException JavaDoc {
92         if (log.isDebugEnabled()) {
93             log.debug("Appending " + attribute);
94         }
95         if (! containsVars) buffer.append(attribute.toString());
96         Iterator i = attributeParts.iterator();
97         while (i.hasNext()) {
98             Part ap = (Part) i.next();
99             ap.appendValue(tag, buffer);
100         }
101     }
102
103     /**
104      * Returns the evaluated Attribute as an Object. Can also be null.
105      */

106     public Object JavaDoc getValue(ContextReferrerTag tag) throws JspTagException JavaDoc {
107         if (! containsVars) return attribute;
108
109         if (attributeParts.size() == 1) { // avoid construction of StringBuffer for this simple case
110
Part ap = (Part) attributeParts.get(0);
111             return ap.getValue(tag);
112         }
113         StringBuffer JavaDoc result = new StringBuffer JavaDoc();
114         appendValue(tag, result);
115         return result.toString();
116     }
117
118
119     /**
120      * Returns the evaluated Attribute as a String. This is never null (empty string in that case)..
121      */

122     public String JavaDoc getString(ContextReferrerTag tag) throws JspTagException JavaDoc {
123         return Casting.toString(getValue(tag));
124     }
125
126     /**
127      * Returns the evaluated Attribute as a int
128      */

129
130     public int getInt(ContextReferrerTag tag, int def) throws JspTagException JavaDoc {
131         return org.mmbase.util.Casting.toInt(getValue(tag), def);
132     }
133
134     public long getLong(ContextReferrerTag tag, long def) throws JspTagException JavaDoc {
135         return org.mmbase.util.Casting.toLong(getValue(tag), def);
136     }
137
138     /**
139      * Returns the evaluated Attribute as a List (evalatued to comma-seperated String, which is 'split').
140      * The List is empty if getValue would give empty String or null.
141      *
142      */

143
144     public List getList(ContextReferrerTag tag) throws JspTagException JavaDoc {
145         return Arrays.asList( getString(tag).trim().split("\\s*,\\s*") );
146     }
147
148     /**
149      * Returns the evaluated Attribute as a boolen (depending on if getValue returns one of the
150      * strings 'true' or 'false' (case insensitve)).
151      *
152      * @param def If the string is not "true" or "false', then this value is returned.
153      * @return true or false
154      */

155
156     public boolean getBoolean(ContextReferrerTag tag, boolean def) throws JspTagException JavaDoc {
157         String JavaDoc val = getString(tag).toLowerCase();
158         if ("true".equals(val)) return true;
159         if ("false".equals(val)) return false;
160         if ("yes".equals(val)) return true;
161         if ("no".equals(val)) return false;
162         if ("1".equals(val)) return true;
163         if ("0".equals(val)) return false;
164         if ("".equals(val)) return def;
165         throw new JspTagException JavaDoc(" " + getString(tag) + " is no boolean");
166     }
167
168     /**
169      * String representation of this Attribute object (for debugging)
170      */

171     public String JavaDoc toString() {
172         return "att: " + attribute.toString() + " parts: " + attributeParts;
173     }
174
175     /**
176      * Parses this attribute into list of 'attributeparts'. This is
177      * the heart of the Attribute class. The method {@link #getValue}
178      * will concatenate them together again (after evaluation).
179      */

180
181     protected void parse() throws JspTagException JavaDoc {
182         String JavaDoc attr = (String JavaDoc) attribute;
183         // search all occurences of $
184
int foundPos = attr.indexOf('$');
185         if (foundPos == -1) {
186             containsVars = false;
187             return; // if none, return imediately.
188
} else {
189             attributeParts = new ArrayList();
190             containsVars = true;
191         }
192
193         int pos = 0;
194         while (foundPos >= 0) { // we found a variable!
195
String JavaDoc npart = attr.substring(pos, foundPos);
196             if (npart.length() > 0) {
197                 attributeParts.add(new StringPart(npart));
198             }
199             // piece of string until now is ready.
200
foundPos ++;
201             if (foundPos >= attr.length()) { // end of string
202
// could not happen :-)
203
break;
204             }
205             char c = attr.charAt(foundPos);
206             if (c == '{' || c == '[') { // using parentheses
207
char close = (c == '{' ? '}' : ']');
208                 // find matching closing parenthes
209
pos = ++foundPos;
210                 int opened = 1;
211                 while (opened > 0) {
212                     int posClose = attr.indexOf(close, pos);
213                     if (posClose == -1) {
214                         log.error("Unbalanced parentheses in '" + this + "'");
215                         throw new AttributeException("Unbalanced parentheses in '" + this + "'");
216                     }
217                     int posOpen = attr.indexOf(c, pos);
218
219                     if (posOpen > -1 && posOpen < posClose) { // another one was opened!
220
opened++;
221                         pos = posOpen + 1;
222                     } else {
223                         opened--;
224                         pos = posClose + 1;
225                     }
226                 }
227                 if (attr.charAt(foundPos) != '+') {
228                     Attribute var = getAttribute(attr.substring(foundPos, pos - 1));
229                     attributeParts.add(new VariablePart(var));
230                 } else {
231                     Attribute var = getAttribute(attr.substring(foundPos + 1, pos - 1));
232                     attributeParts.add(new ExpressionPart(var));
233                 }
234             } else { // not using parentheses.
235
pos = foundPos;
236                 if (c == '$') { // make escaping of $ possible
237
attributeParts.add(new StringPart("$"));
238                     pos++;
239                 } else { // search until non-identifier
240
StringBuffer JavaDoc varName = new StringBuffer JavaDoc();
241                     while (ContextContainer.isContextIdentifierChar(c)) {
242                         varName.append(c);
243                         pos++;
244                         if (pos >= attr.length()) break; // end of string
245
c = attr.charAt(pos);
246                     }
247                    Attribute var = getAttribute(varName.toString());
248                    attributeParts.add(new VariablePart(var));
249                 }
250             }
251             // ready with this $, search next occasion;
252
foundPos = attr.indexOf('$', pos);
253         }
254         // no more $'es, add rest of string
255
String JavaDoc rest = attr.substring(pos);
256         if (rest.length() > 0) {
257             attributeParts.add(new StringPart(rest));
258         }
259         return;
260     }
261
262     /**
263      * A Part represents one part of an Attribute.
264      */

265
266     static abstract class Part {
267
268         protected Object JavaDoc part;
269
270         /**
271          * Returns the 'type' of a Part as a string. For debugging use.
272          */

273         abstract protected String JavaDoc getType();
274
275         /**
276          * String representation of this AttributePart (for debugging)
277          */

278         public String JavaDoc toString() {
279             return "(" + getType() + "/" + part.toString() + ")";
280         }
281
282         abstract Object JavaDoc getValue(ContextReferrerTag tag) throws JspTagException JavaDoc;
283
284         final void appendValue(ContextReferrerTag tag, StringBuffer JavaDoc buffer) throws JspTagException JavaDoc {
285             Casting.toStringBuffer(buffer, getValue(tag));
286         }
287     }
288
289     /**
290      * A part containing a $-variable.
291      */

292     static class VariablePart extends Part {
293         protected boolean containsVars; //wether the name of variable itself contains variables.
294
VariablePart(Attribute a) throws JspTagException JavaDoc {
295             containsVars = a.containsVars();
296             if (containsVars) {
297                 part = a;
298             } else {
299                 String JavaDoc var = (String JavaDoc) a.getValue(null);
300                 if (var.length() < 1) {
301                     log.error("Expression too short :" + var);
302                     throw new AttributeException("Expression too short");
303                 }
304                 part = var;
305             }
306         }
307         protected String JavaDoc getType() { return "Variable"; }
308         final Object JavaDoc getValue(ContextReferrerTag tag) throws JspTagException JavaDoc {
309             String JavaDoc v;
310             if (containsVars) {
311                 v = (String JavaDoc) ((Attribute) part).getValue(tag);
312             } else {
313                 v = (String JavaDoc) part;
314             }
315             if ("_".equals(v)) {
316                 return tag.findWriter().getWriterValue();
317             } else {
318                 return tag.getObject(v);
319             }
320         }
321     }
322
323     /**
324      * A ${+ } part containing an 'expression'. This is in fact an
325      * undocumented feature of the taglib. It is based on ExprCalc of
326      * org.mmbase.util.
327      */

328
329     static class ExpressionPart extends Part {
330         protected boolean evaluated;
331         protected String JavaDoc getEvaluated() {
332             return evaluated ? "evaluated" : "not evaluated";
333         }
334
335         ExpressionPart(Attribute a) throws JspTagException JavaDoc {
336             if (a.containsVars()) {
337                 evaluated = false;
338                 part = a;
339             } else {
340                 evaluated = true;
341                 ExprCalc cl = new ExprCalc((String JavaDoc) a.getValue(null));
342                 part = new Double JavaDoc(cl.getResult());
343             }
344         }
345         protected String JavaDoc getType() { return "Expression (" + getEvaluated() + ")"; }
346         final Object JavaDoc getValue(ContextReferrerTag tag) throws JspTagException JavaDoc {
347             if (evaluated) {
348                 return part;
349             } else {
350                 ExprCalc cl = new ExprCalc( ((Attribute) part).getString(tag));
351                 return new Double JavaDoc(cl.getResult());
352             }
353         }
354     }
355
356     /**
357      * A simple 'string' part, wich does not need any evaluating or parsing any more.
358      *
359      */

360
361     static class StringPart extends Part {
362         StringPart(String JavaDoc o) { part = o; }
363         protected String JavaDoc getType() { return "String"; }
364         final Object JavaDoc getValue(ContextReferrerTag tag) throws JspTagException JavaDoc {
365             return part;
366         }
367     }
368 }
369
370
371 /**
372  * Cache which relates unparsed Attribute Strings with parsed
373  * `Attribute' objects. It is not sure that this cache actually
374  * increases performance. Perhaps we can as well do without.
375
376  */

377
378 class AttributeCache extends Cache {
379
380     AttributeCache() {
381         super(1000);
382     }
383
384     public String JavaDoc getName() { return "TagAttributeCache"; }
385     public String JavaDoc getDescription() { return "Cache for parsed Tag Attributes"; }
386     public final Attribute getAttribute(final Object JavaDoc att) throws JspTagException JavaDoc {
387         Attribute res = (Attribute) super.get(att);
388         if (res == null) {
389             res = new Attribute(att);
390             super.put(att, res);
391         }
392         return res;
393     }
394
395 }
396
397 /**
398  * Exception related to errors in tag-attributes
399  */

400 class AttributeException extends JspTagException JavaDoc {
401     AttributeException(String JavaDoc s) { super(s); }
402 }
403
404 /**
405  * The attribute containing 'null' is special. No parsing needed, nothing needed. It is very often
406  * used, so we provide an implementation optimized for speed.
407  */

408 final class NullAttribute extends Attribute {
409     NullAttribute() { }
410     public final Object JavaDoc getValue(ContextReferrerTag tag) throws JspTagException JavaDoc { return null; }
411     public final String JavaDoc getString(ContextReferrerTag tag) throws JspTagException JavaDoc { return ""; }
412     public final void appendValue(ContextReferrerTag tag, StringBuffer JavaDoc buffer) throws JspTagException JavaDoc { return; }
413     public final String JavaDoc toString() { return "NULLATTRIBUTE"; }
414 }
415
Popular Tags