/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2010, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotoolkit.xml; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.Result; import org.apache.sis.util.logging.Logging; import org.apache.sis.xml.Namespaces; /** * An abstract class for all stax stream writer.<br/> * Writers for a given specification should extend this class and * provide appropriate write methods.<br/> * <br/> * Example : <br/> * <pre> * {@code * public class UserWriter extends StaxStreamWriter{ * * public void write(User user) throws XMLStreamException{ * //casual stax writing operations * writer.writeStartElement(... * } * } * } * </pre> * And should be used like :<br/> * <pre> * {@code * final UserWriter instance = new UserWriter(); * try{ * instance.setOutput(stream); * instance.write(aUser); * }finally{ * instance.dispose(); * } * </pre> * * @author Johann Sorel (Geomatys) * @module */ public abstract class StaxStreamWriter extends AbstractConfigurable { /** * Logger for this writer. */ protected static final Logger LOGGER = Logging.getLogger("org.geotoolkit.xml"); protected XMLStreamWriter writer; /** * Store the output stream if it was generated by the parser itself. * It will closed on the dispose method or when a new input is set. */ private OutputStream targetStream; private int lastUnknowPrefix = 0; private final Map<String, String> unknowNamespaces = new HashMap<String, String>(); public StaxStreamWriter(){ } /** * Acces the underlying stax writer. * This method is used when several writer are wrapping a single writer. * Like when an Symbology Encoding writer wraps a Filter writer. * <br> * It can also be used to write tag before or after this writer is used. */ public XMLStreamWriter getWriter(){ return writer; } /** * close potentiel previous stream and cache if there are some. * This way the writer can be reused for a different output later. * The underlying stax writer will be closed. */ public void reset() throws IOException, XMLStreamException{ if(writer != null){ writer.close(); writer = null; } if(targetStream != null){ targetStream.close(); targetStream = null; } } /** * Release potentiel locks or opened stream. * Must be called when the writer is not needed anymore. * It should not be used after this method has been called. */ public void dispose() throws IOException, XMLStreamException{ reset(); } /** * Set the output for this writer.<br/> * Handle types are :<br/> * - java.io.File<br/> * - java.io.Writer<br/> * - java.io.OutputStream<br/> * - javax.xml.stream.XMLStreamWriter<br/> * - javax.xml.transform.Result<br/> * * @param output * @throws IOException * @throws XMLStreamException */ public void setOutput(Object output) throws IOException, XMLStreamException{ reset(); if(output instanceof XMLStreamWriter){ writer = (XMLStreamWriter) output; return; } if(output instanceof File){ targetStream = new FileOutputStream((File)output); final BufferedOutputStream bout = new BufferedOutputStream(targetStream); output = bout; } if(output instanceof Path){ targetStream = Files.newOutputStream((Path) output, StandardOpenOption.CREATE, StandardOpenOption.WRITE); final BufferedOutputStream bout = new BufferedOutputStream(targetStream); output = bout; } writer = toWriter(output); } /** * Write a new tag with the text corresponding to the given value. * The tag won't be written if the value is null. * @param namespace : namespace of the wanted tag * @param localName : local name of the wanted tag * @param value : text value to write * @throws XMLStreamException */ protected void writeSimpleTag(final String namespace, final String localName, final Object value) throws XMLStreamException{ if(value != null){ writer.writeStartElement(namespace, localName); writer.writeCharacters(value.toString()); writer.writeEndElement(); } } /** * Creates a new XMLStreamWriter. * @param output * @return XMLStreamWriter * @throws XMLStreamException if the output is not handled */ private static XMLStreamWriter toWriter(final Object output) throws XMLStreamException{ final XMLOutputFactory XMLfactory = XMLOutputFactory.newInstance(); XMLfactory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, Boolean.TRUE); if(output instanceof OutputStream){ return XMLfactory.createXMLStreamWriter((OutputStream)output,"UTF-8"); }else if(output instanceof Result){ return XMLfactory.createXMLStreamWriter((Result)output); }else if(output instanceof Writer){ return XMLfactory.createXMLStreamWriter((Writer)output); }else{ throw new XMLStreamException("Output type is not supported : "+ output); } } /** * Returns the prefix for the given namespace. * * @param namespace The namespace for which we want the prefix. */ protected Prefix getPrefix(final String namespace) { String prefix = Namespaces.getPreferredPrefix(namespace, null); /* * temporary hack todo remove */ if ("http://www.opengis.net/gml/3.2".equals(namespace)) { return new Prefix(false, "gml"); } boolean unknow = false; if (prefix == null) { prefix = unknowNamespaces.get(namespace); if (prefix == null) { prefix = "ns" + lastUnknowPrefix; lastUnknowPrefix++; unknow = true; unknowNamespaces.put(namespace, prefix); } } return new Prefix(unknow, prefix); } /** * Inner class for handling prefix and if it is already known. */ protected final class Prefix { public boolean unknow; public String prefix; public Prefix(final boolean unknow, final String prefix) { this.prefix = prefix; this.unknow = unknow; } } }