Skip to content

RFE: Modify XMLOutputter to allow smart subclasses (Paul Libbrecht) #5

@hunterhacker

Description

@hunterhacker

http://markmail.org/message/4nfda3qfi36lc5w5

Hello,

please find at:
http://www.activemath.org/~paul/tmp/DTDaware
a contribution to JDOM in the form of a patched XMLOutputter to allow
subclasses to stop output of some attributes and namespace
declarations as well as a DTDAwareXMLOutputter subclass which uses
Mark Wutka's DTDparser (I used version 1.23) to decide not to output
attributes or namespace decls if they are implicit in the DTD. This
feature has been a key to maintenance of a clean authorable XML.

I haven't put licenses yet... want me to?
Feel free to apply any license there.
thanks in advance

paul

Here's the XMLOutputter.java diff

--- XMLOutputter.java   2010-10-28 14:05:31.000000000 -0700
+++ /tmp/XMLOutputter.java  2011-07-31 17:50:46.000000000 -0700
@@ -115,7 +115,7 @@
 public class XMLOutputter implements Cloneable {

     private static final String CVS_ID =
-      "@(#) $RCSfile: XMLOutputter.java,v $ $Revision: 1.117 $ $Date: 2009/07/23 05:54:23 $ $Name:  $";
+      "@(#) $RCSfile: XMLOutputter.java,v $ $Revision: 1.117 $ $Date: 2009/07/23 05:54:23 $ $Name: jdom_1_1_1 $";

     // For normal output
     private Format userFormat = Format.getRawFormat();
@@ -1099,9 +1099,10 @@
      * declarations.
      *
      * @param ns <code>Namespace</code> to print definition of
+     * @param elt <code>Element</code> in which this namespace is output
      * @param out <code>Writer</code> to use.
      */
-    private void printNamespace(Writer out, Namespace ns,
+    private void printNamespace(Writer out, Namespace ns, Element elt,
                                 NamespaceStack namespaces)
                      throws IOException {
         String prefix = ns.getPrefix();
@@ -1111,6 +1112,9 @@
         if (uri.equals(namespaces.getURI(prefix))) {
             return;
         }
+        if(!shouldOutputNamespace(ns,elt,namespaces)) {
+            return;
+        }

         out.write(" xmlns");
         if (!prefix.equals("")) {
@@ -1123,6 +1127,19 @@
         namespaces.push(ns);
     }

+    protected boolean shouldOutputNamespace(Namespace ns, Element element, NamespaceStack namespaces) {
+        // Add namespace decl only if it's not the XML namespace and it's
+        // not the NO_NAMESPACE with the prefix "" not yet mapped
+        // (we do output xmlns="" if the "" prefix was already used and we
+        // need to reclaim it for the NO_NAMESPACE)
+        if (ns == Namespace.XML_NAMESPACE) {
+            return false;
+        } else if ( ((ns == Namespace.NO_NAMESPACE) &&
+               (namespaces.getURI("") == null))) {
+            return false;
+        } else
+            return true;
+    }
     /**
      * This will handle printing of a <code>{@link Attribute}</code> list.
      *
@@ -1141,36 +1158,33 @@
         for (int i = 0; i < attributes.size(); i++) {
             Attribute attribute = (Attribute) attributes.get(i);
             Namespace ns = attribute.getNamespace();
-            if ((ns != Namespace.NO_NAMESPACE) &&
-                (ns != Namespace.XML_NAMESPACE)) {
-                    printNamespace(out, ns, namespaces);
+            if (shouldOutputNamespace(ns,parent,namespaces)
+                    && ns != Namespace.NO_NAMESPACE && ns != Namespace.XML_NAMESPACE) {
+                    printNamespace(out, ns, parent, namespaces);
             }

-            out.write(" ");
-            printQualifiedName(out, attribute);
-            out.write("=");
+            if(shouldOutputAttribute(attribute,parent,namespaces)) {
+                out.write(" ");
+                printQualifiedName(out, attribute);
+                out.write("=");

-            out.write("\"");
-            out.write(escapeAttributeEntities(attribute.getValue()));
-            out.write("\"");
+                out.write("\"");
+                out.write(escapeAttributeEntities(attribute.getValue()));
+                out.write("\"");
+            }
         }
     }

+    protected boolean shouldOutputAttribute(Attribute attribute, Element parent, NamespaceStack namespaces) {
+        return true;
+    }
+
     private void printElementNamespace(Writer out, Element element,
                                        NamespaceStack namespaces)
                              throws IOException {
-        // Add namespace decl only if it's not the XML namespace and it's
-        // not the NO_NAMESPACE with the prefix "" not yet mapped
-        // (we do output xmlns="" if the "" prefix was already used and we
-        // need to reclaim it for the NO_NAMESPACE)
         Namespace ns = element.getNamespace();
-        if (ns == Namespace.XML_NAMESPACE) {
-            return;
-        }
-        if ( !((ns == Namespace.NO_NAMESPACE) &&
-               (namespaces.getURI("") == null))) {
-            printNamespace(out, ns, namespaces);
-        }
+        if(shouldOutputNamespace(ns,element,namespaces))
+            printNamespace(out, ns, element, namespaces);
     }

     private void printAdditionalNamespaces(Writer out, Element element,
@@ -1180,7 +1194,7 @@
         if (list != null) {
             for (int i = 0; i < list.size(); i++) {
                 Namespace additional = (Namespace)list.get(i);
-                printNamespace(out, additional, namespaces);
+                printNamespace(out, additional, element, namespaces);
             }
         }
     }

Here's DTDAwareXMLOutputter.java

package org.jdom.output;

import com.wutka.dtd.DTD;
import com.wutka.dtd.DTDElement;
import com.wutka.dtd.DTDAttribute;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.Attribute;

/** A subclass of {@link XMLOutputter} to avoid printing some attributes and namespace declarations
 * whose values is already the default as specified by the DTD. This is a key ingredient to provide
 * a much more readable output but may break re-parsing if not output with the appropriate
 * {@link org.jdom.DocType}.
 *
 * @author Paul Libbrecht <paul@activemath.org>
 */
public class DTDAwareXMLOutputter extends XMLOutputter {

    public DTDAwareXMLOutputter() {
        super();
    }

    public DTDAwareXMLOutputter(DTD dtd) {
        super();
        this.setDtd(dtd);
    }

    public DTDAwareXMLOutputter(Format format) {
        super(format);
    }

    public DTDAwareXMLOutputter(XMLOutputter that) {
        super(that);
    }

    protected DTD dtd;

    public DTD getDtd() {
        return dtd;
    }

    public void setDtd(DTD dtd) {
        this.dtd = dtd;
    }



    protected boolean shouldOutputNamespace(Namespace ns, Element element, NamespaceStack namespaces) {
        if(super.shouldOutputNamespace(ns,element,namespaces))
        if(dtd == null) return true;
        DTDElement eltDecl = null;
        eltDecl = (DTDElement) dtd.elements.get(element.getName());
         if(eltDecl!=null) {
             String nsAttName;
             String prefix = ns.getPrefix();
             if(prefix!=null && prefix.length()>0) {
                 nsAttName = "xmlns:".concat(prefix);
             } else {
                 nsAttName = "xmlns";
             }
            DTDAttribute nsDecl = eltDecl.getAttribute(nsAttName);
            if(nsDecl != null && ns.getURI().equals(nsDecl.getDefaultValue())) {
                return false;
            }
         }
        return true;
    }

    protected boolean shouldOutputAttribute(Attribute attribute, Element parent, NamespaceStack namespaces) {
        if(false == super.shouldOutputAttribute(attribute, parent, namespaces)) return false;
        // PL: check if attribute is in default value, then don't output it
        DTDElement eltDecl = null;
        if(dtd!=null) {
            eltDecl = (DTDElement) dtd.elements.get(parent.getName());
            if(eltDecl!=null) {
                DTDAttribute attDecl = eltDecl
                        .getAttribute(attribute.getQualifiedName());
                if(attDecl!=null) {
                    String defaultValue = attDecl.getDefaultValue();
                    if(defaultValue!=null && defaultValue.equals(attribute.getValue()))
                        return false;
                }
            }
        }
        return true;
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions