749 lines
26 KiB
Java
749 lines
26 KiB
Java
/*
|
|
* 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;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.CharArrayWriter;
|
|
import java.io.InputStream;
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
import javax.servlet.jsp.tagext.PageData;
|
|
|
|
import org.apache.jasper.JasperException;
|
|
import org.apache.tomcat.util.security.Escape;
|
|
import org.xml.sax.Attributes;
|
|
import org.xml.sax.helpers.AttributesImpl;
|
|
|
|
/**
|
|
* An implementation of <code>javax.servlet.jsp.tagext.PageData</code> 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 final StringBuilder buf;
|
|
|
|
/**
|
|
* @param page the page nodes from which to generate the XML view
|
|
* @param compiler The compiler for this page
|
|
*
|
|
* @throws JasperException If an error occurs
|
|
*/
|
|
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 StringBuilder();
|
|
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
|
|
*/
|
|
@Override
|
|
public InputStream getInputStream() {
|
|
return new ByteArrayInputStream(
|
|
buf.toString().getBytes(StandardCharsets.UTF_8));
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
private static class FirstPassVisitor
|
|
extends Node.Visitor implements TagConstants {
|
|
|
|
private final Node.Root root;
|
|
private final AttributesImpl rootAttrs;
|
|
private final 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";
|
|
}
|
|
|
|
@Override
|
|
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);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
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.
|
|
*/
|
|
@Override
|
|
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: https://bz.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.
|
|
*/
|
|
private static class SecondPassVisitor extends Node.Visitor
|
|
implements TagConstants {
|
|
|
|
private final Node.Root root;
|
|
private final StringBuilder buf;
|
|
private final Compiler compiler;
|
|
private final String jspIdPrefix;
|
|
private boolean resetDefaultNS = false;
|
|
|
|
// Current value of jsp:id attribute
|
|
private int jspId;
|
|
|
|
/*
|
|
* Constructor
|
|
*/
|
|
public SecondPassVisitor(Node.Root root, StringBuilder buf,
|
|
Compiler compiler, String jspIdPrefix) {
|
|
this.root = root;
|
|
this.buf = buf;
|
|
this.compiler = compiler;
|
|
this.jspIdPrefix = jspIdPrefix;
|
|
}
|
|
|
|
/*
|
|
* Visits root node.
|
|
*/
|
|
@Override
|
|
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.
|
|
*/
|
|
@Override
|
|
public void visit(Node.JspRoot n) throws JasperException {
|
|
visitBody(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.PageDirective n) throws JasperException {
|
|
appendPageDirective(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.IncludeDirective n) throws JasperException {
|
|
// expand in place
|
|
visitBody(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.Comment n) throws JasperException {
|
|
// Comments are ignored in XML view
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.Declaration n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.Expression n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.Scriptlet n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.JspElement n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
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(Escape.xml(n.getText()));
|
|
buf.append("}");
|
|
if (!n.getRoot().isXmlSyntax()) {
|
|
buf.append(JSP_TEXT_ACTION_END);
|
|
}
|
|
buf.append("\n");
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.IncludeAction n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.ForwardAction n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.GetProperty n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.SetProperty n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.ParamAction n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.ParamsAction n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.FallBackAction n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.UseBean n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.PlugIn n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.NamedAttribute n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.JspBody n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.CustomTag n) throws JasperException {
|
|
boolean resetDefaultNSSave = resetDefaultNS;
|
|
appendTag(n, resetDefaultNS);
|
|
resetDefaultNS = resetDefaultNSSave;
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.UninterpretedTag n) throws JasperException {
|
|
boolean resetDefaultNSSave = resetDefaultNS;
|
|
appendTag(n, resetDefaultNS);
|
|
resetDefaultNS = resetDefaultNSSave;
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.JspText n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.DoBodyAction n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.InvokeAction n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.TagDirective n) throws JasperException {
|
|
appendTagDirective(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.AttributeDirective n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
public void visit(Node.VariableDirective n) throws JasperException {
|
|
appendTag(n);
|
|
}
|
|
|
|
@Override
|
|
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++) {
|
|
@SuppressWarnings("null") // If attrs==null, len == 0
|
|
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++) {
|
|
@SuppressWarnings("null") // If attrs==null, len == 0
|
|
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;
|
|
for (String i : n.getImports()) {
|
|
if (first) {
|
|
first = false;
|
|
buf.append(" import=\"");
|
|
} else {
|
|
buf.append(",");
|
|
}
|
|
buf.append(JspUtil.getExprInXml(i));
|
|
}
|
|
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++) {
|
|
@SuppressWarnings("null") // If attrs==null, len == 0
|
|
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++) {
|
|
@SuppressWarnings("null") // If attrs==null, len == 0
|
|
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++) {
|
|
@SuppressWarnings("null") // If attrs==null, len == 0
|
|
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++) {
|
|
@SuppressWarnings("null") // If attrs==null, len == 0
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
|