balld 00/07/23 21:13:27
Modified: . changes.xml
src/org/apache/cocoon/processor/xinclude
XIncludeProcessor.java
Log:
brought XInclude processor into conformance with 2000-07-17 WD
Revision Changes Path
1.96 +4 -1 xml-cocoon/changes.xml
Index: changes.xml
===================================================================
RCS file: /home/cvs/xml-cocoon/changes.xml,v
retrieving revision 1.95
retrieving revision 1.96
diff -u -r1.95 -r1.96
--- changes.xml 2000/07/23 20:23:40 1.95
+++ changes.xml 2000/07/24 04:13:23 1.96
@@ -4,7 +4,7 @@
<!--
History of Cocoon changes
- $Id: changes.xml,v 1.95 2000/07/23 20:23:40 stefano Exp $
+ $Id: changes.xml,v 1.96 2000/07/24 04:13:23 balld Exp $
-->
<changes title="History of Changes">
@@ -16,6 +16,9 @@
</devs>
<release version="@version@" date="@date@">
+ <action dev="DB" type="update">
+ Brought XInclude processor into conformance (mostly) with the 2000-07-17
version of the working draft.
+ </action>
<action dev="SM" type="fix">
Fixed problem with unresolved SystemID URIs that cause problems with
latest Xerces.
</action>
1.12 +130 -100
xml-cocoon/src/org/apache/cocoon/processor/xinclude/XIncludeProcessor.java
Index: XIncludeProcessor.java
===================================================================
RCS file:
/home/cvs/xml-cocoon/src/org/apache/cocoon/processor/xinclude/XIncludeProcessor.java,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- XIncludeProcessor.java 2000/07/20 00:17:41 1.11
+++ XIncludeProcessor.java 2000/07/24 04:13:26 1.12
@@ -60,6 +60,8 @@
import java.util.Stack;
import java.util.Dictionary;
import java.util.Hashtable;
+import java.util.Vector;
+import java.util.Enumeration;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpUtils;
import org.xml.sax.InputSource;
@@ -83,23 +85,35 @@
import org.apache.cocoon.Utils;
/**
- * First version of a DOM XInclude parser for cocoon. This has been back
ported
- * from my XInclude filter for cocoon2.
+ * Second version of a DOM2 XInclude parser for cocoon. This revision
+ * should support the bulk of the 2000-07-17 version of the XInclude working
+ * draft. Notably excluded is inclusion loop checking
+ * (<a href="http://www.w3.org/TR/xinclude#IDw2Bq1">section 3.2.1</a>). Note
+ * also that included namespaces may not be handled properly
+ * (<a href="http://www.w3.org/TR/xinclude#ID0mBq1">section 3.2.2</a>) -
+ * I'd love feedback on this. Namespaces are simple - but the DOM2 (and SAX2)
+ * methods for interacting with them aren't. Finally, note that the order of
+ * include element processing as noted in
+ * <a href="http://www.w3.org/TR/xinclude#IDwgAq1">section 3.1</a> is not
+ * correct - internal xpointer links are not necessarily resolved against
+ * the original source document. I haven't figured out a good way to resolve
+ * that without cloning the entire source document first, which would be
+ * a terrible wasteful of memory.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Donald Ball</a>
- * @version CVS $Revision: 1.11 $ $Date: 2000/07/20 00:17:41 $ $Author:
stefano $
+ * @version CVS $Revision: 1.12 $ $Date: 2000/07/24 04:13:26 $ $Author:
balld $
*/
public class XIncludeProcessor extends AbstractActor implements Processor,
Status {
- protected boolean debug = true;
+ protected boolean debug = false;
public static final String XMLBASE_NAMESPACE_URI =
"http://www.w3.org/XML/1998/namespace";
public static final String XMLBASE_ATTRIBUTE = "base";
public static final String XINCLUDE_NAMESPACE_URI =
"http://www.w3.org/1999/XML/xinclude";
- public static final String XINCLUDE_INCLUDE_ELEMENT = "include";
- public static final String XINCLUDE_INCLUDE_ELEMENT_HREF_ATTRIBUTE =
"href";
- public static final String XINCLUDE_INCLUDE_ELEMENT_PARSE_ATTRIBUTE =
"parse";
+ public static final String XINCLUDE_HREF_ATTRIBUTE = "href";
+ public static final String XINCLUDE_PARSE_ATTRIBUTE = "parse";
+ public static final int BUFFER_SIZE = 1024;
protected Parser parser;
protected Logger logger;
@@ -130,7 +144,7 @@
public boolean hasChanged(Object object) {
/** I would have thought that the monitor would return false if
the
key has no resources being monitored, but it doesn't. I
think
- that might should change, but we'll work around it for
now. **/
+ that might should change, but we'll work around it for now.
**/
Object key = Utils.encode((HttpServletRequest)object);
if (monitored_table.containsKey(key)) {
return monitor.hasChanged(key);
@@ -138,7 +152,36 @@
return false;
}
+class XIncludeElement {
+ Element element;
+ Element parent;
+ String href;
+ String parse;
+ Object base;
+ String suffix;
+ String xpath = null;
+
+ XIncludeElement(Element element, Element parent, String href, String
parse, Object base) {
+ this.element = element;
+ this.parent = parent;
+ this.parse = parse;
+ this.base = base;
+ int index = href.indexOf('#');
+ if (index < 0) {
+ suffix = "";
+ this.href = href;
+ } else {
+ suffix = href.substring(index+1);
+ this.href = href.substring(0,index);
+ if (suffix.startsWith("xpointer(") &&
suffix.endsWith(")")) {
+ xpath = suffix.substring(9,suffix.length()-1);
+ }
+ }
+ }
+
+}
+
class XIncludeProcessorWorker {
boolean debug;
@@ -147,15 +190,11 @@
Document document;
- File base_file = null;
-
- /** The current XMLBase URI. We start with an empty "dummy" URL. **/
- URL current_xmlbase_uri = null;
+ Object current_xmlbase;
- /** This is a stack of xml:base attributes which belong to our
ancestors **/
Stack xmlbase_stack = new Stack();
- Hashtable namespace_table = new Hashtable();
+ Vector xinclude_elements = new Vector();
Object monitor_key;
@@ -168,131 +207,123 @@
request = (HttpServletRequest)parameters.get("request");
monitor_key = Utils.encode(request);
String basename = Utils.getBasename(request,context);
- base_file = new File((new File(basename)).getParent());
+ current_xmlbase = new File((new File(basename)).getParent());
}
void process() throws Exception {
Element element = document.getDocumentElement();
- /** FIXME - why doesn't Xerces let us use
node.getNamespaceURI()??? **/
- NamedNodeMap attributes = element.getAttributes();
- int length = attributes.getLength();
- for (int i=0; i<length; i++) {
- Attr attr = (Attr)attributes.item(i);
- String name = attr.getName();
- if (name.length() >= 6 &&
name.substring(0,6).equals("xmlns:")) {
- String prefix = name.substring(6);
- String uri = attr.getValue();
- namespace_table.put(prefix,uri);
- }
- }
- process(element,null);
- }
-
- void process(Element element, Element parent) throws Exception {
- String name = element.getLocalName();
- String uri = element.getNamespaceURI();
- String prefix = element.getPrefix();
- String value;
- boolean xmlbase_attribute = false;
- if ((value =
element.getAttributeNS(processor.XMLBASE_NAMESPACE_URI,processor.XMLBASE_ATTRIBUTE))
!= null) {
- startXMLBaseAttribute(value);
- xmlbase_attribute = true;
- }
- if (uri != null && uri.equals(processor.XINCLUDE_NAMESPACE_URI)
&& name.equals(processor.XINCLUDE_INCLUDE_ELEMENT)) {
- String href =
element.getAttribute(processor.XINCLUDE_INCLUDE_ELEMENT_HREF_ATTRIBUTE);
- String parse =
element.getAttribute(processor.XINCLUDE_INCLUDE_ELEMENT_PARSE_ATTRIBUTE);
- Object object = processXIncludeElement(element, href,
parse);
+ scan(element,null);
+ Enumeration e = xinclude_elements.elements();
+ while (e.hasMoreElements()) {
+ XIncludeElement xinclude =
(XIncludeElement)e.nextElement();
+ Object object = processXIncludeElement(xinclude);
if (object instanceof Node) {
Node node = (Node)object;
- parent.replaceChild(node,element);
+
xinclude.parent.replaceChild(node,xinclude.element);
if (node.getNodeType() == Node.ELEMENT_NODE) {
- element = (Element)node;
- } else {
- return;
+ xmlbase_stack.push(xinclude.base);
+ scan((Element)node,xinclude.parent);
+ xmlbase_stack.pop();
}
} else if (object instanceof Node[]) {
Node ary[] = (Node[])object;
for (int i=0; i<ary.length; i++) {
- parent.insertBefore(ary[i],element);
+
xinclude.parent.insertBefore(ary[i],xinclude.element);
}
- parent.removeChild(element);
- for (int i=0; i<ary.length ;i++) {
+ xinclude.parent.removeChild(xinclude.element);
+ for (int i=0; i<ary.length; i++) {
+ xmlbase_stack.push(xinclude.base);
if (ary[i].getNodeType() ==
Node.ELEMENT_NODE) {
- process((Element)ary[i],parent);
+
scan((Element)ary[i],xinclude.parent);
}
+ xmlbase_stack.pop();
}
- return;
+
}
}
- NodeList child_nodes = element.getChildNodes();
- int length = child_nodes.getLength();
- Node ary[] = new Node[length];
- for (int i=0; i<length; i++) {
- ary[i] = child_nodes.item(i);
+ }
+
+ void scan(Element element, Element parent) throws Exception {
+ String name = element.getLocalName();
+ String uri = element.getNamespaceURI();
+ String prefix = element.getPrefix();
+ String value;
+ boolean xmlbase_attribute = false;
+ if
(element.hasAttributeNS(processor.XMLBASE_NAMESPACE_URI,processor.XMLBASE_ATTRIBUTE))
{
+ xmlbase_stack.push(current_xmlbase);
+ current_xmlbase = new
URL(element.getAttributeNS(processor.XMLBASE_NAMESPACE_URI,processor.XMLBASE_ATTRIBUTE));
+ xmlbase_attribute = true;
}
- for (int i=0; i<length; i++) {
- if (ary[i].getNodeType() == Node.ELEMENT_NODE) {
- process((Element)ary[i],element);
+ if (!(scanForXInclude(element,parent))) {
+ for (Node child = element.getFirstChild(); child !=
null; child = child.getNextSibling()) {
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ scan((Element)child,element);
+ }
}
}
if (xmlbase_attribute) {
- endXMLBaseAttribute();
+ current_xmlbase = xmlbase_stack.pop();
}
}
- void startXMLBaseAttribute(String value) throws MalformedURLException {
- if (current_xmlbase_uri != null) {
- xmlbase_stack.push(current_xmlbase_uri);
+ boolean scanForXInclude(Element element, Element parent) {
+ if
(element.hasAttributeNS(processor.XINCLUDE_NAMESPACE_URI,processor.XINCLUDE_HREF_ATTRIBUTE)
&&
element.hasAttributeNS(processor.XINCLUDE_NAMESPACE_URI,processor.XINCLUDE_PARSE_ATTRIBUTE))
{
+ String href =
element.getAttributeNS(processor.XINCLUDE_NAMESPACE_URI,processor.XINCLUDE_HREF_ATTRIBUTE);
+ String parse =
element.getAttributeNS(processor.XINCLUDE_NAMESPACE_URI,processor.XINCLUDE_PARSE_ATTRIBUTE);
+ xinclude_elements.addElement(new
XIncludeElement(element,parent,href,parse,current_xmlbase));
+ return true;
}
- System.err.println("URL IS "+value);
- current_xmlbase_uri = new URL(value);
- }
-
- void endXMLBaseAttribute() {
- current_xmlbase_uri = (URL)xmlbase_stack.pop();
+ return false;
}
- Object processXIncludeElement(Element element, String href, String
parse) throws Exception {
- String suffix;
- int index = href.indexOf('#');
- if (index < 0) {
- suffix = "";
- } else {
- suffix = href.substring(index+1);
- href = href.substring(0,index);
- }
- Object content;
- String system_id;
- Object local;
+ Object processXIncludeElement(XIncludeElement xinclude) throws
Exception {
+ Object content = null;
+ String system_id = null;
+ Object local = null;
try {
- if (href.charAt(0) == '/') {
- local = new
File(Utils.getRootpath(request,context)+href);
+ if (xinclude.href.equals("")) {
+ if (xinclude.xpath == null) {
+ throw new ProcessorException("Invalid
xinclude element: "+xinclude+": no href, no valid suffix");
+ }
+ NodeList list =
XPathAPI.selectNodeList(document,xinclude.xpath);
+ int length = list.getLength();
+ Node ary[] = new Node[length];
+ for (int i=0; i<length; i++) {
+ ary[i] = list.item(i).cloneNode(true);
+ }
+ return ary;
+ } else if (xinclude.href.charAt(0) == '/') {
+ /** local absolute URI, e.g. /foo.xml **/
+ local = new
File(Utils.getRootpath(request,context),xinclude.href);
system_id = ((File)local).getAbsolutePath();
content = new FileReader((File)local);
- } else if (href.indexOf("://") >= 0) {
- local = new URL(href);
+ } else if (xinclude.href.indexOf("://") >= 0) {
+ /** absolute URI, e.g.
http://example.com/foo.xml **/
+ local = new URL(xinclude.href);
system_id = local.toString();
content = ((URL)local).getContent();
- } else if (current_xmlbase_uri != null) {
- local = new URL(current_xmlbase_uri,href);
+ } else if (xinclude.base instanceof URL) {
+ /** relative URI, relative to XML Base URI **/
+ local = new
URL((URL)xinclude.base,xinclude.href);
system_id = local.toString();
content = ((URL)local).getContent();
- } else {
- local = new
File(Utils.getBasepath(request,context)+href);
- system_id = local.toString();
+ } else if (xinclude.base instanceof File) {
+ /** relative URI, relative to XML file in
filesystem **/
+ local = new
File((File)xinclude.base,xinclude.href);
+ system_id = ((File)local).getAbsolutePath();
content = new FileReader((File)local);
}
processor.monitored_table.put(monitor_key,"");
processor.monitor.watch(monitor_key,local);
} catch (MalformedURLException e) {
- throw new ProcessorException("Could not include
document: "+href+" is a malformed URL.");
+ throw new ProcessorException("Invalid xinclude element:
"+xinclude+": malformed URL");
}
Object result = null;
- if (parse.equals("text")) {
+ if (xinclude.parse.equals("text")) {
if (content instanceof Reader) {
Reader reader = (Reader)content;
int read;
- char ary[] = new char[1024];
+ char ary[] = new char[processor.BUFFER_SIZE];
StringBuffer sb = new StringBuffer();
if (reader != null) {
while ((read = reader.read(ary)) != -1)
{
@@ -305,7 +336,7 @@
InputStream input = (InputStream)content;
InputStreamReader reader = new
InputStreamReader(input);
int read;
- char ary[] = new char[1024];
+ char ary[] = new char[processor.BUFFER_SIZE];
StringBuffer sb = new StringBuffer();
if (reader != null) {
while ((read = reader.read(ary)) != -1)
{
@@ -315,7 +346,7 @@
}
result = document.createTextNode(sb.toString());
}
- } else if (parse.equals("xml")) {
+ } else if (xinclude.parse.equals("xml")) {
InputSource input;
if (content instanceof Reader) {
input = new InputSource((Reader)content);
@@ -330,9 +361,8 @@
included_document = parser.parse(input,false);
stripDocumentTypeNodes(included_document.getDocumentElement());
} catch (Exception e) {}
- if (suffix.startsWith("xpointer(") &&
suffix.endsWith(")")) {
- String xpath =
suffix.substring(9,suffix.length()-1);
- NodeList list =
XPathAPI.selectNodeList(included_document,xpath);
+ if (xinclude.xpath != null) {
+ NodeList list =
XPathAPI.selectNodeList(included_document,xinclude.xpath);
int length = list.getLength();
Node ary[] = new Node[length];
for (int i=0; i<length; i++) {