001: /*
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright (c) 1999 The Apache Software Foundation. All rights
005: * reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution, if
020: * any, must include the following acknowlegement:
021: * "This product includes software developed by the
022: * Apache Software Foundation (http://www.apache.org/)."
023: * Alternately, this acknowlegement may appear in the software itself,
024: * if and wherever such third-party acknowlegements normally appear.
025: *
026: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
027: * Foundation" must not be used to endorse or promote products derived
028: * from this software without prior written permission. For written
029: * permission, please contact apache@apache.org.
030: *
031: * 5. Products derived from this software may not be called "Apache"
032: * nor may "Apache" appear in their names without prior written
033: * permission of the Apache Group.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of the Apache Software Foundation. For more
051: * information on the Apache Software Foundation, please see
052: * <http://www.apache.org/>.
053: *
054: */
055: package com.rimfaxe.webserver.compiler.jsp;
056:
057: import java.net.URL;
058:
059: import java.io.CharArrayWriter;
060: import java.io.IOException;
061: import java.io.InputStream;
062: import java.io.FileInputStream;
063: import java.util.Hashtable;
064: import java.util.Vector;
065: import java.util.Enumeration;
066:
067: import org.w3c.dom.*;
068: import javax.xml.parsers.DocumentBuilder;
069: import javax.xml.parsers.DocumentBuilderFactory;
070: import javax.xml.parsers.ParserConfigurationException;
071: import org.xml.sax.Attributes;
072: import org.xml.sax.EntityResolver;
073: import org.xml.sax.ErrorHandler;
074: import org.xml.sax.SAXException;
075: import org.xml.sax.SAXParseException;
076: import org.xml.sax.InputSource;
077: import org.xml.sax.helpers.AttributesImpl;
078:
079: import com.rimfaxe.webserver.compiler.jsp.error.JspErrorHandler;
080: import com.rimfaxe.webserver.compiler.JspToJavaException;
081:
082: /**
083: * This class has all the utility method(s).
084: * Ideally should move all the bean containers here.
085: *
086: * @author Mandar Raje.
087: * @author Rajiv Mordani.
088: * @author Danno Ferrin
089: * @author Pierre Delisle
090: * @author Lars Andersen
091: */
092: public class JspUtil {
093:
094: // Delimiters for request-time expressions (JSP and XML syntax)
095: private static final String OPEN_EXPR = "<%=";
096: private static final String CLOSE_EXPR = "%>";
097: private static final String OPEN_EXPR_XML = "%=";
098: private static final String CLOSE_EXPR_XML = "%";
099:
100: private static ErrorHandler errorHandler = new MyErrorHandler();
101: private static EntityResolver entityResolver = new MyEntityResolver();
102:
103: public static char[] removeQuotes(char[] chars) {
104: CharArrayWriter caw = new CharArrayWriter();
105: for (int i = 0; i < chars.length; i++) {
106: if (chars[i] == '%' && chars[i + 1] == '\\'
107: && chars[i + 2] == '\\' && chars[i + 3] == '>') {
108: caw.write('%');
109: caw.write('>');
110: i = i + 3;
111: } else
112: caw.write(chars[i]);
113: }
114: return caw.toCharArray();
115: }
116:
117: public static char[] escapeQuotes(char[] chars) {
118: // Prescan to convert %\> to %>
119: String s = new String(chars);
120: while (true) {
121: int n = s.indexOf("%\\>");
122: if (n < 0)
123: break;
124: StringBuffer sb = new StringBuffer(s.substring(0, n));
125: sb.append("%>");
126: sb.append(s.substring(n + 3));
127: s = sb.toString();
128: }
129: chars = s.toCharArray();
130: return (chars);
131:
132: // Escape all backslashes not inside a Java string literal
133: /*
134: CharArrayWriter caw = new CharArrayWriter();
135: boolean inJavaString = false;
136: for (int i = 0; i < chars.length; i++) {
137: if (chars[i] == '"') inJavaString = !inJavaString;
138: // escape out the escape character
139: if (!inJavaString && (chars[i] == '\\')) caw.write('\\');
140: caw.write(chars[i]);
141: }
142: return caw.toCharArray();
143: */
144: }
145:
146: /**
147: * Checks if the token is a runtime expression.
148: * In standard JSP syntax, a runtime expression starts with '<%' and
149: * ends with '%>'. When the JSP document is in XML syntax, a runtime
150: * expression starts with '%=' and ends with '%'.
151: *
152: * @param token The token to be checked
153: * return whether the token is a runtime expression or not.
154: */
155: public static boolean isExpression(String token, boolean isXml) {
156: String openExpr;
157: String closeExpr;
158: if (isXml) {
159: openExpr = OPEN_EXPR_XML;
160: closeExpr = CLOSE_EXPR_XML;
161: } else {
162: openExpr = OPEN_EXPR;
163: closeExpr = CLOSE_EXPR;
164: }
165: if (token.startsWith(openExpr) && token.endsWith(closeExpr)) {
166: return true;
167: } else {
168: return false;
169: }
170: }
171:
172: /**
173: * @return the "expression" part of a runtime expression,
174: * taking the delimiters out.
175: */
176: public static String getExpr(String expression, boolean isXml) {
177: String returnString;
178: String openExpr;
179: String closeExpr;
180: if (isXml) {
181: openExpr = OPEN_EXPR_XML;
182: closeExpr = CLOSE_EXPR_XML;
183: } else {
184: openExpr = OPEN_EXPR;
185: closeExpr = CLOSE_EXPR;
186: }
187: int length = expression.length();
188: if (expression.startsWith(openExpr)
189: && expression.endsWith(closeExpr)) {
190: returnString = expression.substring(openExpr.length(),
191: length - closeExpr.length());
192: } else {
193: returnString = "";
194: }
195: return returnString;
196: }
197:
198: /**
199: * Takes a potential expression and converts it into XML form
200: */
201: public static String getExprInXml(String expression) {
202: String returnString;
203: int length = expression.length();
204:
205: if (expression.startsWith(OPEN_EXPR)
206: && expression.endsWith(CLOSE_EXPR)) {
207: returnString = expression.substring(1, length - 1);
208: } else {
209: returnString = expression;
210: }
211:
212: return escapeXml(returnString);
213: }
214:
215: /**
216: * Parses the XML document contained in the InputStream.
217: *
218: * @deprecated Use ParserUtils.parseXMLDocument() instead
219: */
220: public static Document parseXMLDoc(String uri, InputStream in)
221: throws JasperException {
222: return parseXMLDocJaxp(uri, in);
223: }
224:
225: /**
226: * Parses the XML document contained in the InputStream.
227: * This XML document is either web.xml or a tld.
228: * [The TLD has to be cached internally (see MyEntityResolver)]
229: *
230: * @deprecated Use ParserUtils.parseXMLDocument() instead
231: */
232: public static Document parseXMLDocJaxp(String uri, InputStream in)
233: throws JasperException {
234: try {
235: Document doc;
236: DocumentBuilderFactory docFactory = DocumentBuilderFactory
237: .newInstance();
238: docFactory.setValidating(true);
239: docFactory.setNamespaceAware(true);
240: DocumentBuilder builder = docFactory.newDocumentBuilder();
241: builder.setEntityResolver(entityResolver);
242: builder.setErrorHandler(errorHandler);
243: doc = builder.parse(in);
244: return doc;
245: } catch (ParserConfigurationException ex) {
246: throw new JasperException(Constants.getString(
247: "jsp.error.parse.xml", new Object[] { uri,
248: ex.getMessage() }));
249: } catch (SAXParseException ex) {
250: throw new JasperException(Constants.getString(
251: "jsp.error.parse.xml.line", new Object[] { uri,
252: new Integer(ex.getLineNumber()),
253: new Integer(ex.getColumnNumber()),
254: ex.getMessage() }));
255: } catch (SAXException sx) {
256: throw new JasperException(Constants.getString(
257: "jsp.error.parse.xml", new Object[] { uri,
258: sx.getMessage() }));
259: } catch (IOException io) {
260: throw new JasperException(Constants.getString(
261: "jsp.error.parse.xml", new Object[] { uri,
262: io.toString() }));
263: }
264: }
265:
266: public static void checkAttributes(String typeOfTag,
267: Attributes attrs, ValidAttribute[] validAttributes,
268: Mark start) throws JasperException, JspToJavaException {
269: boolean valid = true;
270: // AttributesImpl.removeAttribute is broken, so we do this...
271: int tempLength = attrs.getLength();
272: Vector temp = new Vector(tempLength, 1);
273: for (int i = 0; i < tempLength; i++) {
274: String qName = attrs.getQName(i);
275: if ((!qName.equals("xmlns"))
276: && (!qName.startsWith("xmlns:")))
277: temp.addElement(qName);
278: }
279:
280: /*
281: * First check to see if all the mandatory attributes are present.
282: * If so only then proceed to see if the other attributes are valid
283: * for the particular tag.
284: */
285: String missingAttribute = null;
286:
287: for (int i = 0; i < validAttributes.length; i++) {
288: int attrPos;
289: if (validAttributes[i].mandatory) {
290: attrPos = temp.indexOf(validAttributes[i].name);
291: if (attrPos != -1) {
292: temp.remove(attrPos);
293: valid = true;
294: } else {
295: valid = false;
296: missingAttribute = validAttributes[i].name;
297: break;
298: }
299: }
300: }
301:
302: // If mandatory attribute is missing then the exception is thrown
303: if (!valid)
304: throw new JspToJavaException(start,
305: "Mandatory Attribute type=" + typeOfTag + " name="
306: + missingAttribute);
307:
308: // Check to see if there are any more attributes for the specified tag.
309: int attrLeftLength = temp.size();
310: if (attrLeftLength == 0)
311: return;
312:
313: // Now check to see if the rest of the attributes are valid too.
314: String attribute = null;
315:
316: for (int j = 0; j < attrLeftLength; j++) {
317: valid = false;
318: attribute = (String) temp.elementAt(j);
319: for (int i = 0; i < validAttributes.length; i++) {
320: if (attribute.equals(validAttributes[i].name)) {
321: valid = true;
322: break;
323: }
324: }
325: if (!valid)
326: throw new JspToJavaException(start,
327: "Invalid Attribute type=" + typeOfTag
328: + " name=" + missingAttribute);
329:
330: }
331: }
332:
333: public static String escapeQueryString(String unescString) {
334: if (unescString == null)
335: return null;
336:
337: String escString = "";
338: String shellSpChars = "\\\"";
339:
340: for (int index = 0; index < unescString.length(); index++) {
341: char nextChar = unescString.charAt(index);
342:
343: if (shellSpChars.indexOf(nextChar) != -1)
344: escString += "\\";
345:
346: escString += nextChar;
347: }
348: return escString;
349: }
350:
351: /**
352: * Escape the 5 entities defined by XML.
353: */
354: public static String escapeXml(String s) {
355: if (s == null)
356: return null;
357: StringBuffer sb = new StringBuffer();
358: for (int i = 0; i < s.length(); i++) {
359: char c = s.charAt(i);
360: if (c == '<') {
361: sb.append("<");
362: } else if (c == '>') {
363: sb.append(">");
364: } else if (c == '\'') {
365: sb.append("'");
366: } else if (c == '&') {
367: sb.append("&");
368: } else if (c == '"') {
369: sb.append(""");
370: } else {
371: sb.append(c);
372: }
373: }
374: return sb.toString();
375: }
376:
377: /**
378: * Replaces any occurrence of <tt>replace</tt> with <tt>with</tt> in the
379: * given string.
380: */
381: public static String replace(String name, char replace, String with) {
382: StringBuffer buf = new StringBuffer();
383: int begin = 0;
384: int end;
385: int last = name.length();
386:
387: while (true) {
388: end = name.indexOf(replace, begin);
389: if (end < 0) {
390: end = last;
391: }
392: buf.append(name.substring(begin, end));
393: if (end == last) {
394: break;
395: }
396: buf.append(with);
397: begin = end + 1;
398: }
399:
400: return buf.toString();
401: }
402:
403: public static class ValidAttribute {
404: String name;
405: boolean mandatory;
406:
407: public ValidAttribute(String name, boolean mandatory) {
408: this .name = name;
409: this .mandatory = mandatory;
410: }
411:
412: public ValidAttribute(String name) {
413: this (name, false);
414: }
415: }
416:
417: public static Hashtable attrsToHashtable(Attributes attrs) {
418: int len = attrs.getLength();
419: Hashtable table = new Hashtable(len);
420: for (int i = 0; i < len; i++) {
421: table.put(attrs.getQName(i), attrs.getValue(i));
422: }
423: return table;
424: }
425:
426: /**
427: * Get the data for the first child associated with the
428: * Element provided as argument. It is assumed that this
429: * first child is of type Text.
430: *
431: * @param e the DOM Element to read from
432: * @return the data associated with the first child of the DOM
433: * element.
434: */
435: public static String getElementChildTextData(Element e) {
436: String s = null;
437: Text t = (Text) e.getFirstChild();
438: if (t != null) {
439: s = t.getData();
440: if (s != null) {
441: s = s.trim();
442: }
443: }
444: return s;
445: }
446:
447: /**
448: * Convert a String value to 'boolean'.
449: * Besides the standard conversions done by
450: * Boolean.valueOf(s).booleanValue(), the value "yes"
451: * (ignore case) is also converted to 'true'.
452: * If 's' is null, then 'false' is returned.
453: *
454: * @param s the string to be converted
455: * @return the boolean value associated with the string s
456: */
457: public static boolean booleanValue(String s) {
458: boolean b = false;
459: if (s != null) {
460: if (s.equalsIgnoreCase("yes")) {
461: b = true;
462: } else {
463: b = Boolean.valueOf(s).booleanValue();
464: }
465: }
466: return b;
467: }
468: }
469:
470: class MyEntityResolver implements EntityResolver {
471: public InputSource resolveEntity(String publicId, String systemId)
472: throws SAXException {
473: for (int i = 0; i < Constants.CACHED_DTD_PUBLIC_IDS.length; i++) {
474: String cachedDtdPublicId = Constants.CACHED_DTD_PUBLIC_IDS[i];
475: if (cachedDtdPublicId.equals(publicId)) {
476: String resourcePath = Constants.CACHED_DTD_RESOURCE_PATHS[i];
477: InputStream input = this .getClass()
478: .getResourceAsStream(resourcePath);
479: if (input == null) {
480: throw new SAXException(Constants.getString(
481: "jsp.error.internal.filenotfound",
482: new Object[] { resourcePath }));
483: }
484: InputSource isrc = new InputSource(input);
485: return isrc;
486: }
487: }
488: //Constants.message("jsp.error.parse.xml.invalidPublicId",
489: // new Object[]{publicId}, Logger.ERROR);
490: return null;
491: }
492: }
493:
494: class MyErrorHandler implements ErrorHandler {
495: public void warning(SAXParseException ex) throws SAXException {
496: // We ignore warnings
497: }
498:
499: public void error(SAXParseException ex) throws SAXException {
500: throw ex;
501: }
502:
503: public void fatalError(SAXParseException ex) throws SAXException {
504: throw ex;
505: }
506: }
|