Re: Parsing a MODS-document with validation fails

Thomas Scheffler <[email protected]>
Newsgroups gmane.comp.java.jdom.general
Organization Friedrich-Schiller-Universität Jena
Message-ID <[email protected]>
Am 25.07.2011 09:05, schrieb Thomas Scheffler:
> Am 24.07.2011 23:46, schrieb Bradley S. Huffman:
>> And if the prefix "ns1" is already mapped to another URI because the
>> user decided to use "ns1" as a prefix, what kind of havoc will this
>> cause?
>
> I got your point! We have to check if "ns"+nsPrefixCount is already
> defined, if we create it. I do not know though if that is enough. We can
> do this for the current element and be sure of that. But if for instance
> we have three tag: "a","b","c", where "a" is parent of "b" an that is
> parent of "c". There could be an issue if "a" defines a namespace with
> prefix "ns?" that is in use by "c" and not by "b".
> When it comes to "b" we see the namespace prefix is currently undefined.
> And bound it to the new uri. This way the namespace in c changes.
> So we have to drag every usage of a prefix throughout the document with
> the "endPrefixMapping" event, that is currently missing an
> implementation in SAXHandler.
> Do you agree?

I prepared a new version of the patch that uses the "endPrefixMapping" 
event to handle namespace prefixes and also checks if "ns"+nsPrefixCount 
is already defined.

regards,

Thomas

>>
>> On Fri, Jul 22, 2011 at 4:08 PM, Thomas Scheffler
>> <[email protected]> wrote:
>>> Am 22.07.2011 22:53, schrieb Bradley S. Huffman:
>>>>
>>>> I'm not sure about a patch that makes up a namespace prefix. From the
>>>> patch
>>>>
>>>> nsPrefixCount++;
>>>> ns = Namespace.getNamespace("ns" + nsPrefixCount, attUri);
>>>>
>>>>
>>>> Seems like a kludge. My gut says it's something else.
>>>
>>> This is how it is done by the Oracle JAVA DocumentBuilder. JDOM won't
>>> accept
>>> a namespace without a prefix so you have to build some as the SAXParser
>>> delivers an attribute with QName=LocalName. Before that I take a look in
>>> predeclared namespaces, so that any prefix that is bound to an URI is
>>> used
>>> before building a new one. For the testing case I submitted in my
>>> original
>>> mail, "xlink" is found correctly which makes it more beautiful than the
>>> DocumentBuilder solution that creates "ns0" on every element with
>>> xlink:type
>>> set fixed to "simple". Hope you can follow my arguments.

_______________________________________________
To control your jdom-interest membership:
http://www.jdom.org/mailman/options/jdom-interest/[email protected]
jdom-namespace.patch (text/x-patch, 6.3 KB)
Index: src/java/org/jdom/input/SAXHandler.java
===================================================================
RCS file: /home/cvspublic/jdom/src/java/org/jdom/input/SAXHandler.java,v
retrieving revision 1.73
diff -u -r1.73 SAXHandler.java
--- src/java/org/jdom/input/SAXHandler.java	10 Nov 2007 05:29:00 -0000	1.73
+++ src/java/org/jdom/input/SAXHandler.java	25 Jul 2011 09:13:48 -0000
@@ -83,6 +83,9 @@
     /** Hash table to map SAX attribute type names to JDOM attribute types. */
     private static final Map attrNameToTypeMap = new HashMap(13);
 
+    /** If namespace prefix is undeclared use this a a prefix for nsPrefixCount */
+    private static final String UNDECLARED_NS_PREFIX = "ns";
+    
     /** <code>Document</code> object being built */
     private Document document;
 
@@ -120,6 +123,16 @@
       * startPrefixMapping, but are not yet available on the element */
     private List declaredNamespaces;
 
+    /** Temporary holder for namespaces that have not been declared with
+     * startPrefixMapping, but are required due to schema rules */
+    private List undeclaredNamespaces;
+    
+    /** Holds information of namespace the a currently available in the current context. */
+    private HashMap usedNamespaces;
+    
+    /** Counter for new namespace prefixes */
+    private int nsPrefixCount;
+
     /** Temporary holder for the internal subset */
     private StringBuffer internalSubset = new StringBuffer();
 
@@ -141,6 +154,9 @@
     /** The SAX Locator object provided by the parser */
     private Locator locator;
 
+    /** helps to find declared namespaces */
+    private Map nsURIMapping;
+
     /**
      * Class initializer: Populate a table to translate SAX attribute
      * type names into JDOM attribute type value (integer).
@@ -206,6 +222,9 @@
 
         atRoot = true;
         declaredNamespaces = new ArrayList();
+        undeclaredNamespaces = new ArrayList();
+        usedNamespaces = new HashMap();
+        nsURIMapping = new HashMap();
         externalEntities = new HashMap();
 
         document = this.factory.document(null);
@@ -333,6 +352,7 @@
         if (locator != null) {
             document.setBaseURI(locator.getSystemId());
         }
+        nsPrefixCount=0;
     }
 
     /**
@@ -496,7 +516,15 @@
         if (suppress) return;
 
         Namespace ns = Namespace.getNamespace(prefix, uri);
-        declaredNamespaces.add(ns);
+        
+        nsURIMapping.put(uri,ns); //removed in endPrefixMapping()
+        List namespaces = (List) usedNamespaces.get(prefix);
+        if (namespaces == null) {
+            namespaces = new ArrayList();
+            usedNamespaces.put(prefix, namespaces);
+        }
+        namespaces.add(ns); //removed in endPrefixMapping()
+        declaredNamespaces.add(ns); //removed in transferNamespaces()
     }
 
     /**
@@ -550,6 +578,7 @@
 
             String attLocalName = atts.getLocalName(i);
             String attQName = atts.getQName(i);
+            String attUri=atts.getURI(i);
             int attType = getAttributeType(atts.getType(i));
 
             // Bypass any xmlns attributes which might appear, as we got
@@ -567,10 +596,14 @@
             } else if (!attQName.equals(attLocalName)) {
                 String attPrefix = attQName.substring(0, attQName.indexOf(":"));
                 Namespace attNs = Namespace.getNamespace(attPrefix,
-                                                         atts.getURI(i));
+                                                         attUri);
 
                 attribute = factory.attribute(attLocalName, atts.getValue(i),
                                               attType, attNs);
+            } else if (!"".equals(attUri) && !namespaceURI.equals(attUri)) {
+                Namespace attNs = getUndeclaredNS(attUri);
+                attribute = factory.attribute(attLocalName, atts.getValue(i),
+                        attType, attNs);
             } else {
                 attribute = factory.attribute(attLocalName, atts.getValue(i),
                                               attType);
@@ -589,6 +622,32 @@
         currentElement = element;
     }
 
+    private Namespace getUndeclaredNS(String attUri) {
+        Namespace ns = (Namespace) nsURIMapping.get(attUri);
+        if (ns != null) {
+            return ns;
+        }
+        ns = createNamespaceWithPrefix(attUri);
+        return ns;
+    }
+
+    /**
+     * @param attUri
+     * @return
+     */
+    private Namespace createNamespaceWithPrefix(String attUri) {
+        Namespace ns = null;
+        boolean inUse=true;
+        while (inUse){
+            String prefix = UNDECLARED_NS_PREFIX + nsPrefixCount++;
+            ns = Namespace.getNamespace(prefix, attUri);
+            inUse = usedNamespaces.containsKey(prefix);
+        }
+        nsURIMapping.put(attUri, ns);
+        undeclaredNamespaces.add(ns);
+        return ns;
+    }
+
     /**
      * This will take the supplied <code>{@link Element}</code> and
      * transfer its namespaces to the global namespace storage.
@@ -604,6 +663,15 @@
             }
         }
         declaredNamespaces.clear();
+        i = undeclaredNamespaces.iterator();
+        while (i.hasNext()) {
+            Namespace ns = (Namespace)i.next();
+            if (ns != element.getNamespace()) {
+                element.addNamespaceDeclaration(ns);
+            }
+            nsURIMapping.remove(ns.getURI());
+        }
+        undeclaredNamespaces.clear();
     }
 
     /**
@@ -732,6 +800,23 @@
     }
 
     /**
+     * Receive notification of the end of a Namespace mapping.
+     *
+     * Will remove any information on prefix of that namespace.
+     *
+     * @param prefix The Namespace prefix being declared.
+     * @exception org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     * @see org.xml.sax.ContentHandler#endPrefixMapping
+     */
+    public void endPrefixMapping(String prefix) throws SAXException {
+        List namespaces=(List) usedNamespaces.get(prefix);
+        int index = namespaces.size()-1;
+        Namespace ns = (Namespace) namespaces.remove(index);
+        nsURIMapping.values().remove(ns);
+    }
+
+    /**
      * This will signify that a DTD is being parsed, and can be
      * used to ensure that comments and other lexical structures
      * in the DTD are not added to the JDOM <code>Document</code>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.