org.kalypso.ogc.gml.serialize.GmlSerializer.java Source code

Java tutorial

Introduction

Here is the source code for org.kalypso.ogc.gml.serialize.GmlSerializer.java

Source

/*--------------- Kalypso-Header --------------------------------------------------------------------
    
 This file is part of kalypso.
 Copyright (C) 2004, 2005 by:
    
 Technical University Hamburg-Harburg (TUHH)
 Institute of River and coastal engineering
 Denickestr. 22
 21073 Hamburg, Germany
 http://www.tuhh.de/wb
    
 and
    
 Bjoernsen Consulting Engineers (BCE)
 Maria Trost 3
 56070 Koblenz, Germany
 http://www.bjoernsen.de
    
 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; either
 version 2.1 of the License, or (at your option) any later version.
    
 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.
    
 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    
 Contact:
    
 E-Mail:
 belger@bjoernsen.de
 schlienger@bjoernsen.de
 v.doemming@tuhh.de
    
 ---------------------------------------------------------------------------------------------------*/
package org.kalypso.ogc.gml.serialize;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.httpclient.URIException;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.kalypso.commons.java.net.UrlUtilities;
import org.kalypso.commons.performance.TimeLogger;
import org.kalypso.commons.resources.SetContentHelper;
import org.kalypso.contribs.eclipse.core.resources.ResourceUtilities;
import org.kalypso.contribs.eclipse.core.runtime.ProgressInputStream;
import org.kalypso.contribs.eclipse.core.runtime.StatusUtilities;
import org.kalypso.contribs.eclipse.ui.progress.ProgressUtilities;
import org.kalypso.contribs.org.xml.sax.DelegateXmlReader;
import org.kalypso.core.KalypsoCoreDebug;
import org.kalypso.core.KalypsoCorePlugin;
import org.kalypso.core.i18n.Messages;
import org.kalypso.gml.GMLException;
import org.kalypso.gml.GMLWorkspaceInputSource;
import org.kalypso.gml.GMLWorkspaceReader;
import org.kalypso.gml.GMLorExceptionContentHandler;
import org.kalypso.gmlschema.GMLSchema;
import org.kalypso.gmlschema.GMLSchemaException;
import org.kalypso.gmlschema.IGMLSchema;
import org.kalypso.gmlschema.feature.IFeatureType;
import org.kalypsodeegree.model.feature.GMLWorkspace;
import org.kalypsodeegree_impl.model.feature.FeatureFactory;
import org.kalypsodeegree_impl.model.feature.IFeatureProviderFactory;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

/**
 * Helper - Klasse, um Gml zu lesen und zu schreiben.
 * 
 * @author Gernot Belger
 */
public final class GmlSerializer {
    private static TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();

    public static final IFeatureProviderFactory DEFAULT_FACTORY = new GmlSerializerFeatureProviderFactory();

    public static final String[] GZ_EXTENSIONS = new String[] { "gz", "gmlz" }; //$NON-NLS-1$ //$NON-NLS-2$

    private GmlSerializer() {
        // do not instantiate this class
    }

    public static void serializeWorkspace(final OutputStreamWriter writer, final GMLWorkspace workspace)
            throws GmlSerializeException {
        serializeWorkspace(writer, workspace, writer.getEncoding());
    }

    /**
     * Writes a {@link GMLWorkspace} into an {@link File}. If the filename ends with <code>.gz</code>, the output will be
     * compressed with GZip.
     */
    public static void serializeWorkspace(final File gmlFile, final GMLWorkspace gmlWorkspace,
            final String encoding) throws IOException, GmlSerializeException {
        OutputStream os = null;
        try {
            final BufferedOutputStream bs = new BufferedOutputStream(new FileOutputStream(gmlFile));

            if (isGZ(gmlFile.getName()))
                os = new GZIPOutputStream(bs);
            else
                os = bs;

            GmlSerializer.serializeWorkspace(os, gmlWorkspace, encoding);
            os.close();
        } finally {
            IOUtils.closeQuietly(os);
        }
    }

