aboutsummaryrefslogtreecommitdiff
path: root/libjava/gnu/xml/pipeline/NSFilter.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/gnu/xml/pipeline/NSFilter.java')
-rw-r--r--libjava/gnu/xml/pipeline/NSFilter.java340
1 files changed, 340 insertions, 0 deletions
diff --git a/libjava/gnu/xml/pipeline/NSFilter.java b/libjava/gnu/xml/pipeline/NSFilter.java
new file mode 100644
index 0000000..9e8a643
--- /dev/null
+++ b/libjava/gnu/xml/pipeline/NSFilter.java
@@ -0,0 +1,340 @@
+/* NSFilter.java --
+ Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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, or (at your option)
+any later version.
+
+GNU Classpath 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. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.xml.pipeline;
+
+import java.util.EmptyStackException;
+import java.util.Enumeration;
+import java.util.Stack;
+
+import org.xml.sax.*;
+import org.xml.sax.ext.*;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.NamespaceSupport;
+
+
+/**
+ * This filter ensures that element and attribute names are properly prefixed,
+ * and that such prefixes are declared. Such data is critical for operations
+ * like writing XML text, and validating against DTDs: names or their prefixes
+ * may have been discarded, although they are essential to the exchange of
+ * information using XML. There are various common ways that such data
+ * gets discarded: <ul>
+ *
+ * <li> By default, SAX2 parsers must discard the "xmlns*"
+ * attributes, and may also choose not to report properly prefixed
+ * names for elements or attributes. (Some parsers may support
+ * changing the <em>namespace-prefixes</em> value from the default
+ * to <em>true</em>, effectively eliminating the need to use this
+ * filter on their output.)
+ *
+ * <li> When event streams are generated from a DOM tree, they may
+ * have never have had prefixes or declarations for namespaces; or
+ * the existing prefixes or declarations may have been invalidated
+ * by structural modifications to that DOM tree.
+ *
+ * <li> Other software writing SAX event streams won't necessarily
+ * be worrying about prefix management, and so they will need to
+ * have a transparent solution for managing them.
+ *
+ * </ul>
+ *
+ * <p> This filter uses a heuristic to choose the prefix to assign to any
+ * particular name which wasn't already corectly prefixed. The associated
+ * namespace will be correct, and the prefix will be declared. Original
+ * structures facilitating text editing, such as conventions about use of
+ * mnemonic prefix names or the scoping of prefixes, can't always be
+ * reconstructed after they are discarded, as strongly encouraged by the
+ * current SAX2 defaults.
+ *
+ * <p> Note that this can't possibly know whether values inside attribute
+ * value or document content involve prefixed names. If your application
+ * requires using prefixed names in such locations you'll need to add some
+ * appropriate logic (perhaps adding additional heuristics in a subclass).
+ *
+ * @author David Brownell
+ */
+public class NSFilter extends EventFilter
+{
+ private NamespaceSupport nsStack = new NamespaceSupport ();
+ private Stack elementStack = new Stack ();
+
+ private boolean pushedContext;
+ private String nsTemp [] = new String [3];
+ private AttributesImpl attributes = new AttributesImpl ();
+ private boolean usedDefault;
+
+ // gensymmed prefixes use this root name
+ private static final String prefixRoot = "prefix-";
+
+
+ /**
+ * Passes events through to the specified consumer, after first
+ * processing them.
+ *
+ * @param next the next event consumer to receive events.
+ */
+ // constructor used by PipelineFactory
+ public NSFilter (EventConsumer next)
+ {
+ super (next);
+
+ setContentHandler (this);
+ }
+
+ private void fatalError (String message)
+ throws SAXException
+ {
+ SAXParseException e;
+ ErrorHandler handler = getErrorHandler ();
+ Locator locator = getDocumentLocator ();
+
+ if (locator == null)
+ e = new SAXParseException (message, null, null, -1, -1);
+ else
+ e = new SAXParseException (message, locator);
+ if (handler != null)
+ handler.fatalError (e);
+ throw e;
+ }
+
+
+ public void startDocument () throws SAXException
+ {
+ elementStack.removeAllElements ();
+ nsStack.reset ();
+ pushedContext = false;
+ super.startDocument ();
+ }
+
+ /**
+ * This call is not passed to the next consumer in the chain.
+ * Prefix declarations and scopes are only exposed in the form
+ * of attributes; this callback just records a declaration that
+ * will be exposed as an attribute.
+ */
+ public void startPrefixMapping (String prefix, String uri)
+ throws SAXException
+ {
+ if (pushedContext == false) {
+ nsStack.pushContext ();
+ pushedContext = true;
+ }
+
+ // this check is awkward, but the paranoia prevents big trouble
+ for (Enumeration e = nsStack.getDeclaredPrefixes ();
+ e.hasMoreElements ();
+ /* NOP */ ) {
+ String declared = (String) e.nextElement ();
+
+ if (!declared.equals (prefix))
+ continue;
+ if (uri.equals (nsStack.getURI (prefix)))
+ return;
+ fatalError ("inconsistent binding for prefix '" + prefix
+ + "' ... " + uri + " (was " + nsStack.getURI (prefix) + ")");
+ }
+
+ if (!nsStack.declarePrefix (prefix, uri))
+ fatalError ("illegal prefix declared: " + prefix);
+ }
+
+ private String fixName (String ns, String l, String name, boolean isAttr)
+ throws SAXException
+ {
+ if ("".equals (name) || name == null) {
+ name = l;
+ if ("".equals (name) || name == null)
+ fatalError ("empty/null name");
+ }
+
+ // can we correctly process the name as-is?
+ // handles "element scope" attribute names here.
+ if (nsStack.processName (name, nsTemp, isAttr) != null
+ && nsTemp [0].equals (ns)
+ ) {
+ return nsTemp [2];
+ }
+
+ // nope, gotta modify the name or declare a default mapping
+ int temp;
+
+ // get rid of any current prefix
+ if ((temp = name.indexOf (':')) >= 0) {
+ name = name.substring (temp + 1);
+
+ // ... maybe that's enough (use/prefer default namespace) ...
+ if (!isAttr && nsStack.processName (name, nsTemp, false) != null
+ && nsTemp [0].equals (ns)
+ ) {
+ return nsTemp [2];
+ }
+ }
+
+ // must we define and use the default/undefined prefix?
+ if ("".equals (ns)) {
+ if (isAttr)
+ fatalError ("processName bug");
+ if (attributes.getIndex ("xmlns") != -1)
+ fatalError ("need to undefine default NS, but it's bound: "
+ + attributes.getValue ("xmlns"));
+
+ nsStack.declarePrefix ("", "");
+ attributes.addAttribute ("", "", "xmlns", "CDATA", "");
+ return name;
+ }
+
+ // is there at least one non-null prefix we can use?
+ for (Enumeration e = nsStack.getDeclaredPrefixes ();
+ e.hasMoreElements ();
+ /* NOP */) {
+ String prefix = (String) e.nextElement ();
+ String uri = nsStack.getURI (prefix);
+
+ if (uri == null || !uri.equals (ns))
+ continue;
+ return prefix + ":" + name;
+ }
+
+ // no such luck. create a prefix name, declare it, use it.
+ for (temp = 0; temp >= 0; temp++) {
+ String prefix = prefixRoot + temp;
+
+ if (nsStack.getURI (prefix) == null) {
+ nsStack.declarePrefix (prefix, ns);
+ attributes.addAttribute ("", "", "xmlns:" + prefix,
+ "CDATA", ns);
+ return prefix + ":" + name;
+ }
+ }
+ fatalError ("too many prefixes genned");
+ // NOTREACHED
+ return null;
+ }
+
+ public void startElement (
+ String uri, String localName,
+ String qName, Attributes atts
+ ) throws SAXException
+ {
+ if (!pushedContext)
+ nsStack.pushContext ();
+ pushedContext = false;
+
+ // make sure we have all NS declarations handy before we start
+ int length = atts.getLength ();
+
+ for (int i = 0; i < length; i++) {
+ String aName = atts.getQName (i);
+
+ if (!aName.startsWith ("xmlns"))
+ continue;
+
+ String prefix;
+
+ if ("xmlns".equals (aName))
+ prefix = "";
+ else if (aName.indexOf (':') == 5)
+ prefix = aName.substring (6);
+ else // "xmlnsfoo" etc.
+ continue;
+ startPrefixMapping (prefix, atts.getValue (i));
+ }
+
+ // put namespace decls at the start of our regenned attlist
+ attributes.clear ();
+ for (Enumeration e = nsStack.getDeclaredPrefixes ();
+ e.hasMoreElements ();
+ /* NOP */) {
+ String prefix = (String) e.nextElement ();
+
+ attributes.addAttribute ("", "",
+ ("".equals (prefix)
+ ? "xmlns"
+ : "xmlns:" + prefix),
+ "CDATA",
+ nsStack.getURI (prefix));
+ }
+
+ // name fixups: element, then attributes.
+ // fixName may declare a new prefix or, for the element,
+ // redeclare the default (if element name needs it).
+ qName = fixName (uri, localName, qName, false);
+
+ for (int i = 0; i < length; i++) {
+ String aName = atts.getQName (i);
+ String aNS = atts.getURI (i);
+ String aLocal = atts.getLocalName (i);
+ String aType = atts.getType (i);
+ String aValue = atts.getValue (i);
+
+ if (aName.startsWith ("xmlns"))
+ continue;
+ aName = fixName (aNS, aLocal, aName, true);
+ attributes.addAttribute (aNS, aLocal, aName, aType, aValue);
+ }
+
+ elementStack.push (qName);
+
+ // pass event along, with cleaned-up names and decls.
+ super.startElement (uri, localName, qName, attributes);
+ }
+
+ public void endElement (String uri, String localName, String qName)
+ throws SAXException
+ {
+ nsStack.popContext ();
+ qName = (String) elementStack.pop ();
+ super.endElement (uri, localName, qName);
+ }
+
+ /**
+ * This call is not passed to the next consumer in the chain.
+ * Prefix declarations and scopes are only exposed in their
+ * attribute form.
+ */
+ public void endPrefixMapping (String prefix)
+ throws SAXException
+ { }
+
+ public void endDocument () throws SAXException
+ {
+ elementStack.removeAllElements ();
+ nsStack.reset ();
+ super.endDocument ();
+ }
+}