You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by mu...@apache.org on 2009/07/31 20:12:51 UTC
svn commit: r799681 [8/24] - in /struts/sandbox/trunk/struts2-jsp-plugin: ./
src/main/java/org/apache/struts/ src/main/java/org/apache/struts2/
src/main/java/org/apache/struts2/compiler/
src/main/java/org/apache/struts2/jasper/ src/main/java/org/apache...
Added: struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/PageDataImpl.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/PageDataImpl.java?rev=799681&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/PageDataImpl.java (added)
+++ struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/PageDataImpl.java Fri Jul 31 18:12:48 2009
@@ -0,0 +1,711 @@
+/*
+ * 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.struts2.jasper.compiler;
+
+import java.io.InputStream;
+import java.io.ByteArrayInputStream;
+import java.io.CharArrayWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.ListIterator;
+import javax.servlet.jsp.tagext.PageData;
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.AttributesImpl;
+import org.apache.struts2.jasper.JasperException;
+
+/**
+ * An implementation of <tt>javax.servlet.jsp.tagext.PageData</tt> which
+ * builds the XML view of a given page.
+ *
+ * The XML view is built in two passes:
+ *
+ * During the first pass, the FirstPassVisitor collects the attributes of the
+ * top-level jsp:root and those of the jsp:root elements of any included
+ * pages, and adds them to the jsp:root element of the XML view.
+ * In addition, any taglib directives are converted into xmlns: attributes and
+ * added to the jsp:root element of the XML view.
+ * This pass ignores any nodes other than JspRoot and TaglibDirective.
+ *
+ * During the second pass, the SecondPassVisitor produces the XML view, using
+ * the combined jsp:root attributes determined in the first pass and any
+ * remaining pages nodes (this pass ignores any JspRoot and TaglibDirective
+ * nodes).
+ *
+ * @author Jan Luehe
+ */
+class PageDataImpl extends PageData implements TagConstants {
+
+ private static final String JSP_VERSION = "2.0";
+ private static final String CDATA_START_SECTION = "<![CDATA[\n";
+ private static final String CDATA_END_SECTION = "]]>\n";
+
+ // string buffer used to build XML view
+ private StringBuffer buf;
+
+ /**
+ * Constructor.
+ *
+ * @param page the page nodes from which to generate the XML view
+ */
+ public PageDataImpl(Node.Nodes page, Compiler compiler)
+ throws JasperException {
+
+ // First pass
+ FirstPassVisitor firstPass = new FirstPassVisitor(page.getRoot(),
+ compiler.getPageInfo());
+ page.visit(firstPass);
+
+ // Second pass
+ buf = new StringBuffer();
+ SecondPassVisitor secondPass
+ = new SecondPassVisitor(page.getRoot(), buf, compiler,
+ firstPass.getJspIdPrefix());
+ page.visit(secondPass);
+ }
+
+ /**
+ * Returns the input stream of the XML view.
+ *
+ * @return the input stream of the XML view
+ */
+ public InputStream getInputStream() {
+ // Turn StringBuffer into InputStream
+ try {
+ return new ByteArrayInputStream(buf.toString().getBytes("UTF-8"));
+ } catch (UnsupportedEncodingException uee) {
+ // should never happen
+ throw new RuntimeException(uee.toString());
+ }
+ }
+
+ /*
+ * First-pass Visitor for JspRoot nodes (representing jsp:root elements)
+ * and TablibDirective nodes, ignoring any other nodes.
+ *
+ * The purpose of this Visitor is to collect the attributes of the
+ * top-level jsp:root and those of the jsp:root elements of any included
+ * pages, and add them to the jsp:root element of the XML view.
+ * In addition, this Visitor converts any taglib directives into xmlns:
+ * attributes and adds them to the jsp:root element of the XML view.
+ */
+ static class FirstPassVisitor
+ extends Node.Visitor implements TagConstants {
+
+ private Node.Root root;
+ private AttributesImpl rootAttrs;
+ private PageInfo pageInfo;
+
+ // Prefix for the 'id' attribute
+ private String jspIdPrefix;
+
+ /*
+ * Constructor
+ */
+ public FirstPassVisitor(Node.Root root, PageInfo pageInfo) {
+ this.root = root;
+ this.pageInfo = pageInfo;
+ this.rootAttrs = new AttributesImpl();
+ this.rootAttrs.addAttribute("", "", "version", "CDATA",
+ JSP_VERSION);
+ this.jspIdPrefix = "jsp";
+ }
+
+ public void visit(Node.Root n) throws JasperException {
+ visitBody(n);
+ if (n == root) {
+ /*
+ * Top-level page.
+ *
+ * Add
+ * xmlns:jsp="http://java.sun.com/JSP/Page"
+ * attribute only if not already present.
+ */
+ if (!JSP_URI.equals(rootAttrs.getValue("xmlns:jsp"))) {
+ rootAttrs.addAttribute("", "", "xmlns:jsp", "CDATA",
+ JSP_URI);
+ }
+
+ if (pageInfo.isJspPrefixHijacked()) {
+ /*
+ * 'jsp' prefix has been hijacked, that is, bound to a
+ * namespace other than the JSP namespace. This means that
+ * when adding an 'id' attribute to each element, we can't
+ * use the 'jsp' prefix. Therefore, create a new prefix
+ * (one that is unique across the translation unit) for use
+ * by the 'id' attribute, and bind it to the JSP namespace
+ */
+ jspIdPrefix += "jsp";
+ while (pageInfo.containsPrefix(jspIdPrefix)) {
+ jspIdPrefix += "jsp";
+ }
+ rootAttrs.addAttribute("", "", "xmlns:" + jspIdPrefix,
+ "CDATA", JSP_URI);
+ }
+
+ root.setAttributes(rootAttrs);
+ }
+ }
+
+ public void visit(Node.JspRoot n) throws JasperException {
+ addAttributes(n.getTaglibAttributes());
+ addAttributes(n.getNonTaglibXmlnsAttributes());
+ addAttributes(n.getAttributes());
+
+ visitBody(n);
+ }
+
+ /*
+ * Converts taglib directive into "xmlns:..." attribute of jsp:root
+ * element.
+ */
+ public void visit(Node.TaglibDirective n) throws JasperException {
+ Attributes attrs = n.getAttributes();
+ if (attrs != null) {
+ String qName = "xmlns:" + attrs.getValue("prefix");
+ /*
+ * According to javadocs of org.xml.sax.helpers.AttributesImpl,
+ * the addAttribute method does not check to see if the
+ * specified attribute is already contained in the list: This
+ * is the application's responsibility!
+ */
+ if (rootAttrs.getIndex(qName) == -1) {
+ String location = attrs.getValue("uri");
+ if (location != null) {
+ if (location.startsWith("/")) {
+ location = URN_JSPTLD + location;
+ }
+ rootAttrs.addAttribute("", "", qName, "CDATA",
+ location);
+ } else {
+ location = attrs.getValue("tagdir");
+ rootAttrs.addAttribute("", "", qName, "CDATA",
+ URN_JSPTAGDIR + location);
+ }
+ }
+ }
+ }
+
+ public String getJspIdPrefix() {
+ return jspIdPrefix;
+ }
+
+ private void addAttributes(Attributes attrs) {
+ if (attrs != null) {
+ int len = attrs.getLength();
+
+ for (int i=0; i<len; i++) {
+ String qName = attrs.getQName(i);
+ if ("version".equals(qName)) {
+ continue;
+ }
+
+ // Bugzilla 35252: http://issues.apache.org/bugzilla/show_bug.cgi?id=35252
+ if(rootAttrs.getIndex(qName) == -1) {
+ rootAttrs.addAttribute(attrs.getURI(i),
+ attrs.getLocalName(i),
+ qName,
+ attrs.getType(i),
+ attrs.getValue(i));
+ }
+ }
+ }
+ }
+ }
+
+
+ /*
+ * Second-pass Visitor responsible for producing XML view and assigning
+ * each element a unique jsp:id attribute.
+ */
+ static class SecondPassVisitor extends Node.Visitor
+ implements TagConstants {
+
+ private Node.Root root;
+ private StringBuffer buf;
+ private Compiler compiler;
+ private String jspIdPrefix;
+ private boolean resetDefaultNS = false;
+
+ // Current value of jsp:id attribute
+ private int jspId;
+
+ /*
+ * Constructor
+ */
+ public SecondPassVisitor(Node.Root root, StringBuffer buf,
+ Compiler compiler, String jspIdPrefix) {
+ this.root = root;
+ this.buf = buf;
+ this.compiler = compiler;
+ this.jspIdPrefix = jspIdPrefix;
+ }
+
+ /*
+ * Visits root node.
+ */
+ public void visit(Node.Root n) throws JasperException {
+ if (n == this.root) {
+ // top-level page
+ appendXmlProlog();
+ appendTag(n);
+ } else {
+ boolean resetDefaultNSSave = resetDefaultNS;
+ if (n.isXmlSyntax()) {
+ resetDefaultNS = true;
+ }
+ visitBody(n);
+ resetDefaultNS = resetDefaultNSSave;
+ }
+ }
+
+ /*
+ * Visits jsp:root element of JSP page in XML syntax.
+ *
+ * Any nested jsp:root elements (from pages included via an
+ * include directive) are ignored.
+ */
+ public void visit(Node.JspRoot n) throws JasperException {
+ visitBody(n);
+ }
+
+ public void visit(Node.PageDirective n) throws JasperException {
+ appendPageDirective(n);
+ }
+
+ public void visit(Node.IncludeDirective n) throws JasperException {
+ // expand in place
+ visitBody(n);
+ }
+
+ public void visit(Node.Comment n) throws JasperException {
+ // Comments are ignored in XML view
+ }
+
+ public void visit(Node.Declaration n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.Expression n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.Scriptlet n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.JspElement n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.ELExpression n) throws JasperException {
+ if (!n.getRoot().isXmlSyntax()) {
+ buf.append("<").append(JSP_TEXT_ACTION);
+ buf.append(" ");
+ buf.append(jspIdPrefix);
+ buf.append(":id=\"");
+ buf.append(jspId++).append("\">");
+ }
+ buf.append("${");
+ buf.append(JspUtil.escapeXml(n.getText()));
+ buf.append("}");
+ if (!n.getRoot().isXmlSyntax()) {
+ buf.append(JSP_TEXT_ACTION_END);
+ }
+ buf.append("\n");
+ }
+
+ public void visit(Node.IncludeAction n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.ForwardAction n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.GetProperty n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.SetProperty n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.ParamAction n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.ParamsAction n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.FallBackAction n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.UseBean n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.PlugIn n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.NamedAttribute n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.JspBody n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.CustomTag n) throws JasperException {
+ boolean resetDefaultNSSave = resetDefaultNS;
+ appendTag(n, resetDefaultNS);
+ resetDefaultNS = resetDefaultNSSave;
+ }
+
+ public void visit(Node.UninterpretedTag n) throws JasperException {
+ boolean resetDefaultNSSave = resetDefaultNS;
+ appendTag(n, resetDefaultNS);
+ resetDefaultNS = resetDefaultNSSave;
+ }
+
+ public void visit(Node.JspText n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.DoBodyAction n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.InvokeAction n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.TagDirective n) throws JasperException {
+ appendTagDirective(n);
+ }
+
+ public void visit(Node.AttributeDirective n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.VariableDirective n) throws JasperException {
+ appendTag(n);
+ }
+
+ public void visit(Node.TemplateText n) throws JasperException {
+ /*
+ * If the template text came from a JSP page written in JSP syntax,
+ * create a jsp:text element for it (JSP 5.3.2).
+ */
+ appendText(n.getText(), !n.getRoot().isXmlSyntax());
+ }
+
+ /*
+ * Appends the given tag, including its body, to the XML view.
+ */
+ private void appendTag(Node n) throws JasperException {
+ appendTag(n, false);
+ }
+
+ /*
+ * Appends the given tag, including its body, to the XML view,
+ * and optionally reset default namespace to "", if none specified.
+ */
+ private void appendTag(Node n, boolean addDefaultNS)
+ throws JasperException {
+
+ Node.Nodes body = n.getBody();
+ String text = n.getText();
+
+ buf.append("<").append(n.getQName());
+ buf.append("\n");
+
+ printAttributes(n, addDefaultNS);
+ buf.append(" ").append(jspIdPrefix).append(":id").append("=\"");
+ buf.append(jspId++).append("\"\n");
+
+ if (ROOT_ACTION.equals(n.getLocalName()) || body != null
+ || text != null) {
+ buf.append(">\n");
+ if (ROOT_ACTION.equals(n.getLocalName())) {
+ if (compiler.getCompilationContext().isTagFile()) {
+ appendTagDirective();
+ } else {
+ appendPageDirective();
+ }
+ }
+ if (body != null) {
+ body.visit(this);
+ } else {
+ appendText(text, false);
+ }
+ buf.append("</" + n.getQName() + ">\n");
+ } else {
+ buf.append("/>\n");
+ }
+ }
+
+ /*
+ * Appends the page directive with the given attributes to the XML
+ * view.
+ *
+ * Since the import attribute of the page directive is the only page
+ * attribute that is allowed to appear multiple times within the same
+ * document, and since XML allows only single-value attributes,
+ * the values of multiple import attributes must be combined into one,
+ * separated by comma.
+ *
+ * If the given page directive contains just 'contentType' and/or
+ * 'pageEncoding' attributes, we ignore it, as we've already appended
+ * a page directive containing just these two attributes.
+ */
+ private void appendPageDirective(Node.PageDirective n) {
+ boolean append = false;
+ Attributes attrs = n.getAttributes();
+ int len = (attrs == null) ? 0 : attrs.getLength();
+ for (int i=0; i<len; i++) {
+ String attrName = attrs.getQName(i);
+ if (!"pageEncoding".equals(attrName)
+ && !"contentType".equals(attrName)) {
+ append = true;
+ break;
+ }
+ }
+ if (!append) {
+ return;
+ }
+
+ buf.append("<").append(n.getQName());
+ buf.append("\n");
+
+ // append jsp:id
+ buf.append(" ").append(jspIdPrefix).append(":id").append("=\"");
+ buf.append(jspId++).append("\"\n");
+
+ // append remaining attributes
+ for (int i=0; i<len; i++) {
+ String attrName = attrs.getQName(i);
+ if ("import".equals(attrName) || "contentType".equals(attrName)
+ || "pageEncoding".equals(attrName)) {
+ /*
+ * Page directive's 'import' attribute is considered
+ * further down, and its 'pageEncoding' and 'contentType'
+ * attributes are ignored, since we've already appended
+ * a new page directive containing just these two
+ * attributes
+ */
+ continue;
+ }
+ String value = attrs.getValue(i);
+ buf.append(" ").append(attrName).append("=\"");
+ buf.append(JspUtil.getExprInXml(value)).append("\"\n");
+ }
+ if (n.getImports().size() > 0) {
+ // Concatenate names of imported classes/packages
+ boolean first = true;
+ ListIterator iter = n.getImports().listIterator();
+ while (iter.hasNext()) {
+ if (first) {
+ first = false;
+ buf.append(" import=\"");
+ } else {
+ buf.append(",");
+ }
+ buf.append(JspUtil.getExprInXml((String) iter.next()));
+ }
+ buf.append("\"\n");
+ }
+ buf.append("/>\n");
+ }
+
+ /*
+ * Appends a page directive with 'pageEncoding' and 'contentType'
+ * attributes.
+ *
+ * The value of the 'pageEncoding' attribute is hard-coded
+ * to UTF-8, whereas the value of the 'contentType' attribute, which
+ * is identical to what the container will pass to
+ * ServletResponse.setContentType(), is derived from the pageInfo.
+ */
+ private void appendPageDirective() {
+ buf.append("<").append(JSP_PAGE_DIRECTIVE_ACTION);
+ buf.append("\n");
+
+ // append jsp:id
+ buf.append(" ").append(jspIdPrefix).append(":id").append("=\"");
+ buf.append(jspId++).append("\"\n");
+ buf.append(" ").append("pageEncoding").append("=\"UTF-8\"\n");
+ buf.append(" ").append("contentType").append("=\"");
+ buf.append(compiler.getPageInfo().getContentType()).append("\"\n");
+ buf.append("/>\n");
+ }
+
+ /*
+ * Appends the tag directive with the given attributes to the XML
+ * view.
+ *
+ * If the given tag directive contains just a 'pageEncoding'
+ * attributes, we ignore it, as we've already appended
+ * a tag directive containing just this attributes.
+ */
+ private void appendTagDirective(Node.TagDirective n)
+ throws JasperException {
+
+ boolean append = false;
+ Attributes attrs = n.getAttributes();
+ int len = (attrs == null) ? 0 : attrs.getLength();
+ for (int i=0; i<len; i++) {
+ String attrName = attrs.getQName(i);
+ if (!"pageEncoding".equals(attrName)) {
+ append = true;
+ break;
+ }
+ }
+ if (!append) {
+ return;
+ }
+
+ appendTag(n);
+ }
+
+ /*
+ * Appends a tag directive containing a single 'pageEncoding'
+ * attribute whose value is hard-coded to UTF-8.
+ */
+ private void appendTagDirective() {
+ buf.append("<").append(JSP_TAG_DIRECTIVE_ACTION);
+ buf.append("\n");
+
+ // append jsp:id
+ buf.append(" ").append(jspIdPrefix).append(":id").append("=\"");
+ buf.append(jspId++).append("\"\n");
+ buf.append(" ").append("pageEncoding").append("=\"UTF-8\"\n");
+ buf.append("/>\n");
+ }
+
+ private void appendText(String text, boolean createJspTextElement) {
+ if (createJspTextElement) {
+ buf.append("<").append(JSP_TEXT_ACTION);
+ buf.append("\n");
+
+ // append jsp:id
+ buf.append(" ").append(jspIdPrefix).append(":id").append("=\"");
+ buf.append(jspId++).append("\"\n");
+ buf.append(">\n");
+
+ appendCDATA(text);
+ buf.append(JSP_TEXT_ACTION_END);
+ buf.append("\n");
+ } else {
+ appendCDATA(text);
+ }
+ }
+
+ /*
+ * Appends the given text as a CDATA section to the XML view, unless
+ * the text has already been marked as CDATA.
+ */
+ private void appendCDATA(String text) {
+ buf.append(CDATA_START_SECTION);
+ buf.append(escapeCDATA(text));
+ buf.append(CDATA_END_SECTION);
+ }
+
+ /*
+ * Escapes any occurrences of "]]>" (by replacing them with "]]>")
+ * within the given text, so it can be included in a CDATA section.
+ */
+ private String escapeCDATA(String text) {
+ if( text==null ) return "";
+ int len = text.length();
+ CharArrayWriter result = new CharArrayWriter(len);
+ for (int i=0; i<len; i++) {
+ if (((i+2) < len)
+ && (text.charAt(i) == ']')
+ && (text.charAt(i+1) == ']')
+ && (text.charAt(i+2) == '>')) {
+ // match found
+ result.write(']');
+ result.write(']');
+ result.write('&');
+ result.write('g');
+ result.write('t');
+ result.write(';');
+ i += 2;
+ } else {
+ result.write(text.charAt(i));
+ }
+ }
+ return result.toString();
+ }
+
+ /*
+ * Appends the attributes of the given Node to the XML view.
+ */
+ private void printAttributes(Node n, boolean addDefaultNS) {
+
+ /*
+ * Append "xmlns" attributes that represent tag libraries
+ */
+ Attributes attrs = n.getTaglibAttributes();
+ int len = (attrs == null) ? 0 : attrs.getLength();
+ for (int i=0; i<len; i++) {
+ String name = attrs.getQName(i);
+ String value = attrs.getValue(i);
+ buf.append(" ").append(name).append("=\"").append(value).append("\"\n");
+ }
+
+ /*
+ * Append "xmlns" attributes that do not represent tag libraries
+ */
+ attrs = n.getNonTaglibXmlnsAttributes();
+ len = (attrs == null) ? 0 : attrs.getLength();
+ boolean defaultNSSeen = false;
+ for (int i=0; i<len; i++) {
+ String name = attrs.getQName(i);
+ String value = attrs.getValue(i);
+ buf.append(" ").append(name).append("=\"").append(value).append("\"\n");
+ defaultNSSeen |= "xmlns".equals(name);
+ }
+ if (addDefaultNS && !defaultNSSeen) {
+ buf.append(" xmlns=\"\"\n");
+ }
+ resetDefaultNS = false;
+
+ /*
+ * Append all other attributes
+ */
+ attrs = n.getAttributes();
+ len = (attrs == null) ? 0 : attrs.getLength();
+ for (int i=0; i<len; i++) {
+ String name = attrs.getQName(i);
+ String value = attrs.getValue(i);
+ buf.append(" ").append(name).append("=\"");
+ buf.append(JspUtil.getExprInXml(value)).append("\"\n");
+ }
+ }
+
+ /*
+ * Appends XML prolog with encoding declaration.
+ */
+ private void appendXmlProlog() {
+ buf.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
+ }
+ }
+}
+
Added: struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/PageInfo.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/PageInfo.java?rev=799681&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/PageInfo.java (added)
+++ struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/PageInfo.java Fri Jul 31 18:12:48 2009
@@ -0,0 +1,629 @@
+/*
+ * 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.struts2.jasper.compiler;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Vector;
+
+import org.apache.struts2.jasper.Constants;
+import org.apache.struts2.jasper.JasperException;
+import javax.servlet.jsp.tagext.TagLibraryInfo;
+
+/**
+ * A repository for various info about the translation unit under compilation.
+ *
+ * @author Kin-man Chung
+ */
+
+class PageInfo {
+
+ private Vector imports;
+ private Vector dependants;
+
+ private BeanRepository beanRepository;
+ private HashMap taglibsMap;
+ private HashMap jspPrefixMapper;
+ private HashMap xmlPrefixMapper;
+ private HashMap nonCustomTagPrefixMap;
+ private String jspFile;
+ private String defaultLanguage = "java";
+ private String language;
+ private String defaultExtends = Constants.JSP_SERVLET_BASE;
+ private String xtends;
+ private String contentType = null;
+ private String session;
+ private boolean isSession = true;
+ private String bufferValue;
+ private int buffer = 8*1024; // XXX confirm
+ private String autoFlush;
+ private boolean isAutoFlush = true;
+ private String isThreadSafeValue;
+ private boolean isThreadSafe = true;
+ private String isErrorPageValue;
+ private boolean isErrorPage = false;
+ private String errorPage = null;
+ private String info;
+
+ private boolean scriptless = false;
+ private boolean scriptingInvalid = false;
+ private String isELIgnoredValue;
+ private boolean isELIgnored = false;
+ private String omitXmlDecl = null;
+ private String doctypeName = null;
+ private String doctypePublic = null;
+ private String doctypeSystem = null;
+
+ private boolean isJspPrefixHijacked;
+
+ // Set of all element and attribute prefixes used in this translation unit
+ private HashSet prefixes;
+
+ private boolean hasJspRoot = false;
+ private Vector includePrelude;
+ private Vector includeCoda;
+ private Vector pluginDcls; // Id's for tagplugin declarations
+
+
+ PageInfo(BeanRepository beanRepository, String jspFile) {
+
+ this.jspFile = jspFile;
+ this.beanRepository = beanRepository;
+ this.taglibsMap = new HashMap();
+ this.jspPrefixMapper = new HashMap();
+ this.xmlPrefixMapper = new HashMap();
+ this.nonCustomTagPrefixMap = new HashMap();
+ this.imports = new Vector();
+ this.dependants = new Vector();
+ this.includePrelude = new Vector();
+ this.includeCoda = new Vector();
+ this.pluginDcls = new Vector();
+ this.prefixes = new HashSet();
+
+ // Enter standard imports
+ for(int i = 0; i < Constants.STANDARD_IMPORTS.length; i++)
+ imports.add(Constants.STANDARD_IMPORTS[i]);
+ }
+
+ /**
+ * Check if the plugin ID has been previously declared. Make a not
+ * that this Id is now declared.
+ * @return true if Id has been declared.
+ */
+ public boolean isPluginDeclared(String id) {
+ if (pluginDcls.contains(id))
+ return true;
+ pluginDcls.add(id);
+ return false;
+ }
+
+ public void addImports(List imports) {
+ this.imports.addAll(imports);
+ }
+
+ public void addImport(String imp) {
+ this.imports.add(imp);
+ }
+
+ public List getImports() {
+ return imports;
+ }
+
+ public String getJspFile() {
+ return jspFile;
+ }
+
+ public void addDependant(String d) {
+ if (!dependants.contains(d) && !jspFile.equals(d))
+ dependants.add(d);
+ }
+
+ public List getDependants() {
+ return dependants;
+ }
+
+ public BeanRepository getBeanRepository() {
+ return beanRepository;
+ }
+
+ public void setScriptless(boolean s) {
+ scriptless = s;
+ }
+
+ public boolean isScriptless() {
+ return scriptless;
+ }
+
+ public void setScriptingInvalid(boolean s) {
+ scriptingInvalid = s;
+ }
+
+ public boolean isScriptingInvalid() {
+ return scriptingInvalid;
+ }
+
+ public List getIncludePrelude() {
+ return includePrelude;
+ }
+
+ public void setIncludePrelude(Vector prelude) {
+ includePrelude = prelude;
+ }
+
+ public List getIncludeCoda() {
+ return includeCoda;
+ }
+
+ public void setIncludeCoda(Vector coda) {
+ includeCoda = coda;
+ }
+
+ public void setHasJspRoot(boolean s) {
+ hasJspRoot = s;
+ }
+
+ public boolean hasJspRoot() {
+ return hasJspRoot;
+ }
+
+ public String getOmitXmlDecl() {
+ return omitXmlDecl;
+ }
+
+ public void setOmitXmlDecl(String omit) {
+ omitXmlDecl = omit;
+ }
+
+ public String getDoctypeName() {
+ return doctypeName;
+ }
+
+ public void setDoctypeName(String doctypeName) {
+ this.doctypeName = doctypeName;
+ }
+
+ public String getDoctypeSystem() {
+ return doctypeSystem;
+ }
+
+ public void setDoctypeSystem(String doctypeSystem) {
+ this.doctypeSystem = doctypeSystem;
+ }
+
+ public String getDoctypePublic() {
+ return doctypePublic;
+ }
+
+ public void setDoctypePublic(String doctypePublic) {
+ this.doctypePublic = doctypePublic;
+ }
+
+ /* Tag library and XML namespace management methods */
+
+ public void setIsJspPrefixHijacked(boolean isHijacked) {
+ isJspPrefixHijacked = isHijacked;
+ }
+
+ public boolean isJspPrefixHijacked() {
+ return isJspPrefixHijacked;
+ }
+
+ /*
+ * Adds the given prefix to the set of prefixes of this translation unit.
+ *
+ * @param prefix The prefix to add
+ */
+ public void addPrefix(String prefix) {
+ prefixes.add(prefix);
+ }
+
+ /*
+ * Checks to see if this translation unit contains the given prefix.
+ *
+ * @param prefix The prefix to check
+ *
+ * @return true if this translation unit contains the given prefix, false
+ * otherwise
+ */
+ public boolean containsPrefix(String prefix) {
+ return prefixes.contains(prefix);
+ }
+
+ /*
+ * Maps the given URI to the given tag library.
+ *
+ * @param uri The URI to map
+ * @param info The tag library to be associated with the given URI
+ */
+ public void addTaglib(String uri, TagLibraryInfo info) {
+ taglibsMap.put(uri, info);
+ }
+
+ /*
+ * Gets the tag library corresponding to the given URI.
+ *
+ * @return Tag library corresponding to the given URI
+ */
+ public TagLibraryInfo getTaglib(String uri) {
+ return (TagLibraryInfo) taglibsMap.get(uri);
+ }
+
+ /*
+ * Gets the collection of tag libraries that are associated with a URI
+ *
+ * @return Collection of tag libraries that are associated with a URI
+ */
+ public Collection getTaglibs() {
+ return taglibsMap.values();
+ }
+
+ /*
+ * Checks to see if the given URI is mapped to a tag library.
+ *
+ * @param uri The URI to map
+ *
+ * @return true if the given URI is mapped to a tag library, false
+ * otherwise
+ */
+ public boolean hasTaglib(String uri) {
+ return taglibsMap.containsKey(uri);
+ }
+
+ /*
+ * Maps the given prefix to the given URI.
+ *
+ * @param prefix The prefix to map
+ * @param uri The URI to be associated with the given prefix
+ */
+ public void addPrefixMapping(String prefix, String uri) {
+ jspPrefixMapper.put(prefix, uri);
+ }
+
+ /*
+ * Pushes the given URI onto the stack of URIs to which the given prefix
+ * is mapped.
+ *
+ * @param prefix The prefix whose stack of URIs is to be pushed
+ * @param uri The URI to be pushed onto the stack
+ */
+ public void pushPrefixMapping(String prefix, String uri) {
+ LinkedList stack = (LinkedList) xmlPrefixMapper.get(prefix);
+ if (stack == null) {
+ stack = new LinkedList();
+ xmlPrefixMapper.put(prefix, stack);
+ }
+ stack.addFirst(uri);
+ }
+
+ /*
+ * Removes the URI at the top of the stack of URIs to which the given
+ * prefix is mapped.
+ *
+ * @param prefix The prefix whose stack of URIs is to be popped
+ */
+ public void popPrefixMapping(String prefix) {
+ LinkedList stack = (LinkedList) xmlPrefixMapper.get(prefix);
+ if (stack == null || stack.size() == 0) {
+ // XXX throw new Exception("XXX");
+ }
+ stack.removeFirst();
+ }
+
+ /*
+ * Returns the URI to which the given prefix maps.
+ *
+ * @param prefix The prefix whose URI is sought
+ *
+ * @return The URI to which the given prefix maps
+ */
+ public String getURI(String prefix) {
+
+ String uri = null;
+
+ LinkedList stack = (LinkedList) xmlPrefixMapper.get(prefix);
+ if (stack == null || stack.size() == 0) {
+ uri = (String) jspPrefixMapper.get(prefix);
+ } else {
+ uri = (String) stack.getFirst();
+ }
+
+ return uri;
+ }
+
+
+ /* Page/Tag directive attributes */
+
+ /*
+ * language
+ */
+ public void setLanguage(String value, Node n, ErrorDispatcher err,
+ boolean pagedir)
+ throws JasperException {
+
+ if (!"java".equalsIgnoreCase(value)) {
+ if (pagedir)
+ err.jspError(n, "jsp.error.page.language.nonjava");
+ else
+ err.jspError(n, "jsp.error.tag.language.nonjava");
+ }
+
+ language = value;
+ }
+
+ public String getLanguage(boolean useDefault) {
+ return (language == null && useDefault ? defaultLanguage : language);
+ }
+
+ public String getLanguage() {
+ return getLanguage(true);
+ }
+
+
+ /*
+ * extends
+ */
+ public void setExtends(String value, Node.PageDirective n) {
+
+ xtends = value;
+
+ /*
+ * If page superclass is top level class (i.e. not in a package)
+ * explicitly import it. If this is not done, the compiler will assume
+ * the extended class is in the same pkg as the generated servlet.
+ */
+ if (value.indexOf('.') < 0)
+ n.addImport(value);
+ }
+
+ /**
+ * Gets the value of the 'extends' page directive attribute.
+ *
+ * @param useDefault TRUE if the default
+ * (org.apache.jasper.runtime.HttpJspBase) should be returned if this
+ * attribute has not been set, FALSE otherwise
+ *
+ * @return The value of the 'extends' page directive attribute, or the
+ * default (org.apache.jasper.runtime.HttpJspBase) if this attribute has
+ * not been set and useDefault is TRUE
+ */
+ public String getExtends(boolean useDefault) {
+ return (xtends == null && useDefault ? defaultExtends : xtends);
+ }
+
+ /**
+ * Gets the value of the 'extends' page directive attribute.
+ *
+ * @return The value of the 'extends' page directive attribute, or the
+ * default (org.apache.jasper.runtime.HttpJspBase) if this attribute has
+ * not been set
+ */
+ public String getExtends() {
+ return getExtends(true);
+ }
+
+
+ /*
+ * contentType
+ */
+ public void setContentType(String value) {
+ contentType = value;
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+
+ /*
+ * buffer
+ */
+ public void setBufferValue(String value, Node n, ErrorDispatcher err)
+ throws JasperException {
+
+ if ("none".equalsIgnoreCase(value))
+ buffer = 0;
+ else {
+ if (value == null || !value.endsWith("kb"))
+ err.jspError(n, "jsp.error.page.invalid.buffer");
+ try {
+ Integer k = new Integer(value.substring(0, value.length()-2));
+ buffer = k.intValue() * 1024;
+ } catch (NumberFormatException e) {
+ err.jspError(n, "jsp.error.page.invalid.buffer");
+ }
+ }
+
+ bufferValue = value;
+ }
+
+ public String getBufferValue() {
+ return bufferValue;
+ }
+
+ public int getBuffer() {
+ return buffer;
+ }
+
+
+ /*
+ * session
+ */
+ public void setSession(String value, Node n, ErrorDispatcher err)
+ throws JasperException {
+
+ if ("true".equalsIgnoreCase(value))
+ isSession = true;
+ else if ("false".equalsIgnoreCase(value))
+ isSession = false;
+ else
+ err.jspError(n, "jsp.error.page.invalid.session");
+
+ session = value;
+ }
+
+ public String getSession() {
+ return session;
+ }
+
+ public boolean isSession() {
+ return isSession;
+ }
+
+
+ /*
+ * autoFlush
+ */
+ public void setAutoFlush(String value, Node n, ErrorDispatcher err)
+ throws JasperException {
+
+ if ("true".equalsIgnoreCase(value))
+ isAutoFlush = true;
+ else if ("false".equalsIgnoreCase(value))
+ isAutoFlush = false;
+ else
+ err.jspError(n, "jsp.error.autoFlush.invalid");
+
+ autoFlush = value;
+ }
+
+ public String getAutoFlush() {
+ return autoFlush;
+ }
+
+ public boolean isAutoFlush() {
+ return isAutoFlush;
+ }
+
+
+ /*
+ * isThreadSafe
+ */
+ public void setIsThreadSafe(String value, Node n, ErrorDispatcher err)
+ throws JasperException {
+
+ if ("true".equalsIgnoreCase(value))
+ isThreadSafe = true;
+ else if ("false".equalsIgnoreCase(value))
+ isThreadSafe = false;
+ else
+ err.jspError(n, "jsp.error.page.invalid.isthreadsafe");
+
+ isThreadSafeValue = value;
+ }
+
+ public String getIsThreadSafe() {
+ return isThreadSafeValue;
+ }
+
+ public boolean isThreadSafe() {
+ return isThreadSafe;
+ }
+
+
+ /*
+ * info
+ */
+ public void setInfo(String value) {
+ info = value;
+ }
+
+ public String getInfo() {
+ return info;
+ }
+
+
+ /*
+ * errorPage
+ */
+ public void setErrorPage(String value) {
+ errorPage = value;
+ }
+
+ public String getErrorPage() {
+ return errorPage;
+ }
+
+
+ /*
+ * isErrorPage
+ */
+ public void setIsErrorPage(String value, Node n, ErrorDispatcher err)
+ throws JasperException {
+
+ if ("true".equalsIgnoreCase(value))
+ isErrorPage = true;
+ else if ("false".equalsIgnoreCase(value))
+ isErrorPage = false;
+ else
+ err.jspError(n, "jsp.error.page.invalid.iserrorpage");
+
+ isErrorPageValue = value;
+ }
+
+ public String getIsErrorPage() {
+ return isErrorPageValue;
+ }
+
+ public boolean isErrorPage() {
+ return isErrorPage;
+ }
+
+
+ /*
+ * isELIgnored
+ */
+ public void setIsELIgnored(String value, Node n, ErrorDispatcher err,
+ boolean pagedir)
+ throws JasperException {
+
+ if ("true".equalsIgnoreCase(value))
+ isELIgnored = true;
+ else if ("false".equalsIgnoreCase(value))
+ isELIgnored = false;
+ else {
+ if (pagedir)
+ err.jspError(n, "jsp.error.page.invalid.iselignored");
+ else
+ err.jspError(n, "jsp.error.tag.invalid.iselignored");
+ }
+
+ isELIgnoredValue = value;
+ }
+
+ public void setELIgnored(boolean s) {
+ isELIgnored = s;
+ }
+
+ public String getIsELIgnored() {
+ return isELIgnoredValue;
+ }
+
+ public boolean isELIgnored() {
+ return isELIgnored;
+ }
+
+ public void putNonCustomTagPrefix(String prefix, Mark where) {
+ nonCustomTagPrefixMap.put(prefix, where);
+ }
+
+ public Mark getNonCustomTagPrefix(String prefix) {
+ return (Mark) nonCustomTagPrefixMap.get(prefix);
+ }
+}