001/*
002 * (c) Copyright 2009 University of Bristol
003 * All rights reserved.
004 * [See end of file]
005 */
006package net.rootdev.javardfa.literal;
007
008import java.util.Iterator;
009import java.util.SortedMap;
010import java.util.TreeMap;
011import javax.xml.XMLConstants;
012import javax.xml.namespace.NamespaceContext;
013import javax.xml.namespace.QName;
014import javax.xml.stream.XMLEventReader;
015import javax.xml.stream.XMLEventWriter;
016import javax.xml.stream.XMLStreamException;
017import javax.xml.stream.XMLStreamWriter;
018import javax.xml.stream.events.Attribute;
019import javax.xml.stream.events.ProcessingInstruction;
020import javax.xml.stream.events.StartElement;
021import javax.xml.stream.events.XMLEvent;
022
023/**
024 * All this class does currently is ensure the order of attributes, but
025 * (By using a stream writer) it is more controllable than the event writer.
026 *
027 * @author pldms
028 */
029public class CanonicalXMLEventWriter
030        implements XMLEventWriter {
031
032    private final XMLStreamWriter swriter;
033    private final Attribute contextLang;
034    private int level;
035
036    public CanonicalXMLEventWriter(XMLStreamWriter swriter, Attribute contextLang) {
037        this.swriter = swriter;
038        this.contextLang = contextLang;
039        this.level = 0;
040    }
041
042    public void flush() throws XMLStreamException {
043        swriter.flush();
044    }
045
046    public void close() throws XMLStreamException {
047        swriter.close();
048    }
049
050    public void add(XMLEvent event) throws XMLStreamException {
051        if (event.isEndElement()) {
052            level--;
053            swriter.writeEndElement();
054        } else if (event.isCharacters()) {
055            swriter.writeCharacters(event.asCharacters().getData());
056        } else if (event.isProcessingInstruction()) {
057            swriter.writeProcessingInstruction(((ProcessingInstruction) event).getData(),
058                    ((ProcessingInstruction) event).getTarget());
059        } else if (event.isStartElement()) {
060            level++;
061            StartElement se = event.asStartElement();
062            if (se.getName().getNamespaceURI() == null ||
063                    se.getName().getNamespaceURI().length() == 0)
064                swriter.writeStartElement(se.getName().getLocalPart());
065            else if (se.getName().getPrefix().length() == 0) {
066                swriter.setDefaultNamespace(se.getName().getNamespaceURI());
067                swriter.writeStartElement(
068                    se.getName().getNamespaceURI(),
069                    se.getName().getLocalPart());
070            }
071            else {
072                //swriter.setPrefix(se.getName().getPrefix(), se.getName().getNamespaceURI());
073                swriter.writeStartElement(se.getName().getPrefix(),
074                    se.getName().getLocalPart(),
075                    se.getName().getNamespaceURI());
076            }
077            writeAttributes(se);
078            swriter.writeCharacters(""); // Force close of start element
079        } else {
080            System.err.printf("Gah! Missed one <%s>, '%s'\n", event.getClass(), event);
081        }
082    }
083
084    public void add(XMLEventReader reader) throws XMLStreamException {
085        while (reader.hasNext()) {
086            this.add(reader.nextEvent());
087        }
088    }
089
090    public String getPrefix(String uri) throws XMLStreamException {
091        return swriter.getPrefix(uri);
092    }
093
094    public void setPrefix(String prefix, String uri) throws XMLStreamException {
095        swriter.setPrefix(prefix, uri);
096    }
097
098    public void setDefaultNamespace(String uri) throws XMLStreamException {
099        swriter.setDefaultNamespace(uri);
100    }
101
102    public void setNamespaceContext(NamespaceContext context) throws XMLStreamException {
103        swriter.setNamespaceContext(context);
104    }
105
106    public NamespaceContext getNamespaceContext() {
107        return swriter.getNamespaceContext();
108    }
109
110    private void writeAttributes(StartElement se) throws XMLStreamException {
111        SortedMap<String, Attribute> atts = new TreeMap<String, Attribute>();
112        if (level == 2 && contextLang != null) atts.put("_xml:lang", contextLang);
113        for (Iterator i = se.getAttributes(); i.hasNext();) {
114            Attribute a = (Attribute) i.next();
115            atts.put(getName(a), a);
116        }
117        for (Attribute a : atts.values()) {
118            if (a.getName().getNamespaceURI() == null ||
119                    a.getName().getNamespaceURI().length() == 0)
120                swriter.writeAttribute(a.getName().getLocalPart(), a.getValue());
121            else if (a.getName().getPrefix().length() == 0)
122                swriter.writeAttribute(a.getName().getNamespaceURI(),
123                        a.getName().getLocalPart(), a.getValue());
124            else
125                swriter.writeAttribute(
126                    a.getName().getPrefix(),
127                    a.getName().getNamespaceURI(),
128                    a.getName().getLocalPart(),
129                    a.getValue());
130        }
131    }
132
133    private String getName(Attribute a) {
134        QName name = a.getName();
135        String toReturn = null;
136        // TODO I think something -- probably my code -- is wrong
137        // localName is sometimes xml:lang, so I got xml:xml:lang
138        if (name.getLocalPart().contains(":")) toReturn = name.getLocalPart();
139        else toReturn = (name.getPrefix() == null) ? 
140            name.getLocalPart() :
141            name.getPrefix() + ":" + name.getLocalPart();
142        if (toReturn.startsWith("xml:")) {
143            return "_" + toReturn;
144        } else {
145            return toReturn;
146        }
147    }
148}
149
150/*
151 * (c) Copyright 2009 University of Bristol
152 * All rights reserved.
153 *
154 * Redistribution and use in source and binary forms, with or without
155 * modification, are permitted provided that the following conditions
156 * are met:
157 * 1. Redistributions of source code must retain the above copyright
158 *    notice, this list of conditions and the following disclaimer.
159 * 2. Redistributions in binary form must reproduce the above copyright
160 *    notice, this list of conditions and the following disclaimer in the
161 *    documentation and/or other materials provided with the distribution.
162 * 3. The name of the author may not be used to endorse or promote products
163 *    derived from this software without specific prior written permission.
164 *
165 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
166 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
167 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
168 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
169 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
170 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
171 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
172 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
173 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
174 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
175 */