Source code: org/apache/taglibs/standard/tlv/JstlBaseTLV.java
1 /*
2 * Copyright 1999-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
17 package org.apache.taglibs.standard.tlv;
18
19 import java.io.IOException;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.Map;
23 import java.util.NoSuchElementException;
24 import java.util.Set;
25 import java.util.StringTokenizer;
26 import java.util.Vector;
27
28 import javax.servlet.jsp.JspException;
29 import javax.servlet.jsp.tagext.PageData;
30 import javax.servlet.jsp.tagext.TagData;
31 import javax.servlet.jsp.tagext.TagLibraryValidator;
32 import javax.servlet.jsp.tagext.ValidationMessage;
33 import javax.xml.parsers.ParserConfigurationException;
34 import javax.xml.parsers.SAXParser;
35 import javax.xml.parsers.SAXParserFactory;
36
37 import org.apache.taglibs.standard.lang.support.ExpressionEvaluator;
38 import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;
39 import org.apache.taglibs.standard.resources.Resources;
40 import org.xml.sax.Attributes;
41 import org.xml.sax.SAXException;
42 import org.xml.sax.helpers.DefaultHandler;
43
44 /**
45 * <p>A base class to support SAX-based validation in JSTL.</p>
46 *
47 * @author Shawn Bayern
48 */
49 public abstract class JstlBaseTLV extends TagLibraryValidator {
50
51 //*********************************************************************
52 // Implementation Overview
53
54 /*
55 * We essentially just run the page through a SAX parser, handling
56 * the callbacks that interest us. The SAX parser is supplied by
57 * subclasses using the protected getHandler() method.
58 */
59
60 protected abstract DefaultHandler getHandler();
61
62
63 //*********************************************************************
64 // Constants
65
66 // parameter names
67 private final String EXP_ATT_PARAM = "expressionAttributes";
68
69 // attributes
70 protected static final String VAR = "var";
71 protected static final String SCOPE = "scope";
72
73 //scopes
74 protected static final String PAGE_SCOPE = "page";
75 protected static final String REQUEST_SCOPE = "request";
76 protected static final String SESSION_SCOPE = "session";
77 protected static final String APPLICATION_SCOPE = "application";
78
79 // Relevant URIs
80 protected final String JSP = "http://java.sun.com/JSP/Page";
81
82 // types of sub-classes - used on method validate()
83 private static final int TYPE_UNDEFINED = 0;
84 protected static final int TYPE_CORE = 1;
85 protected static final int TYPE_FMT = 2;
86 protected static final int TYPE_SQL = 3;
87 protected static final int TYPE_XML = 4;
88
89 // which tlv is being validated
90 private int tlvType = TYPE_UNDEFINED;
91
92 //*********************************************************************
93 // Validation and configuration state (protected)
94
95 protected String uri; // our taglib's uri (as passed by JSP container on XML View)
96 protected String prefix; // our taglib's prefix
97 protected Vector messageVector; // temporary error messages
98 protected Map config; // configuration (Map of Sets)
99 protected boolean failed; // have we failed >0 times?
100 protected String lastElementId; // the last element we've seen
101
102 //*********************************************************************
103 // Constructor and lifecycle management
104
105 public JstlBaseTLV() {
106 super();
107 init();
108 }
109
110 private void init() {
111 messageVector = null;
112 prefix = null;
113 config = null;
114 }
115
116 public void release() {
117 super.release();
118 init();
119 }
120
121
122 //*********************************************************************
123 // Validation entry point - this method is called by the sub-classes to
124 // do the validation.
125
126 public synchronized ValidationMessage[] validate(
127 int type, String prefix, String uri, PageData page) {
128 try {
129 this.tlvType = type;
130 this.uri = uri;
131 // initialize
132 messageVector = new Vector();
133
134 // save the prefix
135 this.prefix = prefix;
136
137 // parse parameters if necessary
138 try {
139 if (config == null)
140 configure((String) getInitParameters().get(EXP_ATT_PARAM));
141 } catch (NoSuchElementException ex) {
142 // parsing error
143 return vmFromString(
144 Resources.getMessage("TLV_PARAMETER_ERROR",
145 EXP_ATT_PARAM));
146 }
147
148 // get a handler
149 DefaultHandler h = getHandler();
150
151 // parse the page
152 SAXParserFactory f = SAXParserFactory.newInstance();
153 f.setValidating(false);
154 f.setNamespaceAware(true);
155 SAXParser p = f.newSAXParser();
156 p.parse(page.getInputStream(), h);
157
158 if (messageVector.size() == 0)
159 return null;
160 else
161 return vmFromVector(messageVector);
162
163 } catch (SAXException ex) {
164 return vmFromString(ex.toString());
165 } catch (ParserConfigurationException ex) {
166 return vmFromString(ex.toString());
167 } catch (IOException ex) {
168 return vmFromString(ex.toString());
169 }
170 }
171
172 //*********************************************************************
173 // Protected utility functions
174
175 // delegate validation to the appropriate expression language
176 protected String validateExpression(
177 String elem, String att, String expr) {
178
179 // let's just use the cache kept by the ExpressionEvaluatorManager
180 ExpressionEvaluator current;
181 try {
182 current =
183 ExpressionEvaluatorManager.getEvaluatorByName(
184 ExpressionEvaluatorManager.EVALUATOR_CLASS);
185 } catch (JspException ex) {
186 // (using JspException here feels ugly, but it's what EEM uses)
187 return ex.getMessage();
188 }
189
190 String response = current.validate(att, expr);
191 if (response == null)
192 return response;
193 else
194 return "tag = '" + elem + "' / attribute = '" + att + "': "
195 + response;
196 }
197
198 // utility methods to help us match elements in our tagset
199 protected boolean isTag(String tagUri,
200 String tagLn,
201 String matchUri,
202 String matchLn) {
203 if (tagUri == null
204 || tagLn == null
205 || matchUri == null
206 || matchLn == null)
207 return false;
208 // match beginning of URI since some suffix *_rt tags can
209 // be nested in EL enabled tags as defined by the spec
210 if (tagUri.length() > matchUri.length()) {
211 return (tagUri.startsWith(matchUri) && tagLn.equals(matchLn));
212 } else {
213 return (matchUri.startsWith(tagUri) && tagLn.equals(matchLn));
214 }
215 }
216
217 protected boolean isJspTag(String tagUri, String tagLn, String target) {
218 return isTag(tagUri, tagLn, JSP, target);
219 }
220
221 private boolean isTag( int type, String tagUri, String tagLn, String target) {
222 return ( this.tlvType == type && isTag(tagUri, tagLn, this.uri, target) );
223 }
224
225 protected boolean isCoreTag(String tagUri, String tagLn, String target) {
226 return isTag( TYPE_CORE, tagUri, tagLn, target );
227 }
228
229 protected boolean isFmtTag(String tagUri, String tagLn, String target) {
230 return isTag( TYPE_FMT, tagUri, tagLn, target );
231 }
232
233 protected boolean isSqlTag(String tagUri, String tagLn, String target) {
234 return isTag( TYPE_SQL, tagUri, tagLn, target );
235 }
236
237 protected boolean isXmlTag(String tagUri, String tagLn, String target) {
238 return isTag( TYPE_XML, tagUri, tagLn, target );
239 }
240
241 // utility method to determine if an attribute exists
242 protected boolean hasAttribute(Attributes a, String att) {
243 return (a.getValue(att) != null);
244 }
245
246 /*
247 * method to assist with failure [ as if it's not easy enough
248 * already :-) ]
249 */
250 protected void fail(String message) {
251 failed = true;
252 messageVector.add(new ValidationMessage(lastElementId, message));
253 }
254
255 // returns true if the given attribute name is specified, false otherwise
256 protected boolean isSpecified(TagData data, String attributeName) {
257 return (data.getAttribute(attributeName) != null);
258 }
259
260 // returns true if the 'scope' attribute is valid
261 protected boolean hasNoInvalidScope(Attributes a) {
262 String scope = a.getValue(SCOPE);
263
264 if ((scope != null)
265 && !scope.equals(PAGE_SCOPE)
266 && !scope.equals(REQUEST_SCOPE)
267 && !scope.equals(SESSION_SCOPE)
268 && !scope.equals(APPLICATION_SCOPE))
269 return false;
270
271 return true;
272 }
273
274 // returns true if the 'var' attribute is empty
275 protected boolean hasEmptyVar(Attributes a) {
276 if ("".equals(a.getValue(VAR)))
277 return true;
278 return false;
279 }
280
281 // returns true if the 'scope' attribute is present without 'var'
282 protected boolean hasDanglingScope(Attributes a) {
283 return (a.getValue(SCOPE) != null && a.getValue(VAR) == null);
284 }
285
286 // retrieves the local part of a QName
287 protected String getLocalPart(String qname) {
288 int colon = qname.indexOf(":");
289 if (colon == -1)
290 return qname;
291 else
292 return qname.substring(colon + 1);
293 }
294
295 //*********************************************************************
296 // Miscellaneous utility functions
297
298 // parses our configuration parameter for element:attribute pairs
299 private void configure(String info) {
300 // construct our configuration map
301 config = new HashMap();
302
303 // leave the map empty if we have nothing to configure
304 if (info == null)
305 return;
306
307 // separate parameter into space-separated tokens and store them
308 StringTokenizer st = new StringTokenizer(info);
309 while (st.hasMoreTokens()) {
310 String pair = st.nextToken();
311 StringTokenizer pairTokens = new StringTokenizer(pair, ":");
312 String element = pairTokens.nextToken();
313 String attribute = pairTokens.nextToken();
314 Object atts = config.get(element);
315 if (atts == null) {
316 atts = new HashSet();
317 config.put(element, atts);
318 }
319 ((Set) atts).add(attribute);
320 }
321 }
322
323 // constructs a ValidationMessage[] from a single String and no ID
324 static ValidationMessage[] vmFromString(String message) {
325 return new ValidationMessage[] {
326 new ValidationMessage(null, message)
327 };
328 }
329
330 // constructs a ValidationMessage[] from a ValidationMessage Vector
331 static ValidationMessage[] vmFromVector(Vector v) {
332 ValidationMessage[] vm = new ValidationMessage[v.size()];
333 for (int i = 0; i < vm.length; i++)
334 vm[i] = (ValidationMessage) v.get(i);
335 return vm;
336 }
337 }