Ranides Atterwim пре 10 година
родитељ
комит
e4f9de4594

+ 3 - 16
assira/src/main/java/net/ranides/assira/xml/XMLElement.java

@@ -88,6 +88,8 @@ public interface XMLElement extends Wrapper<String> {
     
     
     
+    XMLElement normalize();
+    
     XMLElement before(XMLElement element);
     
     XMLElement after(XMLElement element);
@@ -117,20 +119,5 @@ public interface XMLElement extends Wrapper<String> {
     <R extends Result> R apply(Transformer transformer, R output) throws XMLException;
     
     <R extends Result> R apply(XMLElement transformer, R output) throws XMLException;
-    
-    // @todo (assira #0) xml: save/format options
-    // może po prostu zrobimy jakąś klasę XMLWriter, która ma
-    //      1. różne konstruktory (file, stream, writer)
-    //      2. pobiera opcje
-    //      3. ma metodę "write(XMLElement)" 
-    //      4. a stąd wylatują metody #save
-    
-    String save();
-    
-    void save(Writer writer) throws IOException;
-    
-    void save(OutputStream stream) throws IOException;
-    
-    void save(File file) throws IOException;
-        
+
 }

+ 3 - 1
assira/src/main/java/net/ranides/assira/xml/XMLElements.java

@@ -52,7 +52,7 @@ public interface XMLElements extends Iterable<XMLElement> {
     
     CQuery<XMLElement> stream();
     
-    void each(Consumer<? super XMLElement> consumer);
+    XMLElements each(Consumer<? super XMLElement> consumer);
     
     XMLElement first();
     
@@ -127,5 +127,7 @@ public interface XMLElements extends Iterable<XMLElement> {
     XMLElements parent(String selector);
     
     XMLElements parent(Predicate<XMLContext> selector);
+    
+    // @todo (assira #0) xml: XMLElements should support "bulk version" of most methods from XMLElement
         
 }

+ 138 - 0
assira/src/main/java/net/ranides/assira/xml/XMLOptions.java

@@ -0,0 +1,138 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+
+package net.ranides.assira.xml;
+
+import javax.xml.transform.OutputKeys;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public enum XMLOptions {
+
+    /**
+     * @see OutputKeys#CDATA_SECTION_ELEMENTS
+     */
+    CDATA_SECTION_ELEMENTS(OutputKeys.CDATA_SECTION_ELEMENTS),
+    
+    /**
+     * @see OutputKeys#DOCTYPE_PUBLIC
+     */
+    DOCTYPE_PUBLIC(OutputKeys.DOCTYPE_PUBLIC),
+    
+    /**
+     * @see OutputKeys#DOCTYPE_SYSTEM
+     */
+    DOCTYPE_SYSTEM(OutputKeys.DOCTYPE_SYSTEM),
+    
+    /**
+     * @see OutputKeys#ENCODING
+     */
+    ENCODING(OutputKeys.ENCODING),
+    
+    /**
+     * @see OutputKeys#INDENT
+     */
+    INDENT(OutputKeys.INDENT),
+    
+    /**
+     * The non-standard property key to use to set the
+     * number of whitepaces to indent by, per indentation level,
+     * if indent="yes".
+     */
+    INDENT_AMOUNT(true, "indent-amount"),
+
+    /**
+     * The non-standard property key to use to set the
+     * line separator if indent="yes"
+     */
+    INDENT_ENDL(true, "line-separator"),
+
+    /**
+     * @see OutputKeys#MEDIA_TYPE
+     */
+    MEDIA_TYPE(OutputKeys.MEDIA_TYPE),
+    
+    /**
+     * @see OutputKeys#METHOD
+     */
+    METHOD(OutputKeys.METHOD),
+    
+    /**
+     * @see OutputKeys#OMIT_XML_DECLARATION
+     */
+    OMIT_XML_DECLARATION(OutputKeys.OMIT_XML_DECLARATION),
+    
+    /**
+     * This non-standard property key is used to set a value of "yes" 
+     * if the META tag should be omitted where it would otherwise be supplied.
+     */
+    OMIT_META_TAG(true, "omit-meta-tag"),
+    
+    /**
+     * @see OutputKeys#STANDALONE
+     */
+    STANDALONE(OutputKeys.STANDALONE),
+    
+    /**
+     * This non-standard, Oracle-impl only property key is used as if OutputKeys.STANDALONE is specified but
+     * without writing it out in the declaration; It can be used to reverse the change by Xalan patch 1495.
+     * Since Xalan patch 1495 can cause incompatible behavior, this property is add for application to neutralize
+     * the effect of Xalan patch 1495
+     */
+    STANDALONE_DEFAULT("http://www.oracle.com/xml/is-standalone"),
+    
+    /**
+     * @see OutputKeys#VERSION
+     */
+    VERSION(OutputKeys.VERSION),
+    
+    /** 
+     * This non-standard property key is used to set the name of the fully qualified
+     * Java class that implements the ContentHandler interface.
+     * 
+     * Fully qualified name of class with a default constructor that
+     * implements the ContentHandler interface, where the result tree events
+     * will be sent to.
+     */
+    CONTENT_HANDLER(true, "content-handler"),
+
+    /**
+     * This non-standard property key is used to specify 
+     * the name of the property file that specifies character to entity reference mappings.
+     */
+    ENTITIES(true, "entities"),
+
+    /**
+     * This non-standard property key is used to set a value of "yes" 
+     * if the href values for HTML serialization should use %xx escaping. 
+     */
+    USE_URL_ESCAPING(true, "use-url-escaping"),
+    
+    ;
+        
+    /**
+     * @see com.sun.org.apache.xml.internal.serializer.OutputPropertiesFactory
+     */
+    private static final String NS = "{http://xml.apache.org/xalan}";
+    
+    private final String key;
+
+    private XMLOptions(String key) {
+        this.key = key;
+    }
+    
+    private XMLOptions(boolean prefix, String key) {
+        this.key = prefix ? NS + key : key;
+    }
+    
+    public String key() {
+        return key;
+    }
+
+}

+ 2 - 6
assira/src/main/java/net/ranides/assira/xml/XMLSelector.java

@@ -18,10 +18,10 @@ import net.ranides.assira.xml.impl.W3Selectors;
 public interface XMLSelector extends Function<XMLContext, XMLElements>, Predicate<XMLContext> {
 
     @Override
-    public XMLElements apply(XMLContext context);
+    XMLElements apply(XMLContext context);
 
     @Override
-    public boolean test(XMLContext context);
+    boolean test(XMLContext context);
 
     static XMLSelector compile(String selector) {
         return W3Selectors.compile(selector);
@@ -35,8 +35,4 @@ public interface XMLSelector extends Function<XMLContext, XMLElements>, Predicat
         return W3Selectors.xpath(selector);
     }
     
-    static XMLSelector text(String selector) {
-        return W3Selectors.text(selector);
-    }
-
 }

+ 104 - 0
assira/src/main/java/net/ranides/assira/xml/XMLWriter.java

@@ -0,0 +1,104 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+
+package net.ranides.assira.xml;
+
+import com.sun.org.apache.xml.internal.serialize.OutputFormat;
+import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.stream.StreamResult;
+import net.ranides.assira.text.Charsets;
+import org.w3c.dom.Node;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class XMLWriter implements AutoCloseable, Closeable {
+    
+    
+
+    
+    private final Writer writer;
+    
+    private final Transformer transformer;
+    
+    public XMLWriter() {
+        this((Writer)null);
+    }
+    
+    public XMLWriter(File file) throws FileNotFoundException {
+        this(new FileOutputStream(file));
+    }
+
+    public XMLWriter(OutputStream ostream) {
+        this(new OutputStreamWriter(ostream, Charsets.UTF8));
+    }
+    
+    public XMLWriter(Writer writer) {
+        try {
+            this.writer = writer;
+            this.transformer = TransformerFactory.newInstance().newTransformer();
+        } catch(TransformerFactoryConfigurationError | TransformerConfigurationException cause) {
+            throw new XMLException(cause);
+        }
+    }
+    
+    public XMLWriter write(XMLElement document) throws IOException {
+        document.apply(transformer, new StreamResult(writer));
+        return this;
+    }
+    
+    public String toString(XMLElement document) {
+        StringWriter out = new StringWriter();
+        document.apply(transformer, new StreamResult(out));
+        return out.toString();
+    }
+
+
+    @Override
+    public void close() throws IOException {
+        if(null != writer) {
+            writer.close();
+        }
+    }
+    
+    public XMLWriter param(XMLOptions param, String value) {
+        return param(param.key(), value);
+    }
+    
+    public XMLWriter param(String param, String value) {
+        transformer.setOutputProperty(param, value);
+        return this;
+    }
+    
+    public XMLWriter indent(int size) {
+        if(size == 0) {
+            param(XMLOptions.INDENT, "no");
+        } else {
+            // we want to put "root tag" in new line after <?xml declaration?>
+            // DOCTYPE is workaround for strage bug in JDK formatter
+            param(XMLOptions.DOCTYPE_PUBLIC, ""); 
+            param(XMLOptions.INDENT, "yes");
+            param(XMLOptions.INDENT_AMOUNT, String.valueOf(size));
+        }
+        return this;
+    }
+
+}

+ 2 - 1
assira/src/main/java/net/ranides/assira/xml/impl/CElements.java

@@ -129,8 +129,9 @@ public class CElements implements XMLElements {
     }
 
     @Override
-    public void each(Consumer<? super XMLElement> consumer) {
+    public XMLElements each(Consumer<? super XMLElement> consumer) {
         stream.each(consumer);
+        return this;
     }
 
     @Override

+ 6 - 51
assira/src/main/java/net/ranides/assira/xml/impl/W3Element.java

@@ -6,13 +6,6 @@
  */
 package net.ranides.assira.xml.impl;
 
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.StringWriter;
-import java.io.Writer;
 import java.util.AbstractList;
 import java.util.Collection;
 import java.util.function.Function;
@@ -20,7 +13,6 @@ import java.util.function.Predicate;
 
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
 import javax.xml.transform.Result;
 import javax.xml.transform.Transformer;
 import javax.xml.transform.TransformerConfigurationException;
@@ -29,7 +21,6 @@ import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.TransformerFactoryConfigurationError;
 import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
@@ -42,7 +33,6 @@ import net.ranides.assira.collection.query.CQuery;
 import net.ranides.assira.collection.query.CQueryBuilder;
 import net.ranides.assira.generic.ValueUtils;
 import net.ranides.assira.reflection.IClass;
-import net.ranides.assira.text.Charsets;
 import net.ranides.assira.xml.*;
 
 /**
@@ -338,6 +328,12 @@ public class W3Element extends CElement implements XMLElement {
         return this;
     }
 
+    @Override
+    public XMLElement normalize() {
+        node.normalize();
+        return this;
+    }
+
     @Override
     public XMLElement apply(XMLElement transformer) throws XMLException {
         return apply($tr(transformer));
@@ -370,39 +366,6 @@ public class W3Element extends CElement implements XMLElement {
         }
     }
 
-    @Override
-    public String save() {
-        Transformer save = $tr();
-        save.setOutputProperty(OutputKeys.INDENT, "yes");
-        save.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
-        
-        return apply(save, new StreamResult(new StringWriter())).getWriter().toString();
-    }
-
-    @Override
-    public void save(Writer writer) {
-        Transformer save = $tr();
-        save.setOutputProperty(OutputKeys.INDENT, "yes");
-        save.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
-        
-        apply(save, new StreamResult(writer));
-    }
-
-    @Override
-    public void save(OutputStream stream) throws IOException {
-        try(OutputStreamWriter writer = new OutputStreamWriter(stream, Charsets.UTF8)) {
-            save(writer);
-        }
-    }
-
-    @Override
-    public void save(File file) throws IOException {
-        try (FileOutputStream stream = new FileOutputStream(file)) {
-            save(stream);
-        }
-    }
-   
-
     private static Transformer $tr(XMLElement transformer) {
         try {
             return TransformerFactory.newInstance().newTransformer(new DOMSource(transformer.node()));
@@ -411,14 +374,6 @@ public class W3Element extends CElement implements XMLElement {
         }
     }
     
-    private static Transformer $tr() {
-        try {
-            return TransformerFactory.newInstance().newTransformer();
-        } catch(TransformerFactoryConfigurationError | TransformerConfigurationException cause) {
-            throw new XMLException(cause);
-        }
-    }
-    
     static XMLElement $element(Node node) {
         return null == node ? null : new W3Element(node);
     }

+ 0 - 30
assira/src/main/java/net/ranides/assira/xml/impl/W3Selectors.java

@@ -57,10 +57,6 @@ public final class W3Selectors {
         return new XPath(selector);
     }
     
-    public static XMLSelector text(String selector) {
-        return new Text(selector);
-    }
-
     private static boolean isxpath(String selector) {
         return selector.startsWith("? ");
     }
@@ -81,32 +77,6 @@ public final class W3Selectors {
         return (cn=='-') || (cn>='a' && cn<='z') || (cn>='A' && cn<='Z');
     }
     
-    static final class Text implements XMLSelector {
-        
-        private final Predicate<String> predicate;
-
-        public Text(String regex) {
-            Pattern re = Pattern.compile(regex);
-            this.predicate = v -> re.matcher(v).matches();
-        }
-        
-        public Text(Predicate<String> predicate) {
-            this.predicate = predicate;
-        }
-
-        @Override
-        public XMLElements apply(XMLContext c) {
-            // @todo (assira #2.2) xml: selectors: text: scan
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public boolean test(XMLContext c) {
-            return predicate.test(c.node().text());
-        }
-    
-    }
-    
     static final class Name implements XMLSelector {
         
         private final String name;

+ 0 - 5
assira/src/test/java/net/ranides/assira/xml/XMLQueryTest.java

@@ -129,11 +129,6 @@ public class XMLQueryTest {
         assertEquals(Arrays.asList(), attr1.parent().children().names().list());
         assertEquals(Arrays.asList("filter","output"), attr1.parent().attrs().names().list());
         assertEquals(Arrays.asList("output", "rule", "mappings", "repository", "#document"), attr1.parents().names().list());
-        
-        attr1.set("hello world");
-
-        System.out.printf("%s%n", attr1.parents("mappings").first().save());
-        
     }
     
 }

+ 63 - 0
assira/src/test/java/net/ranides/assira/xml/XMLWriterTest.java

@@ -0,0 +1,63 @@
+/*
+ * @author Ranides Atterwim <ranides@gmail.com>
+ * @copyright Ranides Atterwim
+ * @license WTFPL
+ * @url http://ranides.net/projects/assira
+ */
+package net.ranides.assira.xml;
+
+import java.io.File;
+import java.io.IOException;
+import net.ranides.assira.io.StringOutput;
+import net.ranides.assira.text.Charsets;
+import static net.ranides.assira.xml.XMLQuery.$;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+public class XMLWriterTest {
+    
+    private static final File INPUT = new File("src/test/resources/eclipse/artifacts.xml");
+
+    @Test
+    public void testWriter() throws IOException {
+        XMLElement doc = load(INPUT).find("? //repository/mappings").first();
+        doc.find("? //rule[2]/@output").first().content("under");
+        
+        String out1 = new XMLWriter().toString(doc);
+        
+        String out2 = new XMLWriter()
+            .param(XMLOptions.DOCTYPE_PUBLIC, "yes")
+            .param(XMLOptions.INDENT, "yes")
+            .param(XMLOptions.INDENT_AMOUNT, "2")
+            .toString(doc);
+        
+        StringOutput out3 = new StringOutput();
+        new XMLWriter(out3)
+            .param(XMLOptions.DOCTYPE_PUBLIC, "")
+            .param(XMLOptions.INDENT, "yes")
+            .param(XMLOptions.INDENT_AMOUNT, "2")
+            .write(doc);
+        
+        String out4 = new XMLWriter()
+            .indent(4)
+            .toString(doc);
+        
+        System.out.printf("%n%s%n", out1);
+        System.out.printf("%n%s%n", out2);
+        System.out.printf("%n%s%n", out3.toString(Charsets.UTF8));
+        System.out.printf("%n%s%n", out4);
+        
+        assertTrue(true);
+    }
+    
+
+    private XMLElement load(File file) {
+        return $(file).normalize().find("? //text()[normalize-space(.) = '']").each(n->n.remove()).first().root();
+    }
+    
+    
+}