%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.commons.jelly.TagSupport |
|
|
1 | /* |
|
2 | * Copyright 2002,2004 The Apache Software Foundation. |
|
3 | * |
|
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 | * you may not use this file except in compliance with the License. |
|
6 | * You may obtain a copy of the License at |
|
7 | * |
|
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
9 | * |
|
10 | * Unless required by applicable law or agreed to in writing, software |
|
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 | * See the License for the specific language governing permissions and |
|
14 | * limitations under the License. |
|
15 | */ |
|
16 | package org.apache.commons.jelly; |
|
17 | ||
18 | import java.io.StringWriter; |
|
19 | import java.util.Arrays; |
|
20 | import java.util.Collection; |
|
21 | import java.util.Iterator; |
|
22 | import java.util.List; |
|
23 | ||
24 | import org.apache.commons.jelly.impl.CompositeTextScriptBlock; |
|
25 | import org.apache.commons.jelly.impl.ScriptBlock; |
|
26 | import org.apache.commons.jelly.impl.TextScript; |
|
27 | ||
28 | /** <p><code>TagSupport</code> an abstract base class which is useful to |
|
29 | * inherit from if developing your own tag.</p> |
|
30 | * |
|
31 | * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> |
|
32 | * @version $Revision: 1.33 $ |
|
33 | */ |
|
34 | ||
35 | public abstract class TagSupport implements Tag { |
|
36 | ||
37 | /** the parent of this tag */ |
|
38 | protected Tag parent; |
|
39 | ||
40 | /** the body of the tag */ |
|
41 | protected Script body; |
|
42 | /** The current context */ |
|
43 | ||
44 | protected Boolean shouldTrim; |
|
45 | protected boolean hasTrimmed; |
|
46 | ||
47 | protected JellyContext context; |
|
48 | ||
49 | /** whether xml text should be escaped */ |
|
50 | 1860 | private boolean escapeText = true; |
51 | ||
52 | /** |
|
53 | * Searches up the parent hierarchy from the given tag |
|
54 | * for a Tag of the given type |
|
55 | * |
|
56 | * @param from the tag to start searching from |
|
57 | * @param tagClass the type of the tag to find |
|
58 | * @return the tag of the given type or null if it could not be found |
|
59 | */ |
|
60 | public static Tag findAncestorWithClass(Tag from, Class tagClass) { |
|
61 | // we could implement this as |
|
62 | // return findAncestorWithClass(from,Collections.singleton(tagClass)); |
|
63 | // but this is so simple let's save the object creation for now |
|
64 | 782 | while (from != null) { |
65 | 392 | if (tagClass.isInstance(from)) { |
66 | 322 | return from; |
67 | } |
|
68 | 70 | from = from.getParent(); |
69 | } |
|
70 | 34 | return null; |
71 | } |
|
72 | ||
73 | /** |
|
74 | * Searches up the parent hierarchy from the given tag |
|
75 | * for a Tag matching one or more of given types. |
|
76 | * |
|
77 | * @param from the tag to start searching from |
|
78 | * @param tagClasses a Collection of Class types that might match |
|
79 | * @return the tag of the given type or null if it could not be found |
|
80 | */ |
|
81 | public static Tag findAncestorWithClass(Tag from, Collection tagClasses) { |
|
82 | 0 | while (from != null) { |
83 | 0 | for(Iterator iter = tagClasses.iterator();iter.hasNext();) { |
84 | 0 | Class klass = (Class)(iter.next()); |
85 | 0 | if (klass.isInstance(from)) { |
86 | 0 | return from; |
87 | } |
|
88 | } |
|
89 | 0 | from = from.getParent(); |
90 | } |
|
91 | 0 | return null; |
92 | } |
|
93 | ||
94 | /** |
|
95 | * Searches up the parent hierarchy from the given tag |
|
96 | * for a Tag matching one or more of given types. |
|
97 | * |
|
98 | * @param from the tag to start searching from |
|
99 | * @param tagClasses an array of types that might match |
|
100 | * @return the tag of the given type or null if it could not be found |
|
101 | * @see #findAncestorWithClass(Tag,Collection) |
|
102 | */ |
|
103 | public static Tag findAncestorWithClass(Tag from, Class[] tagClasses) { |
|
104 | 0 | return findAncestorWithClass(from,Arrays.asList(tagClasses)); |
105 | } |
|
106 | ||
107 | 1860 | public TagSupport() { |
108 | 1860 | } |
109 | ||
110 | 0 | public TagSupport(boolean shouldTrim) { |
111 | 0 | setTrim( shouldTrim ); |
112 | 0 | } |
113 | ||
114 | /** |
|
115 | * Sets whether whitespace inside this tag should be trimmed or not. |
|
116 | * Defaults to true so whitespace is trimmed |
|
117 | */ |
|
118 | public void setTrim(boolean shouldTrim) { |
|
119 | 14 | if ( shouldTrim ) { |
120 | 6 | this.shouldTrim = Boolean.TRUE; |
121 | } |
|
122 | else { |
|
123 | 8 | this.shouldTrim = Boolean.FALSE; |
124 | } |
|
125 | 14 | } |
126 | ||
127 | public boolean isTrim() { |
|
128 | 1496 | if ( this.shouldTrim == null ) { |
129 | 910 | Tag parent = getParent(); |
130 | 910 | if ( parent == null ) { |
131 | 334 | return true; |
132 | } |
|
133 | else { |
|
134 | 576 | if ( parent instanceof TagSupport ) { |
135 | 576 | TagSupport parentSupport = (TagSupport) parent; |
136 | ||
137 | 576 | this.shouldTrim = ( parentSupport.isTrim() ? Boolean.TRUE : Boolean.FALSE ); |
138 | } |
|
139 | else { |
|
140 | 0 | this.shouldTrim = Boolean.TRUE; |
141 | } |
|
142 | } |
|
143 | } |
|
144 | ||
145 | 1162 | return this.shouldTrim.booleanValue(); |
146 | } |
|
147 | ||
148 | /** @return the parent of this tag */ |
|
149 | public Tag getParent() { |
|
150 | 1336 | return parent; |
151 | } |
|
152 | ||
153 | /** Sets the parent of this tag */ |
|
154 | public void setParent(Tag parent) { |
|
155 | 4786 | this.parent = parent; |
156 | 4786 | } |
157 | ||
158 | /** @return the body of the tag */ |
|
159 | public Script getBody() { |
|
160 | 968 | if (! hasTrimmed) { |
161 | 920 | hasTrimmed = true; |
162 | 920 | if (isTrim()) { |
163 | 912 | trimBody(); |
164 | } |
|
165 | } |
|
166 | 968 | return body; |
167 | } |
|
168 | ||
169 | /** Sets the body of the tag */ |
|
170 | public void setBody(Script body) { |
|
171 | 4786 | this.body = body; |
172 | 4786 | this.hasTrimmed = false; |
173 | 4786 | } |
174 | ||
175 | /** @return the context in which the tag will be run */ |
|
176 | public JellyContext getContext() { |
|
177 | 14 | return context; |
178 | } |
|
179 | ||
180 | /** Sets the context in which the tag will be run */ |
|
181 | public void setContext(JellyContext context) throws JellyTagException { |
|
182 | 1788 | this.context = context; |
183 | 1788 | } |
184 | ||
185 | /** |
|
186 | * Invokes the body of this tag using the given output |
|
187 | */ |
|
188 | public void invokeBody(XMLOutput output) throws JellyTagException { |
|
189 | 930 | getBody().run(context, output); |
190 | 846 | } |
191 | ||
192 | // Implementation methods |
|
193 | //------------------------------------------------------------------------- |
|
194 | /** |
|
195 | * Searches up the parent hierarchy for a Tag of the given type. |
|
196 | * @return the tag of the given type or null if it could not be found |
|
197 | */ |
|
198 | protected Tag findAncestorWithClass(Class parentClass) { |
|
199 | 356 | return findAncestorWithClass(getParent(), parentClass); |
200 | } |
|
201 | ||
202 | /** |
|
203 | * Searches up the parent hierarchy for a Tag of one of the given types. |
|
204 | * @return the tag of the given type or null if it could not be found |
|
205 | * @see #findAncestorWithClass(Collection) |
|
206 | */ |
|
207 | protected Tag findAncestorWithClass(Class[] parentClasses) { |
|
208 | 0 | return findAncestorWithClass(getParent(),parentClasses); |
209 | } |
|
210 | ||
211 | /** |
|
212 | * Searches up the parent hierarchy for a Tag of one of the given types. |
|
213 | * @return the tag of the given type or null if it could not be found |
|
214 | */ |
|
215 | protected Tag findAncestorWithClass(Collection parentClasses) { |
|
216 | 0 | return findAncestorWithClass(getParent(),parentClasses); |
217 | } |
|
218 | ||
219 | /** |
|
220 | * Executes the body of the tag and returns the result as a String. |
|
221 | * |
|
222 | * @return the text evaluation of the body |
|
223 | */ |
|
224 | protected String getBodyText() throws JellyTagException { |
|
225 | 60 | return getBodyText(escapeText); |
226 | } |
|
227 | ||
228 | /** |
|
229 | * Executes the body of the tag and returns the result as a String. |
|
230 | * |
|
231 | * @param shouldEscape Signal if the text should be escaped. |
|
232 | * |
|
233 | * @return the text evaluation of the body |
|
234 | */ |
|
235 | protected String getBodyText(boolean shouldEscape) throws JellyTagException { |
|
236 | 80 | StringWriter writer = new StringWriter(); |
237 | 80 | invokeBody(XMLOutput.createXMLOutput(writer, shouldEscape)); |
238 | 80 | return writer.toString(); |
239 | } |
|
240 | ||
241 | ||
242 | /** |
|
243 | * Find all text nodes inside the top level of this body and |
|
244 | * if they are just whitespace then remove them |
|
245 | */ |
|
246 | protected void trimBody() { |
|
247 | 912 | synchronized(body) { |
248 | // #### should refactor this code into |
|
249 | // #### trimWhitespace() methods on the Script objects |
|
250 | ||
251 | 912 | if ( body instanceof CompositeTextScriptBlock ) { |
252 | 6 | CompositeTextScriptBlock block = (CompositeTextScriptBlock) body; |
253 | 6 | List list = block.getScriptList(); |
254 | 6 | int size = list.size(); |
255 | 6 | if ( size > 0 ) { |
256 | 6 | Script script = (Script) list.get(0); |
257 | 6 | if ( script instanceof TextScript ) { |
258 | 6 | TextScript textScript = (TextScript) script; |
259 | 6 | textScript.trimStartWhitespace(); |
260 | } |
|
261 | 6 | if ( size > 1 ) { |
262 | 6 | script = (Script) list.get(size - 1); |
263 | 6 | if ( script instanceof TextScript ) { |
264 | 6 | TextScript textScript = (TextScript) script; |
265 | 6 | textScript.trimEndWhitespace(); |
266 | } |
|
267 | } |
|
268 | } |
|
269 | } |
|
270 | else |
|
271 | 906 | if ( body instanceof ScriptBlock ) { |
272 | 798 | ScriptBlock block = (ScriptBlock) body; |
273 | 798 | List list = block.getScriptList(); |
274 | 4048 | for ( int i = list.size() - 1; i >= 0; i-- ) { |
275 | 3250 | Script script = (Script) list.get(i); |
276 | 3250 | if ( script instanceof TextScript ) { |
277 | 1640 | TextScript textScript = (TextScript) script; |
278 | 1640 | String text = textScript.getText(); |
279 | 1640 | text = text.trim(); |
280 | 1640 | if ( text.length() == 0 ) { |
281 | 1632 | list.remove(i); |
282 | } |
|
283 | else { |
|
284 | 8 | textScript.setText(text); |
285 | } |
|
286 | } |
|
287 | } |
|
288 | } |
|
289 | 108 | else if ( body instanceof TextScript ) { |
290 | 30 | TextScript textScript = (TextScript) body; |
291 | 30 | textScript.trimWhitespace(); |
292 | } |
|
293 | 912 | } |
294 | 912 | } |
295 | ||
296 | /** |
|
297 | * Returns whether the body of this tag will be escaped or not. |
|
298 | */ |
|
299 | public boolean isEscapeText() { |
|
300 | 6 | return escapeText; |
301 | } |
|
302 | ||
303 | /** |
|
304 | * Sets whether the body of the tag should be escaped as text (so that < and > are |
|
305 | * escaped as &lt; and &gt;), which is the default or leave the text as XML. |
|
306 | */ |
|
307 | public void setEscapeText(boolean escapeText) { |
|
308 | 4 | this.escapeText = escapeText; |
309 | 4 | } |
310 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |