Permalink
Browse files

Fixes #66 Attribute/DTD-default aware output processor.

This fix goes far beyond moving the contrib code to core. Instead, adds a new flag property to Attribute, which indicates whether that Attribute is specified as part of the XML input document, or added as a default by the DTD. The JDOM SAXHandler respects the Attributes2.isSpecified() methods to in turn set the isSPecified flag on the Attribute. The end result is that the isSpecified flag is set on all Attributes unless the attribute was provided by a Default (or 'fixed') DTD declaration.

I cannot find comprehensive documentation for it, but it appears that at least the Xerces parser also manages the specified flag for XML Schema derived attributes.

Additionally, the Format class has been extended to support the isSpecifiedAttributesOnly() method. This can be set with Format.setSpecifiedAttributesOnly(boolean).

All the Outputters (XML, SAX, StAX, DOM) honour the Format.isSpecifiedAttributesOnly() flag. As a consequence, you can now easily exclude those attributes which are created only by DTD (XML Schema) devices when outputting your XML.

The Attribute class itself has been modified to set the specified flag to true if any change is direclty made to the Attribute. The logic being that, if you change the attribute, then it is not what was set in the DTD, and it is now 'specified'.
  • Loading branch information...
1 parent dc0bfd9 commit a854727b5b2d922a1a70cf88eddc7c890b4cc567 @rolfl rolfl committed Mar 12, 2012
View
135 contrib/src/java/org/jdom2/contrib/dtdaware/AttFilter.java
@@ -0,0 +1,135 @@
+package org.jdom2.contrib.dtdaware;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.jdom2.Attribute;
+import org.jdom2.Element;
+import org.jdom2.filter.AbstractFilter;
+
+
+/**
+ * This AttFilter can be programmed to ignore specific attributes.
+ * The ignore criteria is based on the Attribute name, value, and the
+ * Element it is attached to.
+ *
+ * @author Rolf Lear
+ *
+ */
+public class AttFilter extends AbstractFilter<Attribute> {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ /**
+ * Yeah, this is complicated, but it is basically a search tree index.
+ * This is a nested index of attribute details:
+ * <p>
+ * <ol>
+ * <li> at the top is the attribute name
+ * <li> then the Attribute value.
+ * <li> next is the Element name. We only check this level if the attribute
+ * name and value matches to something that is ignored.
+ * <li> next we check to make sure that we are in the correct Attribute Namespace
+ * <li> then finally the Element namespace.
+ * </ol>
+ * <p>
+ * If we search this index tree and all the details match existing entries,
+ * then it is an ignored attribute. The order is set up in such a way that
+ * there should not need to be many checks in HashMaps for non-ignored
+ * content... essentiually we have to have the same attname and attvalue
+ * before the system even checks the Attribute's parent details.
+ */
+ // attname attval emtname attns emtns
+ private final Map<String,Map<String,Map<String,Map<String,Set<String>>>>>
+ ignores = new HashMap<String,Map<String,Map<String,Map<String,Set<String>>>>>();
+
+ /**
+ * Ignore an attribute (where the attribute and element are both in the
+ * no-uri namespace).
+ * @param elementname The name of the Attribute's parent element.
+ * @param attname The name of the attribute.
+ * @param attvalue The attribute value to ignore.
+ */
+ public void ignore(String elementname, String attname, String attvalue) {
+ ignore("", elementname, "", attname, attvalue);
+ }
+
+ /**
+ * Ignore an attribute.
+ * @param elementuri The NamespaceURI of the Attribute's parent Element
+ * @param elementname The name of the Attribute's parent Element.
+ * @param atturi The Attribute's namespace URI.
+ * @param attname The name of the attribute.
+ * @param attvalue The attribute value.
+ */
+ public void ignore(String elementuri, String elementname,
+ String atturi, String attname, String attvalue) {
+ Map<String, Map<String, Map<String, Set<String>>>> av =
+ ignores.get(attname);
+ if (av == null) {
+ av = new HashMap<String, Map<String,Map<String,Set<String>>>>();
+ ignores.put(attname, av);
+ }
+ Map<String, Map<String,Set<String>>> en = av.get(attvalue);
+ if (en == null) {
+ en = new HashMap<String, Map<String,Set<String>>>();
+ av.put(attvalue, en);
+ }
+ Map<String, Set<String>> ans = en.get(elementname);
+ if (ans == null) {
+ ans = new HashMap<String, Set<String>>();
+ en.put(elementname, ans);
+ }
+ Set<String> ens = ans.get(atturi);
+ if (ens == null) {
+ ens = new HashSet<String>();
+ ans.put(atturi, ens);
+ }
+ ens.add(elementuri);
+ }
+
+
+
+
+ @Override
+ public Attribute filter(Object content) {
+ if (!(content instanceof Attribute)) {
+ return null;
+ }
+ Attribute attribute = (Attribute)content;
+ if (!ignores.isEmpty()) {
+ // yes, there are ignores.
+ final Map<String, Map<String, Map<String, Set<String>>>> av =
+ ignores.get(attribute.getName());
+ if (av != null) {
+ // we are ignoring at least one attribute with this name.
+ final Map<String, Map<String, Set<String>>> en =
+ av.get(attribute.getValue());
+ if (en != null) {
+ // we ignore something with the attribute name and value
+ final Element e = attribute.getParent();
+ final Map<String, Set<String>> ans =
+ en.get(e.getName());
+ if (ans != null) {
+ // and the same Element name
+ final Set<String> ens = ans.get(attribute.getNamespaceURI());
+ if (ens != null && ens.contains(e.getNamespaceURI())) {
+ // and the same Attribute and Element namespace....
+ // we match the ignored content...
+ // skip this attribute.
+ return null;
+ }
+ }
+ }
+ }
+ }
+ return attribute;
+ }
+
+
+
+}
View
101 contrib/src/java/org/jdom2/contrib/dtdaware/AttFilteredXMLOutputProcessor.java
@@ -0,0 +1,101 @@
+/*--
+
+ Copyright (C) 2012 Jason Hunter & Brett McLaughlin.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions, and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions, and the disclaimer that follows
+ these conditions in the documentation and/or other materials
+ provided with the distribution.
+
+ 3. The name "JDOM" must not be used to endorse or promote products
+ derived from this software without prior written permission. For
+ written permission, please contact <request_AT_jdom_DOT_org>.
+
+ 4. Products derived from this software may not be called "JDOM", nor
+ may "JDOM" appear in their name, without prior written permission
+ from the JDOM Project Management <request_AT_jdom_DOT_org>.
+
+ In addition, we request (but do not require) that you include in the
+ end-user documentation provided with the redistribution and/or in the
+ software itself an acknowledgement equivalent to the following:
+ "This product includes software developed by the
+ JDOM Project (http://www.jdom.org/)."
+ Alternatively, the acknowledgment may be graphical using the logos
+ available at http://www.jdom.org/images/logos.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ This software consists of voluntary contributions made by many
+ individuals on behalf of the JDOM Project and was originally
+ created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
+ Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
+ on the JDOM Project, please see <http://www.jdom.org/>.
+
+ */
+
+package org.jdom2.contrib.dtdaware;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.jdom2.Attribute;
+import org.jdom2.filter.Filter;
+import org.jdom2.output.support.AbstractXMLOutputProcessor;
+import org.jdom2.output.support.FormatStack;
+
+/**
+ * Implement a class that is sensitive to a document's attributes, and will not
+ * output attributes that fail to match the supplied Filter.
+ *
+ * @author Rolf Lear
+ *
+ */
+public class AttFilteredXMLOutputProcessor extends AbstractXMLOutputProcessor {
+ /**
+ * Attributes that do not match this filter will not be printed.
+ */
+ private final Filter<? super Attribute> attfilter;
+
+ /**
+ * Construct this AttAwareXMLOutputProcessor.
+ * @param attf The filter to use which determines what Attributes are processed.
+ */
+ public AttFilteredXMLOutputProcessor(Filter<? super Attribute> attf) {
+ super();
+ this.attfilter = attf;
+ }
+
+
+ /**
+ * This extends the printAttribute code to search for attributes to ignore.
+ */
+ @Override
+ protected void printAttribute(Writer out, FormatStack fstack,
+ Attribute attribute) throws IOException {
+ // do we have anything to ignore.
+ // we are not ignoring this attribute.
+ if (!attfilter.matches(attribute)) {
+ return;
+ }
+ super.printAttribute(out, fstack, attribute);
+ }
+}
View
30 core/src/java/org/jdom2/Attribute.java
@@ -150,6 +150,12 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
/** The type of the <code>Attribute</code> */
protected AttributeType type = AttributeType.UNDECLARED;
+
+ /**
+ * Specified attributes are part of the XML,
+ * unspecified attributes are 'defaulted' from a DTD.
+ */
+ protected boolean specified = true;
/**
* The parent to which this Attribute belongs. Change it with
@@ -362,6 +368,7 @@ public Attribute setName(final String name) {
throw new IllegalNameException(name, "attribute", reason);
}
this.name = name;
+ specified = true;
return this;
}
@@ -460,6 +467,7 @@ public Attribute setNamespace(Namespace namespace) {
"NO_NAMESPACE namespace");
}
this.namespace = namespace;
+ specified = true;
return this;
}
@@ -493,6 +501,7 @@ public Attribute setValue(final String value) {
throw new IllegalDataException(value, "attribute", reason);
}
this.value = value;
+ specified = true;
return this;
}
@@ -516,6 +525,7 @@ public AttributeType getAttributeType() {
*/
public Attribute setAttributeType(final AttributeType type) {
this.type = type == null ? AttributeType.UNDECLARED : type;
+ specified = true;
return this;
}
@@ -535,6 +545,26 @@ public Attribute setAttributeType(final int type) {
}
/**
+ * Get the 'specified' flag. True values indicate this attribute
+ * was part of an XML document, false indicates it was defaulted
+ * from a DTD.
+ * @return the specified flag.
+ * @since JDOM2
+ */
+ public boolean isSpecified() {
+ return specified;
+ }
+
+ /**
+ * Change the specified flag to the given value.
+ * @param specified The value to set the specified flag to.
+ * @since JDOM2
+ */
+ public void setSpecified(boolean specified) {
+ this.specified = specified;
+ }
+
+ /**
* This returns a <code>String</code> representation of the
* <code>Attribute</code>, suitable for debugging.
*
View
2 core/src/java/org/jdom2/filter/AbstractFilter.java
@@ -68,7 +68,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* @author Bradley S. Huffman
* @param <T> The Generic type of content returned by this Filter
*/
-abstract class AbstractFilter<T> implements Filter<T> {
+public abstract class AbstractFilter<T> implements Filter<T> {
/**
* JDOM2 Serialization: Default mechanism
View
6 core/src/java/org/jdom2/input/sax/SAXHandler.java
@@ -65,6 +65,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
import org.xml.sax.DTDHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
+import org.xml.sax.ext.Attributes2;
import org.xml.sax.ext.DeclHandler;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.DefaultHandler;
@@ -622,6 +623,7 @@ public void startElement(final String namespaceURI, String localName,
String attPrefix = "";
String attLocalName = atts.getLocalName(i);
final String attQName = atts.getQName(i);
+ final boolean specified = (atts instanceof Attributes2) ? ((Attributes2)atts).isSpecified(i) : true;
// If attribute QName is set, then set attribute prefix and
// attribute local name as necessary
@@ -712,6 +714,10 @@ public void startElement(final String namespaceURI, String localName,
final Attribute attribute = factory.attribute(attLocalName,
attValue, attType, attNs);
+ if (!specified) {
+ // it is a DTD defaulted value.
+ attribute.setSpecified(false);
+ }
factory.setAttribute(element, attribute);
}
View
25 core/src/java/org/jdom2/output/Format.java
@@ -444,6 +444,10 @@ public static final String escapeText(
/** Whether or not to output the encoding in the XML declaration
* - default is <code>false</code> */
boolean omitEncoding = false;
+
+ /** Whether Attributes that are defaulted from the DTD or Schema
+ * are output. */
+ boolean specifiedAttributesOnly = false;
/** Whether or not to expand empty elements to
* &lt;tagName&gt;&lt;/tagName&gt; - default is <code>false</code> */
@@ -751,6 +755,27 @@ public Format setEncoding(String encoding) {
public String getEncoding() {
return encoding;
}
+
+
+ /**
+ * Will Attributes defaulted from the DTD or XMLSchema
+ * be output
+ * @return true if the defaulted Attributes will be output
+ */
+ public boolean isSpecifiedAttributesOnly() {
+ return specifiedAttributesOnly;
+ }
+
+ /**
+ * Set whether only those Attributes specified in the input XML should
+ * be output. Other Attributes (those defaulted or 'fixed' in the DTD
+ * or XMLSchema) should be ignored.
+ * @param specifiedAttributesOnly true if the defaulted
+ * Attributes should be ignored, false if they should be output
+ */
+ public void setSpecifiedAttributesOnly(boolean specifiedAttributesOnly) {
+ this.specifiedAttributesOnly = specifiedAttributesOnly;
+ }
@Override
public Format clone() {
View
8 core/src/java/org/jdom2/output/support/AbstractDOMOutputProcessor.java
@@ -407,6 +407,9 @@ private static String getXmlnsTagFor(Namespace ns) {
*/
protected org.w3c.dom.Attr printAttribute(final FormatStack fstack,
final org.w3c.dom.Document basedoc, final Attribute attribute) {
+ if (!attribute.isSpecified() && fstack.isSpecifiedAttributesOnly()) {
+ return null;
+ }
org.w3c.dom.Attr attr = basedoc.createAttributeNS(
attribute.getNamespaceURI(), attribute.getQualifiedName());
attr.setValue(attribute.getValue());
@@ -464,7 +467,10 @@ private static String getXmlnsTagFor(Namespace ns) {
if (element.hasAttributes()) {
for (Attribute att : element.getAttributes()) {
- ret.setAttributeNodeNS(printAttribute(fstack, basedoc, att));
+ final org.w3c.dom.Attr a = printAttribute(fstack, basedoc, att);
+ if (a != null) {
+ ret.setAttributeNodeNS(a);
+ }
}
}
View
3 core/src/java/org/jdom2/output/support/AbstractSAXOutputProcessor.java
@@ -568,6 +568,9 @@ protected void printElement(final SAXTarget out, final FormatStack fstack,
// Allocate attribute list.
if (element.hasAttributes()) {
for (Attribute a : element.getAttributes()) {
+ if (!a.isSpecified() && fstack.isSpecifiedAttributesOnly()) {
+ continue;
+ }
atts.addAttribute(a.getNamespaceURI(), a.getName(),
a.getQualifiedName(),
getAttributeTypeName(a.getAttributeType()),
View
28 core/src/java/org/jdom2/output/support/AbstractStAXEventProcessor.java
@@ -155,20 +155,36 @@ public void remove() {
private final Iterator<Attribute> source;
private final XMLEventFactory fac;
- public AttIterator(Iterator<Attribute> source, XMLEventFactory fac) {
+ public AttIterator(final Iterator<Attribute> source, final XMLEventFactory fac,
+ final boolean specifiedAttributesOnly) {
super();
- this.source = source;
+ // remove not-specified Attributes if needed....
+ this.source = specifiedAttributesOnly ? specified(source) : source;
this.fac = fac;
}
+ private Iterator<Attribute> specified(Iterator<Attribute> src) {
+ if (src == null) {
+ return null;
+ }
+ final ArrayList<Attribute> al = new ArrayList<Attribute>();
+ while (src.hasNext()) {
+ Attribute att = src.next();
+ if (att.isSpecified()) {
+ al.add(att);
+ }
+ }
+ return al.isEmpty() ? null : al.iterator();
+ }
+
@Override
public boolean hasNext() {
return source != null && source.hasNext();
}
@Override
public javax.xml.stream.events.Attribute next() {
- Attribute att = source.next();
+ final Attribute att = source.next();
final Namespace ns = att.getNamespace();
if (ns == Namespace.NO_NAMESPACE) {
return fac.createAttribute(att.getName(), att.getValue());
@@ -627,15 +643,15 @@ protected void printElement(final XMLEventConsumer out, final FormatStack fstack
null;
if (ns == Namespace.NO_NAMESPACE) {
out.add(eventfactory.createStartElement("", "", element.getName(),
- new AttIterator(ait, eventfactory),
+ new AttIterator(ait, eventfactory, fstack.isSpecifiedAttributesOnly()),
new NSIterator(nstack.addedForward().iterator(), eventfactory)));
} else if ("".equals(ns.getPrefix())) {
out.add(eventfactory.createStartElement("", ns.getURI(), element.getName(),
- new AttIterator(ait, eventfactory),
+ new AttIterator(ait, eventfactory, fstack.isSpecifiedAttributesOnly()),
new NSIterator(nstack.addedForward().iterator(), eventfactory)));
} else {
out.add(eventfactory.createStartElement(ns.getPrefix(), ns.getURI(), element.getName(),
- new AttIterator(ait, eventfactory),
+ new AttIterator(ait, eventfactory, fstack.isSpecifiedAttributesOnly()),
new NSIterator(nstack.addedForward().iterator(), eventfactory)));
}
ait = null;
View
4 core/src/java/org/jdom2/output/support/AbstractStAXStreamProcessor.java
@@ -785,6 +785,10 @@ protected void printNamespace(final XMLStreamWriter out, final FormatStack fstac
protected void printAttribute(final XMLStreamWriter out, final FormatStack fstack,
final Attribute attribute) throws XMLStreamException {
+ if (!attribute.isSpecified() && fstack.isSpecifiedAttributesOnly()) {
+ return;
+ }
+
final Namespace ns = attribute.getNamespace();
if (ns == Namespace.NO_NAMESPACE) {
out.writeAttribute(attribute.getName(), attribute.getValue());
View
3 core/src/java/org/jdom2/output/support/AbstractXMLOutputProcessor.java
@@ -1034,6 +1034,9 @@ protected void printNamespace(final Writer out, final FormatStack fstack,
protected void printAttribute(final Writer out, final FormatStack fstack,
final Attribute attribute) throws IOException {
+ if (!attribute.isSpecified() && fstack.isSpecifiedAttributesOnly()) {
+ return;
+ }
write(out, " ");
write(out, attribute.getQualifiedName());
write(out, "=");
View
17 core/src/java/org/jdom2/output/support/FormatStack.java
@@ -115,6 +115,11 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* &lt;tagName&gt;&lt;/tagName&gt; - default is <code>false</code>
*/
private final boolean expandEmptyElements;
+
+ /**
+ * Whether or not to output 'specified' Attributes only
+ */
+ private final boolean specifiedAttributesOnly;
/** entity escape logic */
private EscapeStrategy escapeStrategy;
@@ -163,6 +168,7 @@ public FormatStack(Format format) {
expandEmptyElements = format.getExpandEmptyElements();
escapeStrategy = format.getEscapeStrategy();
defaultMode = format.getTextMode();
+ specifiedAttributesOnly = format.isSpecifiedAttributesOnly();
levelIndent[depth] = format.getIndent() == null
? null : "";
@@ -203,6 +209,17 @@ public String getEncoding() {
public boolean isOmitDeclaration() {
return omitDeclaration;
}
+
+ /**
+ * Indicate whether only those Attributes specified in the XML
+ * should be output.
+ * @return true if only the specified Attributes should be output,
+ * false if those Attributes defaulted from the DTD or XML schema
+ * should be output too.
+ */
+ public boolean isSpecifiedAttributesOnly() {
+ return specifiedAttributesOnly;
+ }
/**
* @return the original {@link Format#getOmitEncoding()}
View
31 test/src/java/org/jdom2/test/cases/TestAttribute.java
@@ -101,6 +101,7 @@ public void test_TCC() {
private static final long serialVersionUID = 200L;
};
assertTrue(null == attribute.getName());
+ assertTrue(attribute.isSpecified());
}
/**
@@ -110,6 +111,7 @@ public void test_TCC() {
@Test
public void test_TCC___String_String() {
final Attribute attribute = new Attribute("test", "value");
+ assertTrue(attribute.isSpecified());
assertTrue("incorrect attribute name", attribute.getName().equals("test"));
assertTrue("incoorect attribute value", attribute.getValue().equals("value"));
assertEquals("incorrect attribute type", attribute.getAttributeType(), Attribute.UNDECLARED_TYPE);
@@ -923,5 +925,34 @@ public void testNullAttributeType() {
att.setAttributeType(AttributeType.ID);
assertTrue(att.getAttributeType() == AttributeType.ID);
}
+
+ @Test
+ public void testSpecified() {
+ Attribute att = new Attribute("att", "value");
+ assertTrue(att.isSpecified());
+ att.setSpecified(false);
+ assertFalse(att.isSpecified());
+
+ att.setValue("val");
+ assertTrue(att.isSpecified());
+ att.setSpecified(false);
+ assertFalse(att.isSpecified());
+
+ att.setName("attb");
+ assertTrue(att.isSpecified());
+ att.setSpecified(false);
+ assertFalse(att.isSpecified());
+
+ att.setNamespace(Namespace.getNamespace("pfx", "nothing"));
+ assertTrue(att.isSpecified());
+ att.setSpecified(false);
+ assertFalse(att.isSpecified());
+
+ att.setAttributeType(AttributeType.ID);
+ assertTrue(att.isSpecified());
+ att.setSpecified(false);
+ assertFalse(att.isSpecified());
+
+ }
}
View
2 test/src/java/org/jdom2/test/cases/input/TestSAXComplexSchema.java
@@ -106,8 +106,10 @@ public void testBuildFileNewSAX() throws IOException {
assertTrue(defns.equals(data.getNamespace()));
Attribute att = data.getAttribute("type", Namespace.NO_NAMESPACE);
assertTrue("Could not find type attribute in default ns.", att != null);
+ assertTrue(att.isSpecified());
att = data.getAttribute("type", impns);
assertTrue("Could not find type attribute in impns.", att != null);
+ assertFalse(att.isSpecified());
}
} catch (JDOMException e) {
e.printStackTrace();
View
139 test/src/java/org/jdom2/test/cases/output/AbstractTestOutputter.java
@@ -286,12 +286,15 @@ protected final String expect(String expect) {
protected static final Format fraw = Format.getRawFormat();
protected static final Format fcompact = Format.getCompactFormat();
protected static final Format fpretty = Format.getPrettyFormat();
+ protected static final Format ftso = Format.getPrettyFormat();
protected static final Format ftfw = Format.getPrettyFormat();
static {
fraw.setLineSeparator("\n");
fcompact.setLineSeparator("\n");
fpretty.setLineSeparator("\n");
+ ftso.setLineSeparator("\n");
+ ftso.setSpecifiedAttributesOnly(true);
ftfw.setLineSeparator("\n");
ftfw.setTextMode(TextMode.TRIM_FULL_WHITE);
}
@@ -303,6 +306,7 @@ public void testTextEmpty() {
assertEquals("", outputString(fraw, content));
assertEquals("", outputString(fcompact, content));
assertEquals("", outputString(fpretty, content));
+ assertEquals("", outputString(ftso, content));
assertEquals("", outputString(ftfw, content));
}
@@ -316,6 +320,8 @@ public void testTextWhitespace() {
assertEquals("",
outputString(fpretty, content));
assertEquals("",
+ outputString(ftso, content));
+ assertEquals("",
outputString(ftfw, content));
}
@@ -328,6 +334,8 @@ public void testTextWithText() {
outputString(fcompact, content));
assertEquals(expect("&amp;"),
outputString(fpretty, content));
+ assertEquals(expect("&amp;"),
+ outputString(ftso, content));
assertEquals(expect(" \r &amp; \n \t "),
outputString(ftfw, content));
}
@@ -342,6 +350,8 @@ public void testCDATAEmpty() {
assertEquals("",
outputString(fpretty, content));
assertEquals("",
+ outputString(ftso, content));
+ assertEquals("",
outputString(ftfw, content));
}
@@ -355,6 +365,8 @@ public void testCDATAWhitespace() {
assertEquals("",
outputString(fpretty, content));
assertEquals("",
+ outputString(ftso, content));
+ assertEquals("",
outputString(ftfw, content));
}
@@ -367,6 +379,8 @@ public void testCDATAWithText() {
outputString(fcompact, content));
assertEquals("<![CDATA[&]]>",
outputString(fpretty, content));
+ assertEquals("<![CDATA[&]]>",
+ outputString(ftso, content));
assertEquals("<![CDATA[ \r & \n \t ]]>",
outputString(ftfw, content));
}
@@ -381,6 +395,8 @@ public void testEntityRef() {
assertEquals("&ref;",
outputString(fpretty, content));
assertEquals("&ref;",
+ outputString(ftso, content));
+ assertEquals("&ref;",
outputString(ftfw, content));
}
@@ -408,6 +424,8 @@ public void testProcessingInstructionTargetWithData() {
assertEquals("<?target data?>",
outputString(fpretty, content));
assertEquals("<?target data?>",
+ outputString(ftso, content));
+ assertEquals("<?target data?>",
outputString(ftfw, content));
}
@@ -421,6 +439,8 @@ public void testComment() {
assertEquals("<!--comment-->",
outputString(fpretty, content));
assertEquals("<!--comment-->",
+ outputString(ftso, content));
+ assertEquals("<!--comment-->",
outputString(ftfw, content));
}
@@ -435,6 +455,8 @@ public void testDocTypeSimple() {
assertEquals("<!DOCTYPE root>",
outputString(fpretty, content));
assertEquals("<!DOCTYPE root>",
+ outputString(ftso, content));
+ assertEquals("<!DOCTYPE root>",
outputString(ftfw, content));
}
@@ -449,6 +471,8 @@ public void testDocTypeSimpleISS() {
assertEquals("<!DOCTYPE root [\n<!ENTITY name \"value\">]>",
outputString(fpretty, content));
assertEquals("<!DOCTYPE root [\n<!ENTITY name \"value\">]>",
+ outputString(ftso, content));
+ assertEquals("<!DOCTYPE root [\n<!ENTITY name \"value\">]>",
outputString(ftfw, content));
}
@@ -462,6 +486,8 @@ public void testDocTypeSystemID() {
assertEquals("<!DOCTYPE root SYSTEM \"sysid\">",
outputString(fpretty, content));
assertEquals("<!DOCTYPE root SYSTEM \"sysid\">",
+ outputString(ftso, content));
+ assertEquals("<!DOCTYPE root SYSTEM \"sysid\">",
outputString(ftfw, content));
}
@@ -476,6 +502,8 @@ public void testDocTypeSystemIDISS() {
assertEquals("<!DOCTYPE root SYSTEM \"sysid\" [\ninternal]>",
outputString(fpretty, content));
assertEquals("<!DOCTYPE root SYSTEM \"sysid\" [\ninternal]>",
+ outputString(ftso, content));
+ assertEquals("<!DOCTYPE root SYSTEM \"sysid\" [\ninternal]>",
outputString(ftfw, content));
}
@@ -489,6 +517,8 @@ public void testDocTypePublicSystemID() {
assertEquals("<!DOCTYPE root PUBLIC \"pubid\" \"sysid\">",
outputString(fpretty, content));
assertEquals("<!DOCTYPE root PUBLIC \"pubid\" \"sysid\">",
+ outputString(ftso, content));
+ assertEquals("<!DOCTYPE root PUBLIC \"pubid\" \"sysid\">",
outputString(ftfw, content));
}
@@ -503,6 +533,8 @@ public void testDocTypePublicSystemIDISS() {
assertEquals("<!DOCTYPE root PUBLIC \"pubid\" \"sysid\" [\ninternal]>",
outputString(fpretty, content));
assertEquals("<!DOCTYPE root PUBLIC \"pubid\" \"sysid\" [\ninternal]>",
+ outputString(ftso, content));
+ assertEquals("<!DOCTYPE root PUBLIC \"pubid\" \"sysid\" [\ninternal]>",
outputString(ftfw, content));
}
@@ -524,6 +556,8 @@ public void testMultiWhiteText() {
assertEquals(expect("<root/>"),
outputString(fpretty, root));
assertEquals(expect("<root/>"),
+ outputString(ftso, root));
+ assertEquals(expect("<root/>"),
outputString(ftfw, root));
}
@@ -544,6 +578,8 @@ public void testMultiText() {
outputString(fcompact, root));
assertEquals(expect("<root>X</root>"),
outputString(fpretty, root));
+ assertEquals(expect("<root>X</root>"),
+ outputString(ftso, root));
assertEquals(expect("<root><![CDATA[ ]]> X \n \n \t </root>"),
outputString(ftfw, root));
}
@@ -557,6 +593,8 @@ public void testDocumentSimple() {
outputString(fcompact, content));
assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
outputString(fpretty, content));
+ assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
+ outputString(ftso, content));
assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
outputString(ftfw, content));
}
@@ -571,6 +609,8 @@ public void testDocumentDocType() {
outputString(fcompact, content));
assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE root>\n",
outputString(fpretty, content));
+ assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE root>\n",
+ outputString(ftso, content));
assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE root>\n",
outputString(ftfw, content));
}
@@ -585,6 +625,8 @@ public void testDocumentComment() {
outputString(fcompact, content));
assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--comment-->\n",
outputString(fpretty, content));
+ assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--comment-->\n",
+ outputString(ftso, content));
assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--comment-->\n",
outputString(ftfw, content));
}
@@ -617,7 +659,7 @@ public void testXXX() {
@Test
public void testOutputText() {
- checkOutput(new Text(" hello there "), " hello there ", "hello there", "hello there", " hello there ");
+ checkOutput(new Text(" hello there "), " hello there ", "hello there", "hello there", "hello there", " hello there ");
}
@Test
@@ -628,55 +670,77 @@ public void testOutputCDATA() {
String prettydata = "<![CDATA[hello there bozo !]]>";
String trimdata = "<![CDATA[ hello there bozo ! ]]>";
- checkOutput(new CDATA(indata), rawcdata, compdata, prettydata, trimdata);
+ checkOutput(new CDATA(indata), rawcdata, compdata, prettydata, prettydata, trimdata);
}
@Test
public void testOutputComment() {
String incomment = " hello there bozo ! ";
String outcomment = "<!--" + incomment + "-->";
- checkOutput(new Comment(incomment), outcomment, outcomment, outcomment, outcomment);
+ checkOutput(new Comment(incomment), outcomment, outcomment, outcomment, outcomment, outcomment);
}
@Test
public void testOutputProcessingInstructionSimple() {
ProcessingInstruction inpi = new ProcessingInstruction("jdomtest", "");
String outpi = "<?jdomtest?>";
- checkOutput(inpi, outpi, outpi, outpi, outpi);
+ checkOutput(inpi, outpi, outpi, outpi, outpi, outpi);
}
@Test
public void testOutputProcessingInstructionData() {
String pi = " hello there ";
ProcessingInstruction inpi = new ProcessingInstruction("jdomtest", pi);
String outpi = "<?jdomtest " + pi + "?>";
- checkOutput(inpi, outpi, outpi, outpi, outpi);
+ checkOutput(inpi, outpi, outpi, outpi, outpi, outpi);
}
@Test
public void testOutputEntityRef() {
checkOutput(new EntityRef("name", "publicID", "systemID"),
- "&name;", "&name;", "&name;", "&name;");
+ "&name;", "&name;", "&name;", "&name;", "&name;");
}
@Test
public void testOutputElementSimple() {
String txt = "<root/>";
- checkOutput(new Element("root"), txt, txt, txt, txt);
+ checkOutput(new Element("root"), txt, txt, txt, txt, txt);
}
@Test
public void testOutputElementAttribute() {
String txt = "<root att=\"val\" />";
- checkOutput(new Element("root").setAttribute("att", "val"), txt, txt, txt, txt);
+ checkOutput(new Element("root").setAttribute("att", "val"), txt, txt, txt, txt, txt);
+ }
+
+ @Test
+ public void testOutputElementAttributeNotSpecifiedA() {
+ String txt = "<root att=\"val\" />";
+ final Element root = new Element("root");
+ final Attribute att = new Attribute("att", "val");
+ root.setAttribute(att);
+ att.setSpecified(false);
+ checkOutput(root, txt, txt, txt, "<root />", txt);
+ }
+
+ @Test
+ public void testOutputElementAttributeNotSpecifiedB() {
+ String txt = "<root atta=\"val\" attb=\"attb\" />";
+ final Element root = new Element("root");
+ final Attribute atta = new Attribute("atta", "val");
+ final Attribute attb = new Attribute("attb", "attb");
+ root.setAttribute(atta);
+ root.setAttribute(attb);
+ atta.setSpecified(false);
+ checkOutput(root, txt, txt, txt, "<root attb=\"attb\" />", txt);
}
@Test
public void testOutputElementCDATA() {
String txt = "<root><![CDATA[xx]]></root>";
Element root = new Element("root");
root.addContent(new CDATA("xx"));
- checkOutput(root, txt, txt, txt, txt);
+ checkOutput(root, txt, txt, txt, txt, txt);
}
@Test
@@ -688,7 +752,7 @@ public void setup(Format fmt) {
fmt.setExpandEmptyElements(true);
}
};
- checkOutput(new Element("root"), setup, txt, txt, txt, txt);
+ checkOutput(new Element("root"), setup, txt, txt, txt, txt, txt);
}
@Test
@@ -702,7 +766,7 @@ public void testOutputElementPreserveSpace() {
child.addContent("abc");
root.addContent(child);
root.addContent(" ");
- checkOutput(root, txt, txt, txt, txt);
+ checkOutput(root, txt, txt, txt, txt, txt);
}
@@ -722,6 +786,7 @@ public void testOutputElementMultiText() {
"<root><![CDATA[ ]]> xx yy zz ww&amp;vv </root>",
"<root>xx yy zz ww&amp;vv</root>",
"<root>xx yy zz ww&amp;vv</root>",
+ "<root>xx yy zz ww&amp;vv</root>",
// This should be changed with issue #31.
// The real value should have one additional
// space at the beginning and two at the end
@@ -746,6 +811,7 @@ public void testOutputElementMultiAllWhite() {
"<root><![CDATA[ ]]> \n \n \t </root>",
"<root />",
"<root />",
+ "<root />",
"<root />");
}
@@ -770,6 +836,7 @@ public void setup(Format fmt) {
"<root><![CDATA[ ]]> \n \n \t </root>",
"<root></root>",
"<root></root>",
+ "<root></root>",
"<root></root>");
}
@@ -797,6 +864,7 @@ public void setup(Format fmt) {
"<root><![CDATA[ ]]> \n \n <!--Boo--> \t </root>",
"<root><!--Boo--></root>",
"<root>\n <!--Boo-->\n</root>",
+ "<root>\n <!--Boo-->\n</root>",
"<root>\n <!--Boo-->\n</root>");
}
@@ -818,6 +886,7 @@ public void setup(Format fmt) {
"<root><!--Boo--> <![CDATA[A]]></root>",
"<root><!--Boo--><![CDATA[A]]></root>",
"<root>\n <!--Boo-->\n <![CDATA[A]]>\n</root>",
+ "<root>\n <!--Boo-->\n <![CDATA[A]]>\n</root>",
"<root>\n <!--Boo-->\n <![CDATA[A]]>\n</root>");
}
@@ -839,6 +908,7 @@ public void setup(Format fmt) {
"<root><!--Boo--> &aer;</root>",
"<root><!--Boo-->&aer;</root>",
"<root>\n <!--Boo-->\n &aer;\n</root>",
+ "<root>\n <!--Boo-->\n &aer;\n</root>",
"<root>\n <!--Boo-->\n &aer;\n</root>");
}
@@ -860,6 +930,7 @@ public void setup(Format fmt) {
"<root><!--Boo--> txt</root>",
"<root><!--Boo-->txt</root>",
"<root>\n <!--Boo-->\n txt\n</root>",
+ "<root>\n <!--Boo-->\n txt\n</root>",
"<root>\n <!--Boo-->\n txt\n</root>");
}
@@ -884,6 +955,7 @@ public void setup(Format fmt) {
"<root><!--Boo--> txt</root>",
"<root><!--Boo-->txt</root>",
"<root>\n <!--Boo-->\n txt\n</root>",
+ "<root>\n <!--Boo-->\n txt\n</root>",
"<root>\n <!--Boo-->\n txt\n</root>");
}
@@ -898,6 +970,7 @@ public void testOutputElementMultiEntityLeftRight() {
"<root>&erl; &err;</root>",
"<root>&erl; &err;</root>",
"<root>&erl; &err;</root>",
+ "<root>&erl; &err;</root>",
"<root>&erl; &err;</root>");
}
@@ -911,6 +984,7 @@ public void testOutputElementMultiTrimLeftRight() {
"<root> tl mid tr </root>",
"<root>tl mid tr</root>",
"<root>tl mid tr</root>",
+ "<root>tl mid tr</root>",
"<root> tl mid tr </root>");
}
@@ -924,6 +998,7 @@ public void testOutputElementMultiCDATALeftRight() {
"<root><![CDATA[ tl ]]> mid <![CDATA[ tr ]]></root>",
"<root><![CDATA[tl]]> mid <![CDATA[tr]]></root>",
"<root><![CDATA[tl ]]> mid <![CDATA[ tr]]></root>",
+ "<root><![CDATA[tl ]]> mid <![CDATA[ tr]]></root>",
"<root><![CDATA[ tl ]]> mid <![CDATA[ tr ]]></root>");
}
@@ -937,38 +1012,39 @@ public void testOutputElementNamespaces() {
emt.setAttribute(new Attribute("att", "val", ans));
emt.addNamespaceDeclaration(Namespace.getNamespace("two", "two"));
checkOutput(emt,
- txt, txt, txt, txt);
+ txt, txt,txt, txt, txt);
}
@Test
public void testOutputDocTypeSimple() {
- checkOutput(new DocType("root"), "<!DOCTYPE root>", "<!DOCTYPE root>", "<!DOCTYPE root>", "<!DOCTYPE root>");
+ checkOutput(new DocType("root"), "<!DOCTYPE root>", "<!DOCTYPE root>", "<!DOCTYPE root>",
+ "<!DOCTYPE root>", "<!DOCTYPE root>");
}
@Test
public void testOutputDocTypeInternalSubset() {
String dec = "<!DOCTYPE root [\ninternal]>";
DocType dt = new DocType("root");
dt.setInternalSubset("internal");
- checkOutput(dt, dec, dec, dec, dec);
+ checkOutput(dt, dec, dec, dec, dec, dec);
}
@Test
public void testOutputDocTypeSystem() {
String dec = "<!DOCTYPE root SYSTEM \"systemID\">";
- checkOutput(new DocType("root", "systemID"), dec, dec, dec, dec);
+ checkOutput(new DocType("root", "systemID"), dec, dec, dec, dec, dec);
}
@Test
public void testOutputDocTypePublic() {
String dec = "<!DOCTYPE root PUBLIC \"publicID\">";
- checkOutput(new DocType("root", "publicID", null), dec, dec, dec, dec);
+ checkOutput(new DocType("root", "publicID", null), dec, dec, dec, dec, dec);
}
@Test
public void testOutputDocTypePublicSystem() {
String dec = "<!DOCTYPE root PUBLIC \"publicID\" \"systemID\">";
- checkOutput(new DocType("root", "publicID", "systemID"), dec, dec, dec, dec);
+ checkOutput(new DocType("root", "publicID", "systemID"), dec, dec, dec, dec, dec);
}
@Test
@@ -981,6 +1057,7 @@ public void testOutputDocumentSimple() {
xmldec + "\n" + rtdec + "\n",
xmldec + "\n" + rtdec + "\n",
xmldec + "\n" + rtdec + "\n",
+ xmldec + "\n" + rtdec + "\n",
xmldec + "\n" + rtdec + "\n");
}
@@ -1000,6 +1077,7 @@ public void setup(Format fmt) {
xmldec + "\n" + rtdec + "\n",
xmldec + "\n" + rtdec + "\n",
xmldec + "\n" + rtdec + "\n",
+ xmldec + "\n" + rtdec + "\n",
xmldec + "\n" + rtdec + "\n");
}
@@ -1018,6 +1096,7 @@ public void setup(Format fmt) {
rtdec + "\n",
rtdec + "\n",
rtdec + "\n",
+ rtdec + "\n",
rtdec + "\n");
}
@@ -1043,6 +1122,7 @@ public void testOutputDocumentFull() {
xmldec + lf + dtdec + commentdec + pidec + rtdec + lf,
xmldec + lf + dtdec + commentdec + pidec + rtdec + lf,
xmldec + lf + dtdec + dlf + commentdec + dlf + pidec + dlf + rtdec + lf,
+ xmldec + lf + dtdec + dlf + commentdec + dlf + pidec + dlf + rtdec + lf,
xmldec + lf + dtdec + dlf + commentdec + dlf + pidec + dlf + rtdec + lf);
}
@@ -1108,34 +1188,34 @@ public void testDeepNesting() {
pretty.append("</root>");
pretty.append(lf);
- checkOutput(doc, raw.toString(), raw.toString(), pretty.toString(), pretty.toString());
+ checkOutput(doc, raw.toString(), raw.toString(), pretty.toString(), pretty.toString(), pretty.toString());
}
@Test
public void testOutputElementContent() {
Element root = new Element("root");
root.addContent(new Element("child"));
- checkOutput(root, "outputElementContent", Element.class, null, "<child />", "<child />", "<child />", "<child />");
+ checkOutput(root, "outputElementContent", Element.class, null, "<child />", "<child />", "<child />", "<child />", "<child />");
}
@Test
public void testOutputList() {
List<Object> c = new ArrayList<Object>();
c.add(new Element("root"));
- checkOutput(c, "output", List.class, null, "<root />", "<root />", "<root />", "<root />");
+ checkOutput(c, "output", List.class, null, "<root />", "<root />", "<root />", "<root />", "<root />");
}
- protected void checkOutput(Object content, String raw, String compact, String pretty, String trimfw) {
+ protected void checkOutput(Object content, String raw, String compact, String pretty, String tso, String trimfw) {
Class<?> clazz = content.getClass();
- checkOutput(content, "output", clazz, null, raw, compact, pretty, trimfw);
+ checkOutput(content, "output", clazz, null, raw, compact, pretty, tso, trimfw);
}
- protected void checkOutput(Object content, FormatSetup setup, String raw, String compact, String pretty, String trimfw) {
+ protected void checkOutput(Object content, FormatSetup setup, String raw, String compact, String pretty, String tso, String trimfw) {
Class<?> clazz = content.getClass();
- checkOutput(content, "output", clazz, setup, raw, compact, pretty, trimfw);
+ checkOutput(content, "output", clazz, setup, raw, compact, pretty, tso, trimfw);
}
/**
@@ -1158,24 +1238,27 @@ protected void checkOutput(Object content, FormatSetup setup, String raw, String
* @param trimfw What we expect the content to look like with the TRIM_FULL_WHITE format
*/
protected void checkOutput(Object content, String methodprefix, Class<?> clazz,
- FormatSetup setup, String raw, String compact, String pretty, String trimfw) {
+ FormatSetup setup, String raw, String compact, String pretty, String tso, String trimfw) {
Method meth = getMyMethod(methodprefix + "String", Format.class, clazz);
if (meth == null) {
return;
}
- String[] descn = new String[] {"Raw", "Compact", "Pretty", "TrimFullWhite"};
+ String[] descn = new String[] {"Raw", "Compact", "Pretty", "PrettySpecifiedOnly", "TrimFullWhite"};
Format ftrimfw = Format.getPrettyFormat();
ftrimfw.setTextMode(TextMode.TRIM_FULL_WHITE);
+ Format fattspec = Format.getPrettyFormat();
+ fattspec.setSpecifiedAttributesOnly(true);
Format[] formats = new Format[] {
getFormat(setup, Format.getRawFormat()),
getFormat(setup, Format.getCompactFormat()),
getFormat(setup, Format.getPrettyFormat()),
+ getFormat(setup, fattspec),
getFormat(setup, ftrimfw)};
- String[] result = new String[] {raw, compact, pretty, trimfw};
+ String[] result = new String[] {raw, compact, pretty, tso, trimfw};
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < 5; i++) {
String mstring;
try {
View
10 test/src/java/org/jdom2/test/cases/output/TestFormat.java
@@ -103,6 +103,16 @@ public void testOmitDeclaration() {
}
@Test
+ public void testSpecifiedAttributesOnly() {
+ assertFalse(Format.getPrettyFormat().isSpecifiedAttributesOnly());
+ assertFalse(Format.getCompactFormat().isSpecifiedAttributesOnly());
+ Format mine = Format.getRawFormat();
+ assertFalse(mine.isSpecifiedAttributesOnly());
+ mine.setSpecifiedAttributesOnly(true);
+ assertTrue (mine.isSpecifiedAttributesOnly());
+ }
+
+ @Test
public void testExpandEmptyElements() {
assertFalse(Format.getPrettyFormat().getExpandEmptyElements());
assertFalse(Format.getCompactFormat().getExpandEmptyElements());
View
1 test/src/java/org/jdom2/test/cases/output/TestStAXStreamOutputter.java
@@ -309,6 +309,7 @@ public void setup(Format fmt) {
rtdec,
rtdec,
rtdec,
+ rtdec,
rtdec);
}
View
7 test/src/java/org/jdom2/test/cases/output/TestXMLOutputter.java
@@ -397,7 +397,8 @@ public void testOutputElementIgnoreTrAXEscapingPIs() {
checkOutput(root,
expect,
excompact,
- expretty,
+ expretty,
+ expretty,
extfw);
}
@@ -459,9 +460,9 @@ public void testCRNLEscaping() {
*/
@Override
protected void checkOutput(Object content, String methodprefix, Class<?> clazz,
- FormatSetup setup, String raw, String compact, String pretty, String trimfw) {
+ FormatSetup setup, String raw, String compact, String pretty, String tso, String trimfw) {
- super.checkOutput(content, methodprefix, clazz, setup, raw, compact, pretty, trimfw);
+ super.checkOutput(content, methodprefix, clazz, setup, raw, compact, pretty, tso, trimfw);
Method mstring = getMethod(methodprefix + "String", clazz);
Method mstream = getMethod(methodprefix, clazz, OutputStream.class);

0 comments on commit a854727

Please sign in to comment.