/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/ package org.apache.jasper.compiler;
/** * Controller for the parsing of a JSP page. * <p> * The same ParserController instance is used for a JSP page and any JSP * segments included by it (via an include directive), where each segment may * be provided in standard or XML syntax. This class selects and invokes the * appropriate parser for the JSP page and its included segments. * * @author Pierre Delisle * @author Jan Luehe
*/ class ParserController implements TagConstants {
/* * Indicates the syntax (XML or standard) of the file being processed
*/ privateboolean isXml;
/* * A stack to keep track of the 'current base directory' * for include directives that refer to relative paths.
*/ privatefinal Deque<String> baseDirStack = new ArrayDeque<>();
public JspCompilationContext getJspCompilationContext () { return ctxt;
}
public Compiler getCompiler () { return compiler;
}
/** * Parses a JSP page or tag file. This is invoked by the compiler. * * @param inFileName The path to the JSP page or tag file to be parsed. * * @return The parsed nodes * * @throws JasperException If an error occurs during parsing * @throws IOException If an I/O error occurs such as the file not being * found
*/ public Node.Nodes parse(String inFileName) throws JasperException, IOException { // If we're parsing a packaged tag file or a resource included by it // (using an include directive), ctxt.getTagFileJar() returns the // JAR file from which to read the tag file or included resource, // respectively.
isTagFile = ctxt.isTagFile();
directiveOnly = false; return doParse(inFileName, null, ctxt.getTagFileJar());
}
/** * Parses the directives of a JSP page or tag file. This is invoked by the * compiler. * * @param inFileName The path to the JSP page or tag file to be parsed. * * @return The parsed directive nodes * * @throws JasperException If an error occurs during parsing * @throws IOException If an I/O error occurs such as the file not being * found
*/ public Node.Nodes parseDirectives(String inFileName) throws JasperException, IOException { // If we're parsing a packaged tag file or a resource included by it // (using an include directive), ctxt.getTagFileJar() returns the // JAR file from which to read the tag file or included resource, // respectively.
isTagFile = ctxt.isTagFile();
directiveOnly = true; return doParse(inFileName, null, ctxt.getTagFileJar());
}
/** * Processes an include directive with the given path. * * @param inFileName The path to the resource to be included. * @param parent The parent node of the include directive. * @param jar The JAR file from which to read the included resource, * or null of the included resource is to be read from the filesystem * * @return The parsed nodes * * @throws JasperException If an error occurs during parsing * @throws IOException If an I/O error occurs such as the file not being * found
*/ public Node.Nodes parse(String inFileName, Node parent, Jar jar) throws JasperException, IOException { // For files that are statically included, isTagfile and directiveOnly // remain unchanged. return doParse(inFileName, parent, jar);
}
/** * Extracts tag file directive information from the given tag file. * * This is invoked by the compiler * * @param inFileName The name of the tag file to be parsed. * @param jar The location of the tag file. * * @return The parsed tag file nodes * * @throws JasperException If an error occurs during parsing * @throws IOException If an I/O error occurs such as the file not being * found
*/ public Node.Nodes parseTagFileDirectives(String inFileName, Jar jar) throws JasperException, IOException { boolean isTagFileSave = isTagFile; boolean directiveOnlySave = directiveOnly;
isTagFile = true;
directiveOnly = true;
Node.Nodes page = doParse(inFileName, null, jar);
directiveOnly = directiveOnlySave;
isTagFile = isTagFileSave; return page;
}
/** * Parses the JSP page or tag file with the given path name. * * @param inFileName The name of the JSP page or tag file to be parsed. * @param parent The parent node (non-null when processing an include * directive) * @param jar The JAR file from which to read the JSP page or tag file, * or null if the JSP page or tag file is to be read from the filesystem
*/ private Node.Nodes doParse(String inFileName, Node parent, Jar jar) throws FileNotFoundException, JasperException, IOException {
// Figure out what type of JSP document and encoding type we are // dealing with
determineSyntaxAndEncoding(absFileName, jar, jspConfigPageEnc);
if (parent != null) { // Included resource, add to dependent list if (jar == null) {
compiler.getPageInfo().addDependant(absFileName,
ctxt.getLastModified(absFileName));
} else {
String entry = absFileName.substring(1);
compiler.getPageInfo().addDependant(jar.getURL(entry), Long.valueOf(jar.getLastModified(entry)));
}
}
if ((isXml && isEncodingSpecifiedInProlog) || isBomPresent) { /* * Make sure the encoding explicitly specified in the XML * prolog (if any) matches that in the JSP config element * (if any), treating "UTF-16", "UTF-16BE", and "UTF-16LE" as * identical.
*/ if (jspConfigPageEnc != null && !jspConfigPageEnc.equals(sourceEnc)
&& (!jspConfigPageEnc.startsWith("UTF-16")
|| !sourceEnc.startsWith("UTF-16"))) {
err.jspError("jsp.error.prolog_config_encoding_mismatch",
sourceEnc, jspConfigPageEnc);
}
}
// Dispatch to the appropriate parser if (isXml) { // JSP document (XML syntax) // InputStream for jspx page is created and properly closed in // JspDocumentParser.
parsedPage = JspDocumentParser.parse(this, absFileName, jar, parent,
isTagFile, directiveOnly, sourceEnc, jspConfigPageEnc,
isEncodingSpecifiedInProlog, isBomPresent);
} else { // Standard syntax try (InputStreamReader inStreamReader = JspUtil.getReader(
absFileName, sourceEnc, jar, ctxt, err, skip)) {
JspReader jspReader = new JspReader(ctxt, absFileName,
inStreamReader, err);
parsedPage = Parser.parse(this, jspReader, parent, isTagFile,
directiveOnly, jar, sourceEnc, jspConfigPageEnc,
isDefaultPageEncoding, isBomPresent);
}
}
baseDirStack.remove();
return parsedPage;
}
/* * Checks to see if the given URI is matched by a URL pattern specified in * a jsp-property-group in web.xml, and if so, returns the value of the * <page-encoding> element. * * @param absFileName The URI to match * * @return The value of the <page-encoding> attribute of the * jsp-property-group with matching URL pattern
*/ private String getJspConfigPageEncoding(String absFileName) {
/** * Determines the syntax (standard or XML) and page encoding properties * for the given file, and stores them in the 'isXml' and 'sourceEnc' * instance variables, respectively.
*/ privatevoid determineSyntaxAndEncoding(String absFileName, Jar jar,
String jspConfigPageEnc) throws JasperException, IOException {
isXml = false;
/* * 'true' if the syntax (XML or standard) of the file is given * from external information: either via a JSP configuration element, * the ".jspx" suffix, or the enclosing file (for included resources)
*/ boolean isExternal = false;
/* * Indicates whether we need to revert from temporary usage of * "ISO-8859-1" back to "UTF-8"
*/ boolean revert = false;
JspConfig jspConfig = ctxt.getOptions().getJspConfig();
JspConfig.JspProperty jspProperty = jspConfig.findJspProperty(
absFileName); if (jspProperty.isXml() != null) { // If <is-xml> is specified in a <jsp-property-group>, it is used.
isXml = JspUtil.booleanValue(jspProperty.isXml());
isExternal = true;
} elseif (absFileName.endsWith(".jspx")
|| absFileName.endsWith(".tagx")) {
isXml = true;
isExternal = true;
}
if (isExternal && !isXml) { // JSP (standard) syntax. Use encoding specified in jsp-config // if provided.
sourceEnc = jspConfigPageEnc; if (sourceEnc != null) { return;
} // We don't know the encoding, so use BOM to determine it
sourceEnc = "ISO-8859-1";
} else { // XML syntax or unknown, (auto)detect encoding ...
EncodingDetector encodingDetector; try (BufferedInputStream bis = JspUtil.getInputStream(absFileName, jar, ctxt)) {
encodingDetector = new EncodingDetector(bis);
}
if (!isXml && sourceEnc.equals("UTF-8")) { /* * We don't know if we're dealing with XML or standard syntax. * Therefore, we need to check to see if the page contains * a <jsp:root> element. * * We need to be careful, because the page may be encoded in * ISO-8859-1 (or something entirely different), and may * contain byte sequences that will cause a UTF-8 converter to * throw exceptions. * * It is safe to use a source encoding of ISO-8859-1 in this * case, as there are no invalid byte sequences in ISO-8859-1, * and the byte/character sequences we're looking for (i.e., * <jsp:root>) are identical in either encoding (both UTF-8 * and ISO-8859-1 are extensions of ASCII).
*/
sourceEnc = "ISO-8859-1";
revert = true;
}
}
if (isXml) { // (This implies 'isExternal' is TRUE.) // We know we're dealing with a JSP document (via JSP config or // ".jspx" suffix), so we're done. return;
}
/* * At this point, 'isExternal' or 'isXml' is FALSE. * Search for jsp:root action, in order to determine if we're dealing * with XML or standard syntax (unless we already know what we're * dealing with, i.e., when 'isExternal' is TRUE and 'isXml' is FALSE). * No check for XML prolog, since nothing prevents a page from * outputting XML and still using JSP syntax (in this case, the * XML prolog is treated as template text).
*/
JspReader jspReader = null; try {
jspReader = new JspReader(ctxt, absFileName, sourceEnc, jar, err);
} catch (FileNotFoundException ex) { thrownew JasperException(ex);
}
Mark startMark = jspReader.mark(); if (!isExternal) {
jspReader.reset(startMark); if (hasJspRoot(jspReader)) { if (revert) {
sourceEnc = "UTF-8";
}
isXml = true; return;
} else { if (revert && isBomPresent) {
sourceEnc = "UTF-8";
}
isXml = false;
}
}
/* * At this point, we know we're dealing with JSP syntax. * If an XML prolog is provided, it's treated as template text. * Determine the page encoding from the page directive, unless it's * specified via JSP config.
*/ if (!isBomPresent) {
sourceEnc = jspConfigPageEnc; if (sourceEnc == null) {
sourceEnc = getPageEncodingForJspSyntax(jspReader, startMark); if (sourceEnc == null) { // Default to "ISO-8859-1" per JSP spec
sourceEnc = "ISO-8859-1";
isDefaultPageEncoding = true;
}
}
}
}
/* * Determines page source encoding for page or tag file in JSP syntax, * by reading (in this order) the value of the 'pageEncoding' page * directive attribute, or the charset value of the 'contentType' page * directive attribute. * * @return The page encoding, or null if not found
*/ private String getPageEncodingForJspSyntax(JspReader jspReader,
Mark startMark) throws JasperException {
/* * Determine page encoding from directive of the form <%@ page %>, * <%@ tag %>, <jsp:directive.page > or <jsp:directive.tag >.
*/ while (true) { if (jspReader.skipUntil("<") == null) { break;
} // If this is a comment, skip until its end if (jspReader.matches("%--")) { if (jspReader.skipUntil("--%>") == null) { // error will be caught in Parser break;
} continue;
} boolean isDirective = jspReader.matches("%@"); if (isDirective) {
jspReader.skipSpaces();
} else {
isDirective = jspReader.matches("jsp:directive.");
} if (!isDirective) { continue;
}
// Want to match tag and page but not taglib if (jspReader.matches("tag") && !jspReader.matches("lib") || jspReader.matches("page")) {
if (encoding == null) {
encoding = saveEncoding;
}
return encoding;
}
/* * Scans the given attributes for the attribute with the given name, * which is either 'pageEncoding' or 'contentType', and returns the * specified page encoding. * * In the case of 'contentType', the page encoding is taken from the * content type's 'charset' component. * * @param attrs The page directive attributes * @param attrName The name of the attribute to search for (either * 'pageEncoding' or 'contentType') * * @return The page encoding, or null
*/ private String getPageEncodingFromDirective(Attributes attrs,
String attrName) {
String value = attrs.getValue(attrName); if (attrName.equals("pageEncoding")) { return value;
}
// attrName = contentType
String contentType = value;
String encoding = null; if (contentType != null) { int loc = contentType.indexOf(CHARSET); if (loc != -1) {
encoding = contentType.substring(loc + CHARSET.length());
}
}
return encoding;
}
/* * Resolve the name of the file and update baseDirStack() to keep track of * the current base directory for each included file. * The 'root' file is always an 'absolute' path, so no need to put an * initial value in the baseDirStack.
*/ private String resolveFileName(String inFileName) {
String fileName = inFileName.replace('\\', '/'); boolean isAbsolute = fileName.startsWith("/");
fileName = isAbsolute ? fileName : baseDirStack.peekFirst() + fileName;
String baseDir = fileName.substring(0, fileName.lastIndexOf('/') + 1);
baseDirStack.addFirst(baseDir); return fileName;
}
/* * Checks to see if the given page contains, as its first element, a <root> * element whose prefix is bound to the JSP namespace, as in: * * <wombat:root xmlns:wombat="http://java.sun.com/JSP/Page" version="1.2"> * ... * </wombat:root> * * @param reader The reader for this page * * @return true if this page contains a root element whose prefix is bound * to the JSP namespace, and false otherwise
*/ privateboolean hasJspRoot(JspReader reader) {
// <prefix>:root must be the first element
Mark start = null; while ((start = reader.skipUntil("<")) != null) { int c = reader.nextChar(); if (c != '!' && c != '?') { break;
}
} if (start == null) { returnfalse;
}
Mark stop = reader.skipUntil(":root"); if (stop == null) { returnfalse;
} // call substring to get rid of leading '<'
String prefix = reader.getText(start, stop).substring(1);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.