/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * Free SoftwareFoundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.jsp.java; import com.caucho.jsp.JspParseException; import com.caucho.server.util.CauchoSystem; import com.caucho.util.L10N; import com.caucho.vfs.WriteStream; import com.caucho.xml.QName; import javax.servlet.jsp.HttpJspPage; import java.io.IOException; import java.util.ArrayList; import java.util.Locale; import java.util.logging.*; public class JspDirectivePage extends JspNode { private static final Logger log = Logger.getLogger(JspDirectivePage.class.getName()); static L10N L = new L10N(JspDirectivePage.class); private static final QName IS_EL_IGNORED = new QName("isELIgnored"); private static final QName IS_VELOCITY_ENABLED = new QName("isVelocityEnabled"); private static final QName INFO = new QName("info"); private static final QName CONTENT_TYPE = new QName("contentType"); private static final QName PAGE_ENCODING = new QName("pageEncoding"); private static final QName LANGUAGE = new QName("language"); private static final QName IMPORT = new QName("import"); private static final QName SESSION = new QName("session"); private static final QName BUFFER = new QName("buffer"); private static final QName ERROR_PAGE = new QName("errorPage"); private static final QName IS_ERROR_PAGE = new QName("isErrorPage"); private static final QName AUTO_FLUSH = new QName("autoFlush"); private static final QName IS_THREAD_SAFE = new QName("isThreadSafe"); private static final QName EXTENDS = new QName("extends"); private static final QName TRIM_WS = new QName("trimDirectiveWhitespaces"); private static final QName DEFER = new QName("deferredSyntaxAllowedAsLiteral"); /** * Adds an attribute. * * @param name the attribute name * @param value the attribute value */ public void addAttribute(QName name, String value) throws JspParseException { if (IS_EL_IGNORED.equals(name)) { boolean isIgnored = value.equals("true"); if (_parseState.isELIgnoredPageSpecified() && isIgnored != _parseState.isELIgnored()) throw error(L.l("isELIgnored values conflict")); _parseState.setELIgnored(isIgnored); _parseState.setELIgnoredPageSpecified(true); } /* else if (name.equals("isScriptingInvalid")) _parseState.setScriptingInvalid(value.equals("true")); */ else if (IS_VELOCITY_ENABLED.equals(name)) _parseState.setVelocityEnabled(value.equals("true")); else if (INFO.equals(name)) { String oldInfo = _parseState.getInfo(); if (oldInfo != null && ! value.equals(oldInfo)) throw error(L.l("info '{0}' conflicts with previous value of info '{1}'. Check the .jsp and any included .jsp files for conflicts.", value, oldInfo)); _parseState.setInfo(value); } else if (CONTENT_TYPE.equals(name)) { String oldContentType = _parseState.getContentType(); if (oldContentType != null && ! value.equals(oldContentType)) throw error(L.l("contentType '{0}' conflicts with previous value of contentType '{1}'. Check the .jsp and any included .jsp files for conflicts.", value, oldContentType)); _parseState.setContentType(value); String charEncoding = parseCharEncoding(value); if (charEncoding != null) _parseState.setCharEncoding(charEncoding); } else if (PAGE_ENCODING.equals(name)) { String oldEncoding = _parseState.getPageEncoding(); /* // jsp/01f1 if (oldEncoding != null) { String oldCanonical = Encoding.getMimeName(oldEncoding); String newCanonical = Encoding.getMimeName(value); if (! newCanonical.equals(oldCanonical)) 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)); } */ try { _parseState.setPageEncoding(value); // _parseState.setCharEncoding(value); } catch (JspParseException e) { log.log(Level.FINER, e.toString(), e); throw error(e.getMessage()); } } else if (LANGUAGE.equals(name)) { if (! value.equals("java")) throw error(L.l("'{0}' is not supported as a JSP scripting language.", value)); } else if (IMPORT.equals(name)) { _parseState.addImport(value); } else if (SESSION.equals(name)) { boolean isValid = false; if (value.equals("true")) isValid = _parseState.setSession(true); else if (value.equals("false")) isValid = _parseState.setSession(false); else throw error(L.l("session expects 'true' or 'false' at '{0}'", value)); _parseState.markSessionSet(); if (! isValid) throw error(L.l("session is assigned different values.")); } else if (BUFFER.equals(name)) { boolean isValid = _parseState.setBuffer(processBufferSize(value)); _parseState.markBufferSet(); if (! isValid) throw error(L.l("buffer is assigned different values.")); if (_parseState.getBuffer() == 0 && ! _parseState.isAutoFlush()) throw error(L.l("buffer must be non-zero when autoFlush is false.")); } else if (ERROR_PAGE.equals(name)) { String errorPage = _parseState.getErrorPage(); String newErrorPage = getRelativeUrl(value); _parseState.setErrorPage(newErrorPage); if (errorPage != null && ! errorPage.equals(newErrorPage)) { _parseState.setErrorPage(null); throw error(L.l("errorPage is assigned different value '{0}'.", newErrorPage)); } } else if (IS_ERROR_PAGE.equals(name)) { boolean isValid = false; if (value.equals("true")) isValid = _parseState.setErrorPage(true); else if (value.equals("false")) isValid = _parseState.setErrorPage(false); else throw error(L.l("isErrorPage expects 'true' or 'false' at '{0}'", value)); _parseState.markErrorPage(); if (! isValid) throw error(L.l("isErrorPage is assigned different values.")); } else if (AUTO_FLUSH.equals(name)) { boolean isValid = false; if (value.equals("true")) isValid = _parseState.setAutoFlush(true); else if (value.equals("false")) isValid = _parseState.setAutoFlush(false); else throw error(L.l("autoFlush expects 'true' or 'false' at '{0}'", value)); if (! isValid) throw error(L.l("autoFlush is assigned different values.")); if (_parseState.getBuffer() == 0 && ! _parseState.isAutoFlush()) throw error(L.l("buffer must be non-zero when autoFlush is false.")); _parseState.markAutoFlushSet(); } else if (IS_THREAD_SAFE.equals(name)) { boolean isValid = false; if (value.equals("true")) isValid = _parseState.setThreadSafe(true); else if (value.equals("false")) isValid = _parseState.setThreadSafe(false); else throw error(L.l("isThreadSafe expects 'true' or 'false' at '{0}'", value)); _parseState.markThreadSafeSet(); if (! isValid) throw error(L.l("isThreadSafe is assigned different values.")); } else if (EXTENDS.equals(name)) { Class cl = null; try { cl = CauchoSystem.loadClass(value); } catch (Exception e) { throw error(e); } if (! HttpJspPage.class.isAssignableFrom(cl)) throw error(L.l("'{0}' must implement HttpJspPage. The class named by jsp:directive.page extends='...' must implement HttpJspPage.", value)); Class oldExtends = _parseState.getExtends(); if (oldExtends != null && ! cl.equals(oldExtends)) throw error(L.l("extends '{0}' conflicts with previous value of extends '{1}'. Check the .jsp and any included .jsp files for conflicts.", value, oldExtends.getName())); _parseState.setExtends(cl); } else if (TRIM_WS.equals(name)) { if (value.equals("true")) _parseState.setTrimWhitespace(true); else if (value.equals("false")) _parseState.setTrimWhitespace(false); else throw error(L.l("trimDirectiveWhitespaces expects 'true' or 'false' at '{0}'", value)); } else if (DEFER.equals(name)) { if (value.equals("true")) _parseState.setDeferredSyntaxAllowedAsLiteral(true); else if (value.equals("false")) _parseState.setDeferredSyntaxAllowedAsLiteral(false); else throw error(L.l("deferredSyntaxAllowedAsLiteral expects 'true' or 'false' at '{0}'", value)); } else { throw error(L.l("'{0}' is an unknown JSP page directive attribute. See the JSP documentation for a complete list of page directive attributes.", name.getName())); } } /** * Parses the buffer size directive, grabbing the size out from the units. * * @param value buffer size string. * @return the size of the buffer in kb. */ private int processBufferSize(String value) throws JspParseException { if (value.equals("none")) return 0; int i = 0; int kb = 0; for (; i < value.length(); i++) { char ch = value.charAt(i); if (ch >= '0' && ch <= '9') kb = 10 * kb + ch - '0'; else break; } if (! value.substring(i).equals("kb")) throw error(L.l("Expected buffer size at '{0}'. Buffer sizes must end in 'kb'", value)); return 1024 * kb; } protected String getRelativeUrl(String value) { if (value.length() > 0 && value.charAt(0) == '/') return value; else return _parseState.getUriPwd() + value; } /** * Charset can be specific as follows: * test/html; z=9; charset=utf8; w=12 */ static String parseCharEncoding(String type) throws JspParseException { type = type.toLowerCase(Locale.ENGLISH); int i; char ch; while ((i = type.indexOf(';')) >= 0 && i < type.length()) { i++; while (i < type.length() && ((ch = type.charAt(i)) == ' ' || ch == '\t')) i++; if (i >= type.length()) return null; type = type.substring(i); i = type.indexOf('='); if (i >= 0) { if (! type.startsWith("charset")) continue; for (i++; i < type.length() && ((ch = type.charAt(i)) == ' ' || ch == '\t'); i++) { } type = type.substring(i); } for (i = 0; i < type.length() && (ch = type.charAt(i)) != ';' && ch != ' '; i++) { } return type.substring(0, i); } return null; } /** * Called when the tag ends. */ public void endAttributes() throws JspParseException { if (_gen.isTag()) throw error(L.l("page directives are forbidden in tags.")); } /** * Return true if the node only has static text. */ public boolean isStatic() { return true; } /** * Generates the XML text representation for the tag validation. * * @param os write stream to the generated XML. */ public void printXml(WriteStream os) throws IOException { os.print("<jsp:directive.page"); printJspId(os); if (! _parseState.isELIgnored()) os.print(" el-ignored='false'"); /* if (! _parseState.isScriptingEnabled()) os.print(" scripting-enabled='false'"); */ if (_parseState.getContentType() != null) os.print(" content-type='" + _parseState.getContentType() + "'"); ArrayList<String> imports = _parseState.getImportList(); if (imports != null && imports.size() != 0) { os.print(" import='"); for (int i = 0; i < imports.size(); i++) { if (i != 0) os.print(','); os.print(imports.get(i)); } os.print("'"); } os.print("/>"); } /** * Generates the code for the tag * * @param out the output writer for the generated java. */ public void generate(JspJavaWriter out) throws Exception { } }