    /**
     * REMARK: This method closes the given writer, which is VERY bad. Every caller should close the write on its own
     * 
     * @deprecated Because this method closes it writer. Change to {@link #serializeWorkspace(Writer, GMLWorkspace, String, false)}, rewrite your code, then we can get
     *             rid of this method and the flag.
     */
    @Deprecated
    public static void serializeWorkspace(final Writer writer, final GMLWorkspace gmlWorkspace,
            final String charsetEncoding) throws GmlSerializeException {
        serializeWorkspace(writer, gmlWorkspace, charsetEncoding, true);
    }

    /**
     * @deprecated Do not usae writer to write xml. The caller(!) always must close the writer. Use {@link #serializeWorkspace(OutputStreamWriter, GMLWorkspace)}
     */
    @Deprecated
    public static void serializeWorkspace(final Writer writer, final GMLWorkspace gmlWorkspace,
            final String charsetEncoding, final boolean closeWriter) throws GmlSerializeException {
        final GMLWorkspaceInputSource inputSource = new GMLWorkspaceInputSource(gmlWorkspace);
        final StreamResult result = new StreamResult(writer);

        try {
            serializeWorkspace(inputSource, result, charsetEncoding);
        } finally {
            if (closeWriter) {
                IOUtils.closeQuietly(writer);
            }
        }
    }

    public static void serializeWorkspace(final OutputStream is, final GMLWorkspace gmlWorkspace,
            final String charsetEncoding) throws GmlSerializeException {
        final GMLWorkspaceInputSource inputSource = new GMLWorkspaceInputSource(gmlWorkspace);
        final StreamResult result = new StreamResult(is);

        serializeWorkspace(inputSource, result, charsetEncoding);
    }

    /**
     * Most general way to serialize the workspace. All other serialize methods should eventually cal this method.
     */
    public static void serializeWorkspace(final GMLWorkspaceInputSource inputSource, final StreamResult result,
            final String charsetEncoding) throws GmlSerializeException {
        try {
            // TODO: error handling
            final XMLReader reader = new GMLWorkspaceReader();
            // TODO: add an error handler that logs everything into a status
            // reader.setErrorHandler( null );
            // TODO: write gml to a temporary location and replace the target file later,
            // in order to preserve the old version if anything goes wrong

            reader.setFeature("http://xml.org/sax/features/namespaces", true); //$NON-NLS-1$
            reader.setFeature("http://xml.org/sax/features/namespace-prefixes", true); //$NON-NLS-1$

            final Source source = new SAXSource(reader, inputSource);

            final Transformer transformer = TRANSFORMER_FACTORY.newTransformer();
            transformer.setOutputProperty(OutputKeys.ENCODING, charsetEncoding);
            transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "1"); //$NON-NLS-1$ //$NON-NLS-2$
            transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
            // TODO: maybe also use OutputKeys.CDATA_SECTION_ELEMENTS ? See the marshallMethod of the XSDBaseTypeHandlerString
            // TODO put new QName( NS.OM, "result" ) here instead inside the GMLSaxFactory
            // Problem: must now know the prefix of NS.OM
            transformer.transform(source, result);
        } catch (final Exception e) {
            throw new GmlSerializeException(Messages.getString("org.kalypso.ogc.gml.serialize.GmlSerializer.4"), e); //$NON-NLS-1$
        }
    }

    /**
     * Same as {@link #createGMLWorkspace(URL, IFeatureProviderFactory, null )
        
     */
    public static GMLWorkspace createGMLWorkspace(final URL gmlURL, final IFeatureProviderFactory factory)
            throws Exception {
        return createGMLWorkspace(gmlURL, factory, null);
    }

    /**
     * Reads a {@link GMLWorkspace} from the contents of an {@link URL}.
     */
    public static GMLWorkspace createGMLWorkspace(final URL gmlURL, final IFeatureProviderFactory factory,
            final IProgressMonitor monitor) throws Exception {
        return createGMLWorkspace(gmlURL, null, factory, monitor);
    }

    /**
     * @param context
     *          If set, this context is used instead of the gmlUrl where the workspace is loaded from.
     */
    public static GMLWorkspace createGMLWorkspace(final URL gmlURL, final URL context,
            final IFeatureProviderFactory factory, final IProgressMonitor monitor) throws Exception {
        InputStream is = null;
        InputStream urlStream = null;
        InputStream bis = null;
        try {
            urlStream = gmlURL.openStream();

            if (monitor == null) {
                bis = new BufferedInputStream(urlStream);
            } else {
                final long contentLength = UrlUtilities.getContentLength(gmlURL);
                final String tskMsg = Messages.getString("org.kalypso.ogc.gml.serialize.GmlSerializer.3", gmlURL); //$NON-NLS-1$
                monitor.beginTask(tskMsg, (int) contentLength);
                bis = new ProgressInputStream(urlStream, contentLength, monitor);
            }

            if (isGZ(gmlURL.toExternalForm()))
                is = new GZIPInputStream(bis);
            else
                is = bis;

            final URL usedContext = context == null ? gmlURL : context;

            final GMLWorkspace workspace = createGMLWorkspace(new InputSource(is), null, usedContext, factory);
            is.close();
            return workspace;
        } catch (final IOException e) {
            // Handle cancel of progress monitor: ProgressInputStreams throws IOException with a CoreException as cause
            if (e == ProgressInputStream.CANCEL_EXCEPTION)
                throw new CoreException(new Status(IStatus.CANCEL, KalypsoCorePlugin.getID(),
                        Messages.getString("org.kalypso.ogc.gml.serialize.GmlSerializer.5"), e)); //$NON-NLS-1$

            throw e;
        } finally {
            // also close <code>bis</code> separately, as GZipInputStream throws exception in constructor
            IOUtils.closeQuietly(bis);
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(urlStream);
            ProgressUtilities.done(monitor);
        }
    }

    public static boolean isGZ(final String filename) {
        // REMARK: this is a quite crude way to decide, if to compress or not. But how should we decide it anyway?
        final String extension = FilenameUtils.getExtension(filename);
        return ArrayUtils.contains(GZ_EXTENSIONS, extension);
    }

    public static GMLWorkspace createGMLWorkspace(final InputSource inputSource, final URL schemaLocationHint,
            final URL context, final IFeatureProviderFactory factory)
            throws ParserConfigurationException, SAXException, IOException, GMLException {
        TimeLogger perfLogger = null;
        if (KalypsoCoreDebug.PERF_SERIALIZE_GML.isEnabled())
            perfLogger = new TimeLogger(Messages.getString("org.kalypso.ogc.gml.serialize.GmlSerializer.7")); //$NON-NLS-1$

        final IFeatureProviderFactory providerFactory = factory == null ? DEFAULT_FACTORY : factory;
        final XMLReader xmlReader = createXMLReader();

        // TODO: also set an error handler here

        final GMLorExceptionContentHandler exceptionHandler = new GMLorExceptionContentHandler(xmlReader,
                schemaLocationHint, context, providerFactory);
        xmlReader.setContentHandler(exceptionHandler);

        xmlReader.parse(inputSource);

        final GMLWorkspace workspace = exceptionHandler.getWorkspace();

        if (perfLogger != null) {
            perfLogger.takeInterimTime();
            perfLogger.printCurrentTotal(Messages.getString("org.kalypso.ogc.gml.serialize.GmlSerializer.8")); //$NON-NLS-1$
        }

        return workspace;
    }

    private static XMLReader createXMLReader() throws ParserConfigurationException, SAXException {
        final SAXParserFactory saxFac = SAXParserFactory.newInstance();
        saxFac.setNamespaceAware(true);

        final SAXParser saxParser = saxFac.newSAXParser();
        // make namespace-prefixes visible to content handler
        // used to allow necessary schemas from gml document
        final XMLReader xmlReader = saxParser.getXMLReader();
        xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", Boolean.TRUE); //$NON-NLS-1$
        return xmlReader;
    }

    public static GMLWorkspace createGMLWorkspace(final Node gmlNode, final URL context,
            final IFeatureProviderFactory factory) throws TransformerException, GMLException {
        final IFeatureProviderFactory providerFactory = factory == null ? DEFAULT_FACTORY : factory;

        /* REMARK: we need to simulate a XMLReader in order to make the gml parsing work. */
        final DelegateXmlReader delegateReader = new DelegateXmlReader();
        final GMLorExceptionContentHandler gmlContentHandler = new GMLorExceptionContentHandler(delegateReader,
                null, context, providerFactory);
        delegateReader.setContentHandler(gmlContentHandler);

        final Transformer t = TransformerFactory.newInstance().newTransformer();
        t.transform(new DOMSource(gmlNode), new SAXResult(delegateReader));

        return gmlContentHandler.getWorkspace();
    }

    public static GMLWorkspace createGMLWorkspace(final File file, final IFeatureProviderFactory factory)
            throws Exception {
        InputStream is = null;
        BufferedInputStream bis = null;

        try {
            bis = new BufferedInputStream(new FileInputStream(file));

            if (file.getName().endsWith(".gz")) //$NON-NLS-1$
            {
                is = new GZIPInputStream(bis);
            } else {
                is = bis;
            }

            final URL context = file.toURI().toURL();
            final GMLWorkspace workspace = createGMLWorkspace(new InputSource(is), null, context, factory);
            is.close();
            return workspace;
        } finally {
            IOUtils.closeQuietly(is);
            // Close <code>bis<code> separately, as GZipInputStream throws exception constructor
            IOUtils.closeQuietly(bis);
        }

    }

    public static GMLWorkspace createGMLWorkspace(final InputStream inputStream, final URL schemaURLHint,
            final IFeatureProviderFactory factory) throws Exception {
        return createGMLWorkspace(new InputSource(inputStream), schemaURLHint, null, factory);
    }

    /**
     * Creates an (empty) gml file, containing only one root feature of a given type.
     */
    public static void createGmlFile(final IFeatureType rootFeatureType, final IFile targetFile,
            final IProgressMonitor monitor, final IFeatureProviderFactory factory) throws CoreException {
        try {
            monitor.beginTask(Messages.getString("org.kalypso.ogc.gml.serialize.GmlSerializer.10"), 2); //$NON-NLS-1$
            final IFeatureProviderFactory providerFactory = factory == null ? DEFAULT_FACTORY : factory;
            final GMLWorkspace workspace = FeatureFactory.createGMLWorkspace(rootFeatureType,
                    ResourceUtilities.createURL(targetFile), providerFactory);
            monitor.worked(1);

            final SetContentHelper contentHelper = new SetContentHelper() {
                @Override
                protected void write(final OutputStreamWriter writer) throws Throwable {
                    GmlSerializer.serializeWorkspace(writer, workspace);
                }
            };
            contentHelper.setFileContents(targetFile, false, true, new SubProgressMonitor(monitor, 1));
            monitor.worked(1);
        } catch (final MalformedURLException | URIException e) {
            throw new CoreException(StatusUtilities.statusFromThrowable(e));
        }

    }

    public static void createGmlFile(final QName rootFeatureQName, final String[] introduceNamespaces,
            final IFile targetFile, final IProgressMonitor monitor, final IFeatureProviderFactory factory)
            throws CoreException, GMLSchemaException {
        monitor.beginTask(Messages.getString("org.kalypso.ogc.gml.serialize.GmlSerializer.10"), 2); //$NON-NLS-1$

        final IFeatureProviderFactory providerFactory = factory == null ? DEFAULT_FACTORY : factory;

        URL context = null;
        try {
            context = targetFile.getLocationURI().toURL();
        } catch (final MalformedURLException e) {
            e.printStackTrace();
        }

        final GMLWorkspace workspace = FeatureFactory.createGMLWorkspace(rootFeatureQName, context,
                providerFactory);

        // introduce further schemata into workspace
        final IGMLSchema schema = workspace.getGMLSchema();
        if (introduceNamespaces != null && schema instanceof GMLSchema) {
            final GMLSchema gmlSchema = (GMLSchema) schema;
            for (final String namespaceUri : introduceNamespaces) {
                try {
                    gmlSchema.getGMLSchemaForNamespaceURI(namespaceUri);
                } catch (final GMLSchemaException e) {
                    // probably not a vital error, just log it
                    final IStatus status = StatusUtilities.statusFromThrowable(e);
                    KalypsoCorePlugin.getDefault().getLog().log(status);
                }
            }
        }

        monitor.worked(1);

        final SetContentHelper contentHelper = new SetContentHelper() {
            @Override
            protected void write(final OutputStreamWriter writer) throws Throwable {
                GmlSerializer.serializeWorkspace(writer, workspace);
            }
        };
        contentHelper.setFileContents(targetFile, false, true, new SubProgressMonitor(monitor, 1));
        monitor.worked(1);
    }

    /**
     * This function loads a workspace from a {@link IFile}.
     * 
     * @param file
     *          The file of the workspace.
     * @return The workspace of the file.
     */
    public static GMLWorkspace createGMLWorkspace(final IFile file) throws Exception {
        return createGMLWorkspace(file, new NullProgressMonitor());
    }

    /**
     * This function loads a workspace from a {@link IFile}.
     * 
     * @param file
     *          The file of the workspace.
     * @return The workspace of the file.
     */
    public static GMLWorkspace createGMLWorkspace(final IFile file, final IProgressMonitor monitor)
            throws Exception {
        return createGMLWorkspace(file, null, null, monitor);
    }

    /**
     * This function loads a workspace from a {@link IFile}.
     * 
     * @param file
     *          The file of the workspace.
     * @return The workspace of the file.
     */
    public static GMLWorkspace createGMLWorkspace(final IFile file, final URL context,
            final IFeatureProviderFactory factory, final IProgressMonitor monitor) throws Exception {
        /* Create the url of the workspace. */
        final URL url = ResourceUtilities.createURL(file);

        /* Load the workspace and return it. */
        return GmlSerializer.createGMLWorkspace(url, context, factory, monitor);
    }

    /**
     * This function saves a given workspace to a file. Don't forget to set your charset to the file you are about to
     * create. It will be used by this function.
     * 
     * @param workspace
     *          The workspace to save.
     * @param file
     *          The file to save the workspace to. <strong>Note:</strong> The file must point to a real file.
     */
    public static void saveWorkspace(final GMLWorkspace workspace, final IFile file) throws Exception {
        if (workspace == null || file == null)
            throw new Exception(Messages.getString("org.kalypso.ogc.gml.serialize.GmlSerializer.2")); //$NON-NLS-1$

        /* The default encoding is that of the file. */
        final String encoding = file.getCharset();

        /* Create a writer. */
        final File javaFile = file.getLocation().toFile();

        /* Save the workspace. */
        serializeWorkspace(javaFile, workspace, encoding);

        /* Refresh the file. */
        file.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor());
    }

    /**
     * serializes a workspace into a zipfile
     */
    public static void serializeWorkspaceToZipFile(final File gmlZipResultFile, final GMLWorkspace resultWorkspace,
            final String zipEntryName) throws FileNotFoundException {
        final ZipOutputStream zos = new ZipOutputStream(
                new BufferedOutputStream(new FileOutputStream(gmlZipResultFile)));
        try {
            final ZipEntry newEntry = new ZipEntry(zipEntryName);
            zos.putNextEntry(newEntry);

            serializeWorkspace(zos, resultWorkspace, "CP1252"); //$NON-NLS-1$

            zos.closeEntry();
            zos.close();
        } catch (final Exception e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(zos);
        }
    }

    public static void serializeWorkspace(final IFile targetFile, final GMLWorkspace workspace,
            final IProgressMonitor monitor) throws CoreException {
        monitor.beginTask(Messages.getString("GmlSerializer.0"), 100); //$NON-NLS-1$

        String charset;
        if (targetFile.exists())
            charset = targetFile.getCharset();
        else {
            // check: is there a better way to set this default encoding?
            charset = "UTF-8"; //$NON-NLS-1$
        }
        monitor.worked(5);

        try {
            final File file = targetFile.getLocation().toFile();
            serializeWorkspace(file, workspace, charset);
            monitor.worked(90);
        } catch (final Exception e) {
            e.printStackTrace();
            final IStatus error = new Status(IStatus.ERROR, KalypsoCorePlugin.getID(),
                    Messages.getString("GmlSerializer.1"), e); //$NON-NLS-1$
            throw new CoreException(error);
        } finally {
            targetFile.refreshLocal(IResource.DEPTH_INFINITE, new SubProgressMonitor(monitor, 5));
        }
    }
}