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.java;
030:
031: import com.caucho.jsp.JspParseException;
032: import com.caucho.server.util.CauchoSystem;
033: import com.caucho.util.L10N;
034: import com.caucho.vfs.WriteStream;
035: import com.caucho.xml.QName;
036:
037: import javax.servlet.jsp.HttpJspPage;
038: import java.io.IOException;
039: import java.util.ArrayList;
040: import java.util.logging.*;
041:
042: public class JspDirectivePage extends JspNode {
043: static L10N L = new L10N(JspDirectivePage.class);
044:
045: private static final QName IS_EL_IGNORED = new QName("isELIgnored");
046: private static final QName IS_VELOCITY_ENABLED = new QName(
047: "isVelocityEnabled");
048: private static final QName INFO = new QName("info");
049: private static final QName CONTENT_TYPE = new QName("contentType");
050: private static final QName PAGE_ENCODING = new QName("pageEncoding");
051: private static final QName LANGUAGE = new QName("language");
052: private static final QName IMPORT = new QName("import");
053: private static final QName SESSION = new QName("session");
054: private static final QName BUFFER = new QName("buffer");
055: private static final QName ERROR_PAGE = new QName("errorPage");
056: private static final QName IS_ERROR_PAGE = new QName("isErrorPage");
057: private static final QName AUTO_FLUSH = new QName("autoFlush");
058: private static final QName IS_THREAD_SAFE = new QName(
059: "isThreadSafe");
060: private static final QName EXTENDS = new QName("extends");
061: private static final QName TRIM_WS = new QName(
062: "trimDirectiveWhitespaces");
063: private static final QName DEFER = new QName(
064: "deferredSyntaxAllowedAsLiteral");
065:
066: private Boolean _isElIgnored;
067:
068: /**
069: * Adds an attribute.
070: *
071: * @param name the attribute name
072: * @param value the attribute value
073: */
074: public void addAttribute(QName name, String value)
075: throws JspParseException {
076: if (IS_EL_IGNORED.equals(name)) {
077: boolean isIgnored = value.equals("true");
078:
079: if (!_parseState.setELIgnored(isIgnored))
080: throw error(L.l("isELIgnored values conflict"));
081:
082: _parseState.markELIgnoredSet();
083: }
084: /*
085: else if (name.equals("isScriptingInvalid"))
086: _parseState.setScriptingInvalid(value.equals("true"));
087: */
088: else if (IS_VELOCITY_ENABLED.equals(name))
089: _parseState.setVelocityEnabled(value.equals("true"));
090: else if (INFO.equals(name)) {
091: String oldInfo = _parseState.getInfo();
092:
093: if (oldInfo != null && !value.equals(oldInfo))
094: throw error(L
095: .l(
096: "info '{0}' conflicts with previous value of info '{1}'. Check the .jsp and any included .jsp files for conflicts.",
097: value, oldInfo));
098:
099: _parseState.setInfo(value);
100: } else if (CONTENT_TYPE.equals(name)) {
101: String oldContentType = _parseState.getContentType();
102:
103: if (oldContentType != null && !value.equals(oldContentType))
104: throw error(L
105: .l(
106: "contentType '{0}' conflicts with previous value of contentType '{1}'. Check the .jsp and any included .jsp files for conflicts.",
107: value, oldContentType));
108:
109: _parseState.setContentType(value);
110: String charEncoding = parseCharEncoding(value);
111: if (charEncoding != null)
112: _parseState.setCharEncoding(charEncoding);
113: } else if (PAGE_ENCODING.equals(name)) {
114: String oldEncoding = _parseState.getPageEncoding();
115:
116: /*
117: // jsp/01f1
118: if (oldEncoding != null) {
119: String oldCanonical = Encoding.getMimeName(oldEncoding);
120: String newCanonical = Encoding.getMimeName(value);
121:
122: if (! newCanonical.equals(oldCanonical))
123: throw error(L.l("pageEncoding '{0}' conflicts with previous value of pageEncoding '{1}'. Check the .jsp and any included .jsp files for conflicts.", value, oldEncoding));
124: }
125: */
126:
127: try {
128: _parseState.setPageEncoding(value);
129: _parseState.setCharEncoding(value);
130: } catch (JspParseException e) {
131: log.log(Level.FINER, e.toString(), e);
132:
133: throw error(e.getMessage());
134: }
135: } else if (LANGUAGE.equals(name)) {
136: if (!value.equals("java"))
137: throw error(L
138: .l(
139: "'{0}' is not supported as a JSP scripting language.",
140: value));
141: } else if (IMPORT.equals(name)) {
142: _parseState.addImport(value);
143: } else if (SESSION.equals(name)) {
144: boolean isValid = false;
145:
146: if (value.equals("true"))
147: isValid = _parseState.setSession(true);
148: else if (value.equals("false"))
149: isValid = _parseState.setSession(false);
150: else
151: throw error(L.l(
152: "session expects 'true' or 'false' at '{0}'",
153: value));
154:
155: _parseState.markSessionSet();
156:
157: if (!isValid)
158: throw error(L
159: .l("session is assigned different values."));
160: } else if (BUFFER.equals(name)) {
161: boolean isValid = _parseState
162: .setBuffer(processBufferSize(value));
163:
164: _parseState.markBufferSet();
165:
166: if (!isValid)
167: throw error(L.l("buffer is assigned different values."));
168:
169: if (_parseState.getBuffer() == 0
170: && !_parseState.isAutoFlush())
171: throw error(L
172: .l("buffer must be non-zero when autoFlush is false."));
173: } else if (ERROR_PAGE.equals(name)) {
174: String errorPage = _parseState.getErrorPage();
175:
176: String newErrorPage = getRelativeUrl(value);
177:
178: _parseState.setErrorPage(newErrorPage);
179:
180: if (errorPage != null && !errorPage.equals(newErrorPage)) {
181: _parseState.setErrorPage(null);
182: throw error(L.l(
183: "errorPage is assigned different value '{0}'.",
184: newErrorPage));
185: }
186: } else if (IS_ERROR_PAGE.equals(name)) {
187: boolean isValid = false;
188:
189: if (value.equals("true"))
190: isValid = _parseState.setErrorPage(true);
191: else if (value.equals("false"))
192: isValid = _parseState.setErrorPage(false);
193: else
194: throw error(L
195: .l(
196: "isErrorPage expects 'true' or 'false' at '{0}'",
197: value));
198:
199: _parseState.markErrorPage();
200:
201: if (!isValid)
202: throw error(L
203: .l("isErrorPage is assigned different values."));
204: } else if (AUTO_FLUSH.equals(name)) {
205: boolean isValid = false;
206:
207: if (value.equals("true"))
208: isValid = _parseState.setAutoFlush(true);
209: else if (value.equals("false"))
210: isValid = _parseState.setAutoFlush(false);
211: else
212: throw error(L.l(
213: "autoFlush expects 'true' or 'false' at '{0}'",
214: value));
215:
216: if (!isValid)
217: throw error(L
218: .l("autoFlush is assigned different values."));
219:
220: if (_parseState.getBuffer() == 0
221: && !_parseState.isAutoFlush())
222: throw error(L
223: .l("buffer must be non-zero when autoFlush is false."));
224:
225: _parseState.markAutoFlushSet();
226: } else if (IS_THREAD_SAFE.equals(name)) {
227: boolean isValid = false;
228:
229: if (value.equals("true"))
230: isValid = _parseState.setThreadSafe(true);
231: else if (value.equals("false"))
232: isValid = _parseState.setThreadSafe(false);
233: else
234: throw error(L
235: .l(
236: "isThreadSafe expects 'true' or 'false' at '{0}'",
237: value));
238:
239: _parseState.markThreadSafeSet();
240:
241: if (!isValid)
242: throw error(L
243: .l("isThreadSafe is assigned different values."));
244: } else if (EXTENDS.equals(name)) {
245: Class cl = null;
246:
247: try {
248: cl = CauchoSystem.loadClass(value);
249: } catch (Exception e) {
250: throw error(e);
251: }
252:
253: if (!HttpJspPage.class.isAssignableFrom(cl))
254: throw error(L
255: .l(
256: "'{0}' must implement HttpJspPage. The class named by jsp:directive.page extends='...' must implement HttpJspPage.",
257: value));
258:
259: Class oldExtends = _parseState.getExtends();
260:
261: if (oldExtends != null && !cl.equals(oldExtends))
262: throw error(L
263: .l(
264: "extends '{0}' conflicts with previous value of extends '{1}'. Check the .jsp and any included .jsp files for conflicts.",
265: value, oldExtends.getName()));
266:
267: _parseState.setExtends(cl);
268: } else if (TRIM_WS.equals(name)) {
269: if (value.equals("true"))
270: _parseState.setTrimWhitespace(true);
271: else if (value.equals("false"))
272: _parseState.setTrimWhitespace(false);
273: else
274: throw error(L
275: .l(
276: "trimDirectiveWhitespaces expects 'true' or 'false' at '{0}'",
277: value));
278: } else if (DEFER.equals(name)) {
279: if (value.equals("true"))
280: _parseState.setDeferredSyntaxAllowedAsLiteral(true);
281: else if (value.equals("false"))
282: _parseState.setDeferredSyntaxAllowedAsLiteral(false);
283: else
284: throw error(L
285: .l(
286: "deferredSyntaxAllowedAsLiteral expects 'true' or 'false' at '{0}'",
287: value));
288: } else {
289: throw error(L
290: .l(
291: "'{0}' is an unknown JSP page directive attribute. See the JSP documentation for a complete list of page directive attributes.",
292: name.getName()));
293: }
294: }
295:
296: /**
297: * Parses the buffer size directive, grabbing the size out from the units.
298: *
299: * @param value buffer size string.
300: * @return the size of the buffer in kb.
301: */
302: private int processBufferSize(String value)
303: throws JspParseException {
304: if (value.equals("none"))
305: return 0;
306:
307: int i = 0;
308: int kb = 0;
309: for (; i < value.length(); i++) {
310: char ch = value.charAt(i);
311: if (ch >= '0' && ch <= '9')
312: kb = 10 * kb + ch - '0';
313: else
314: break;
315: }
316:
317: if (!value.substring(i).equals("kb"))
318: throw error(L
319: .l(
320: "Expected buffer size at '{0}'. Buffer sizes must end in 'kb'",
321: value));
322:
323: return 1024 * kb;
324: }
325:
326: protected String getRelativeUrl(String value) {
327: if (value.length() > 0 && value.charAt(0) == '/')
328: return value;
329: else
330: return _parseState.getUriPwd() + value;
331: }
332:
333: /**
334: * Charset can be specific as follows:
335: * test/html; z=9; charset=utf8; w=12
336: */
337: static String parseCharEncoding(String type)
338: throws JspParseException {
339: type = type.toLowerCase();
340: int i;
341: char ch;
342: while ((i = type.indexOf(';')) >= 0 && i < type.length()) {
343: i++;
344: while (i < type.length()
345: && ((ch = type.charAt(i)) == ' ' || ch == '\t'))
346: i++;
347:
348: if (i >= type.length())
349: return null;
350:
351: type = type.substring(i);
352: i = type.indexOf('=');
353: if (i >= 0) {
354: if (!type.startsWith("charset"))
355: continue;
356:
357: for (i++; i < type.length()
358: && ((ch = type.charAt(i)) == ' ' || ch == '\t'); i++) {
359: }
360:
361: type = type.substring(i);
362: }
363:
364: for (i = 0; i < type.length()
365: && (ch = type.charAt(i)) != ';' && ch != ' '; i++) {
366: }
367:
368: return type.substring(0, i);
369: }
370:
371: return null;
372: }
373:
374: /**
375: * Called when the tag ends.
376: */
377: public void endAttributes() throws JspParseException {
378: if (_gen.isTag())
379: throw error(L.l("page directives are forbidden in tags."));
380: }
381:
382: /**
383: * Return true if the node only has static text.
384: */
385: public boolean isStatic() {
386: return true;
387: }
388:
389: /**
390: * Generates the XML text representation for the tag validation.
391: *
392: * @param os write stream to the generated XML.
393: */
394: public void printXml(WriteStream os) throws IOException {
395: os.print("<jsp:directive.page");
396: printJspId(os);
397: if (!_parseState.isELIgnored())
398: os.print(" el-ignored='false'");
399: /*
400: if (! _parseState.isScriptingEnabled())
401: os.print(" scripting-enabled='false'");
402: */
403: if (_parseState.getContentType() != null)
404: os.print(" content-type='" + _parseState.getContentType()
405: + "'");
406:
407: ArrayList<String> imports = _parseState.getImportList();
408:
409: if (imports != null && imports.size() != 0) {
410: os.print(" import='");
411: for (int i = 0; i < imports.size(); i++) {
412: if (i != 0)
413: os.print(',');
414: os.print(imports.get(i));
415: }
416: os.print("'");
417: }
418:
419: os.print("/>");
420: }
421:
422: /**
423: * Generates the code for the tag
424: *
425: * @param out the output writer for the generated java.
426: */
427: public void generate(JspJavaWriter out) throws Exception {
428: }
429: }
|