Package org.geoserver.config.util

Source Code of org.geoserver.config.util.XStreamPersister$FeatureTypeInfoConverter

/* Copyright (c) 2001 - 2008 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.config.util;

import java.awt.geom.AffineTransform;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.collections.MultiHashMap;
import org.geoserver.catalog.AttributeTypeInfo;
import org.geoserver.catalog.AttributionInfo;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CoverageDimensionInfo;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.CoverageStoreInfo;
import org.geoserver.catalog.DataStoreInfo;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.LayerGroupInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.MetadataLinkInfo;
import org.geoserver.catalog.MetadataMap;
import org.geoserver.catalog.NamespaceInfo;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.catalog.StoreInfo;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.catalog.impl.AttributeTypeInfoImpl;
import org.geoserver.catalog.impl.AttributionInfoImpl;
import org.geoserver.catalog.impl.CatalogImpl;
import org.geoserver.catalog.impl.CoverageDimensionImpl;
import org.geoserver.catalog.impl.CoverageInfoImpl;
import org.geoserver.catalog.impl.CoverageStoreInfoImpl;
import org.geoserver.catalog.impl.DataStoreInfoImpl;
import org.geoserver.catalog.impl.FeatureTypeInfoImpl;
import org.geoserver.catalog.impl.LayerGroupInfoImpl;
import org.geoserver.catalog.impl.LayerInfoImpl;
import org.geoserver.catalog.impl.MetadataLinkInfoImpl;
import org.geoserver.catalog.impl.NamespaceInfoImpl;
import org.geoserver.catalog.impl.ResolvingProxy;
import org.geoserver.catalog.impl.ResourceInfoImpl;
import org.geoserver.catalog.impl.StoreInfoImpl;
import org.geoserver.catalog.impl.StyleInfoImpl;
import org.geoserver.catalog.impl.WorkspaceInfoImpl;
import org.geoserver.config.ContactInfo;
import org.geoserver.config.GeoServer;
import org.geoserver.config.GeoServerInfo;
import org.geoserver.config.JAIInfo;
import org.geoserver.config.LoggingInfo;
import org.geoserver.config.ServiceInfo;
import org.geoserver.config.impl.ContactInfoImpl;
import org.geoserver.config.impl.GeoServerImpl;
import org.geoserver.config.impl.GeoServerInfoImpl;
import org.geoserver.config.impl.JAIInfoImpl;
import org.geoserver.config.impl.LoggingInfoImpl;
import org.geoserver.config.impl.ServiceInfoImpl;
import org.geoserver.ows.util.OwsUtils;
import org.geoserver.security.SecureCatalogImpl;
import org.geotools.coverage.grid.GeneralGridEnvelope;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.referencing.crs.DefaultProjectedCRS;
import org.geotools.referencing.operation.DefaultMathTransformFactory;
import org.geotools.referencing.operation.matrix.GeneralMatrix;
import org.geotools.referencing.operation.transform.AffineTransform2D;
import org.geotools.util.NumberRange;
import org.geotools.util.logging.Logging;
import org.opengis.coverage.grid.GridGeometry;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransformFactory;
import org.opengis.referencing.operation.Matrix;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.SingleValueConverterWrapper;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
import com.thoughtworks.xstream.converters.collections.CollectionConverter;
import com.thoughtworks.xstream.converters.collections.MapConverter;
import com.thoughtworks.xstream.converters.reflection.FieldDictionary;
import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
import com.thoughtworks.xstream.converters.reflection.SortableFieldKeySorter;
import com.thoughtworks.xstream.converters.reflection.Sun14ReflectionProvider;
import com.thoughtworks.xstream.io.HierarchicalStreamDriver;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.ClassAliasingMapper;
import com.thoughtworks.xstream.mapper.Mapper;

/**
* Utility class which loads and saves catalog and configuration objects to and
* from an xstream.
*
* @author Justin Deoliveira, The Open Planning Project
*
*/
public class XStreamPersister {

   
    /**
     * Callback interface or xstream persister.
     */
    public static class Callback {
        protected void postEncodeWorkspace( WorkspaceInfo ws, HierarchicalStreamWriter writer, MarshallingContext context ) {
        }
       
        protected void postEncodeNamespace( NamespaceInfo ns, HierarchicalStreamWriter writer, MarshallingContext context ) {
        }
       
        protected void postEncodeDataStore( DataStoreInfo ds, HierarchicalStreamWriter writer, MarshallingContext context ) {
        }
       
        protected void postEncodeCoverageStore( CoverageStoreInfo ds, HierarchicalStreamWriter writer, MarshallingContext context ) {
        }
       
        protected void postEncodeFeatureType( FeatureTypeInfo ds, HierarchicalStreamWriter writer, MarshallingContext context ) {
        }
       
        protected void postEncodeCoverage( CoverageInfo ds, HierarchicalStreamWriter writer, MarshallingContext context ) {
        }
       
        protected void postEncodeLayer( LayerInfo ls, HierarchicalStreamWriter writer, MarshallingContext context ) {
        }
       
        protected void postEncodeLayerGroup( LayerGroupInfo ls, HierarchicalStreamWriter writer, MarshallingContext context ) {
        }
       
        protected void postEncodeReference( Object obj, String ref, HierarchicalStreamWriter writer, MarshallingContext context ) {
        }
    }
   
    /**
     * logging instance
     */
    static Logger LOGGER = Logging.getLogger( "org.geoserver" );
   
    /**
     * internal xstream instance
     */
    XStream xs;

    /**
     * GeoServer reference used to resolve references to gloal from services
     */
    GeoServer geoserver;
   
    /**
     * Catalog reference, used to resolve references to stores, workspaces
     * + namespaces
     */
    Catalog catalog;
   
    /**
     * Callback instance.
     */
    Callback callback;
   
    /**
     * Flag controlling how references to objects are encoded.
     */
    boolean referenceByName = false;
   
    /**
     * Constructs the persister and underlying xstream.
     */
    protected XStreamPersister() {
        this(null);
    }
   
    /**
     * Constructs the persister and underlying xstream specifying the stream driver explicitly.
     */
    protected XStreamPersister(HierarchicalStreamDriver streamDriver) {
       
        //control the order in which fields are sorted
        SortableFieldKeySorter sorter = new SortableFieldKeySorter();
        sorter.registerFieldOrder( CatalogImpl.class, new String[]{ "workspaces", "namespaces", "stores", "styles",
            /* these we actually omit, but the sorter needs them specified */
            "layerGroups", "resources", "maps", "listeners", "layers""resourcePool", "resourceLoader", "LOGGER" } );
       
        ReflectionProvider reflectionProvider = new CustomReflectionProvider( new FieldDictionary( sorter ) );
            //new Sun14ReflectionProvider( new FieldDictionary( sorter  ) );
        if ( streamDriver != null ) {
            xs = new XStream( reflectionProvider, streamDriver );
        }
        else {
            xs = new XStream( reflectionProvider );   
        }
        xs.setMode(XStream.NO_REFERENCES);
       
        init(xs);
    }
   
    protected void init(XStream xs) {
        // Default implementations
        initImplementationDefaults(xs);
       
        // Aliases
        xs.alias("global", GeoServerInfo.class);
        xs.alias("logging", LoggingInfo.class);
        xs.alias("jai", JAIInfo.class);
        xs.alias("catalog", Catalog.class);
        xs.alias("namespace", NamespaceInfo.class);
        xs.alias("workspace", WorkspaceInfo.class);
        xs.alias("dataStore", DataStoreInfo.class);
        xs.alias("coverageStore", CoverageStoreInfo.class);
        xs.alias("style",StyleInfo.class);
        xs.alias( "featureType", FeatureTypeInfo.class);
        xs.alias( "coverage", CoverageInfo.class);
        xs.alias( "coverageDimension", CoverageDimensionInfo.class);
        xs.alias( "metadataLink", MetadataLinkInfo.class);
        xs.alias( "attribute", AttributeTypeInfo.class );
        xs.alias( "layer", LayerInfo.class);
        xs.alias( "layerGroup", LayerGroupInfo.class);
        xs.alias( "gridGeometry", GridGeometry2D.class);
        xs.alias( "projected", DefaultProjectedCRS.class);
        xs.alias( "attribution", AttributionInfo.class );
        xs.aliasField("abstract", ResourceInfoImpl.class, "_abstract" );
       
        // GeoServerInfo
        xs.omitField(impl(GeoServerInfo.class), "clientProperties");
        xs.omitField(impl(GeoServerInfo.class), "geoServer");
        xs.registerLocalConverter(impl(GeoServerInfo.class), "metadata", new MetadataMapConverter());
       
        // ServiceInfo
        xs.omitField(impl(ServiceInfo.class), "clientProperties");
        xs.omitField(impl(ServiceInfo.class), "geoServer");
        xs.registerLocalConverter(impl(ServiceInfo.class), "metadata", new MetadataMapConverter());
       
        // Catalog
        xs.omitField(impl(Catalog.class), "resourcePool");
        xs.omitField(impl(Catalog.class), "resourceLoader");
        xs.omitField(impl(Catalog.class), "resources");
        xs.omitField(impl(Catalog.class), "listeners");
        xs.omitField(impl(Catalog.class), "layers");
        xs.omitField(impl(Catalog.class), "maps");
        xs.omitField(impl(Catalog.class), "layerGroups");
        xs.omitField(impl(Catalog.class), "LOGGER");
        xs.registerLocalConverter(impl(Catalog.class), "stores",
                new StoreMultiHashMapConverter());
        xs.registerLocalConverter(impl(Catalog.class), "namespaces",
                new SpaceMapConverter("namespace"));
        xs.registerLocalConverter(impl(Catalog.class), "workspaces",
                new SpaceMapConverter("workspace"));
       
       
        //WorkspaceInfo
        xs.omitField( impl(WorkspaceInfo.class), "_default");
        xs.registerLocalConverter( impl(WorkspaceInfo.class), "metadata", new MetadataMapConverter() );
       
        //NamespaceInfo
        xs.omitField( impl(NamespaceInfo.class), "catalog");
        xs.omitField( impl(NamespaceInfo.class), "_default");
        xs.registerLocalConverter( impl(NamespaceInfo.class), "metadata", new MetadataMapConverter() );
       
        // StoreInfo
        xs.omitField(impl(StoreInfo.class), "catalog");
        xs.omitField(impl(StoreInfo.class), "error");
        //xs.omitField(StoreInfo.class), "workspace"); //handled by StoreInfoConverter
        xs.registerLocalConverter(impl(StoreInfo.class), "workspace", new ReferenceConverter(WorkspaceInfo.class));
        xs.registerLocalConverter(impl(StoreInfo.class), "connectionParameters", new BreifMapConverter() );
        xs.registerLocalConverter(impl(StoreInfo.class), "metadata", new MetadataMapConverter());
       
        // StyleInfo
        xs.omitField(impl(StyleInfo.class), "catalog");
        xs.registerLocalConverter(impl(StyleInfo.class), "metadata", new MetadataMapConverter() );
       
        // ResourceInfo
        xs.omitField( impl(ResourceInfo.class), "catalog");
        xs.omitField( impl(ResourceInfo.class), "crs" );
        xs.registerLocalConverter( impl(ResourceInfo.class), "nativeCRS", new CRSConverter());
        xs.registerLocalConverter( impl(ResourceInfo.class), "store", new ReferenceConverter(StoreInfo.class));
        xs.registerLocalConverter( impl(ResourceInfo.class), "namespace", new ReferenceConverter(NamespaceInfo.class));
        xs.registerLocalConverter( impl(ResourceInfo.class), "metadata", new MetadataMapConverter() );
       
        // FeatureTypeInfo
       
        // CoverageInfo

        // CoverageDimensionInfo
        xs.registerLocalConverter( impl(CoverageDimensionInfo.class), "range", new NumberRangeConverter());
       
        // AttributeTypeInfo
        xs.omitField( impl(AttributeTypeInfo.class), "featureType");
        xs.omitField( impl(AttributeTypeInfo.class), "attribute");
       
        // LayerInfo
        //xs.omitField( LayerInfo.class), "resource");
        xs.registerLocalConverter( impl(LayerInfo.class), "resource", new ReferenceConverter( ResourceInfo.class ) );
        xs.registerLocalConverter( impl(LayerInfo.class), "defaultStyle", new ReferenceConverter( StyleInfo.class ) );
        xs.registerLocalConverter( impl(LayerInfo.class), "styles", new ReferenceCollectionConverter( StyleInfo.class ) );
        xs.registerLocalConverter( impl(LayerInfo.class), "metadata", new MetadataMapConverter() );
       
        // LayerGroupInfo
        xs.registerLocalConverter(impl(LayerGroupInfo.class), "layers", new ReferenceCollectionConverter( LayerInfo.class ));
        xs.registerLocalConverter(impl(LayerGroupInfo.class), "styles", new ReferenceCollectionConverter( StyleInfo.class ));
        xs.registerLocalConverter(impl(LayerGroupInfo.class), "metadata", new MetadataMapConverter() );
       
        //ReferencedEnvelope
        xs.registerLocalConverter( ReferencedEnvelope.class, "crs", new SRSConverter() );
        xs.registerLocalConverter( GeneralEnvelope.class, "crs", new SRSConverter() );
       
        // ServiceInfo
        xs.omitField( impl(ServiceInfo.class), "geoServer" );
       
        // Converters
        xs.registerConverter(new SpaceInfoConverter());
        xs.registerConverter(new StoreInfoConverter());
        xs.registerConverter(new ResourceInfoConverter());
        xs.registerConverter(new FeatureTypeInfoConverter());
        xs.registerConverter(new CoverageInfoConverter());
        xs.registerConverter(new LayerInfoConverter());
        xs.registerConverter(new LayerGroupInfoConverter());
        xs.registerConverter(new GridGeometry2DConverter());
        xs.registerConverter(new ProxyCollectionConverter( xs.getMapper() ) );
       
        callback = new Callback();
    }

    public XStream getXStream() {
        return xs;
    }

    public ClassAliasingMapper getClassAliasingMapper() {
        return (ClassAliasingMapper) xs.getMapper().lookupMapperOfType( ClassAliasingMapper.class );
    }
   
    public void setCatalog(Catalog catalog) {
        this.catalog = catalog;
    }
   
    public void setGeoServer(GeoServer geoserver) {
        this.geoserver = geoserver;
    }
   
    public void setCallback(Callback callback) {
        this.callback = callback;
    }
   
    public void setReferenceByName(boolean referenceByName) {
        this.referenceByName = referenceByName;
    }
   
    public void setExcludeIds() {
        xs.omitField( WorkspaceInfoImpl.class, "id");
        xs.omitField( NamespaceInfoImpl.class, "id");
        xs.omitField(StoreInfoImpl.class, "id");
        xs.omitField(StyleInfoImpl.class, "id");
        xs.omitField( ResourceInfoImpl.class, "id");
        xs.omitField( LayerInfoImpl.class, "id");
        xs.omitField(LayerGroupInfoImpl.class, "id" );
        xs.omitField(AttributeTypeInfoImpl.class, "id");
    }
   
    /**
     * Saves an object to persistence.
     *
     * @param obj The object to save.
     * @param out The stream to save the object to.
     *
     * @throws IOException
     */
    public void save(Object obj, OutputStream out) throws IOException {
        //unwrap dynamic proxies
        obj = unwrapProxies( obj );
        xs.toXML(obj, new OutputStreamWriter( out, "UTF-8" ));
    }
   
    /**
     * Unwraps any proxies around the object.
     * <p>
     * If the object is not being proxied it is passed back.
     * </p>
     */
    public static Object unwrapProxies( Object obj ) {
        obj = SecureCatalogImpl.unwrap( obj );
        obj = GeoServerImpl.unwrap( obj );
        obj = CatalogImpl.unwrap( obj );
        return obj;
    }
   
    /**
     * Loads an object from peristence.
     *
     * @param in The input stream to read the object from.
     * @param clazz The class of the expected object.
     *
     * @throws IOException
     */
    public <T> T load(InputStream in, Class<T> clazz ) throws IOException {
        T obj = clazz.cast( xs.fromXML( in ) );
       
        //call resolve() to ensure that any references created during loading
        // get resolved to actual objects, for instance for links from datastores
        // to workspaces
        if ( obj instanceof CatalogImpl ) {
            ((CatalogImpl)obj).resolve();
        }
       
        return obj;
    }
   
   
    /**
     * Builds a converter that will marshal/unmarshal the target class by reference, that is, by
     * storing the object id as opposed to fully serializing it
     * @param clazz
     * @return
     */
    public ReferenceConverter buildReferenceConverter(Class clazz) {
        return new ReferenceConverter(clazz);
    }
   
    /**
     * Same as {@link #buildReferenceConverter(Class)}, but works against a collection of objects
     * @param clazz
     * @return
     */
    public ReferenceCollectionConverter buildReferenceCollectionConverter(Class clazz) {
        return new ReferenceCollectionConverter(clazz);
    }

    /**
     * Sets up mappings from interface to implementation classes.
     *
     */
    protected void initImplementationDefaults(XStream xs) {
        //configuration
        xs.addDefaultImplementation(GeoServerInfoImpl.class, GeoServerInfo.class);
        xs.addDefaultImplementation(LoggingInfoImpl.class, LoggingInfo.class);
        xs.addDefaultImplementation(JAIInfoImpl.class, JAIInfo.class);
        xs.addDefaultImplementation(ContactInfoImpl.class, ContactInfo.class);
        xs.addDefaultImplementation(AttributionInfoImpl.class, AttributionInfo.class);
       
        //catalog
        xs.addDefaultImplementation(CatalogImpl.class, Catalog.class);
        xs.addDefaultImplementation(NamespaceInfoImpl.class, NamespaceInfo.class);
        xs.addDefaultImplementation(WorkspaceInfoImpl.class, WorkspaceInfo.class);
        xs.addDefaultImplementation(DataStoreInfoImpl.class, DataStoreInfo.class);
        xs.addDefaultImplementation(CoverageStoreInfoImpl.class, CoverageStoreInfo.class);
        xs.addDefaultImplementation(StyleInfoImpl.class, StyleInfo.class);
        xs.addDefaultImplementation(FeatureTypeInfoImpl.class, FeatureTypeInfo.class );
        xs.addDefaultImplementation(CoverageInfoImpl.class, CoverageInfo.class);
        xs.addDefaultImplementation(CoverageDimensionImpl.class, CoverageDimensionInfo.class);
        xs.addDefaultImplementation(MetadataLinkInfoImpl.class, MetadataLinkInfo.class);
        xs.addDefaultImplementation(AttributeTypeInfoImpl.class, AttributeTypeInfo.class );
        xs.addDefaultImplementation(LayerInfoImpl.class, LayerInfo.class);
        xs.addDefaultImplementation(LayerGroupInfoImpl.class, LayerGroupInfo.class );
       
        //supporting objects
        xs.addDefaultImplementation(GridGeometry2D.class, GridGeometry.class );
        xs.addDefaultImplementation(DefaultGeographicCRS.class, CoordinateReferenceSystem.class);
       
        //collections
        xs.addDefaultImplementation(ArrayList.class, List.class);
    }
   
    protected Class impl(Class interfce) {
        //special case case classes, they don't get registered as default implementations
        // only concrete classes do
        if (interfce == ServiceInfo.class) {
            return ServiceInfoImpl.class;
        }
        if (interfce == StoreInfo.class) {
            return StoreInfoImpl.class;
        }
        if (interfce == ResourceInfo.class) {
            return ResourceInfoImpl.class;
        }
       
        Class clazz = getXStream().getMapper().defaultImplementationOf(interfce);
        if (clazz == null) {
            throw new RuntimeException("No default mapping for " + interfce);
        }
        return clazz;
    }
   
    /**
     * Custom reflection provider which unwraps proxies, and skips empty collections
     * and maps.
     */
    class CustomReflectionProvider extends Sun14ReflectionProvider {
       
        public CustomReflectionProvider( FieldDictionary fd ) {
            super( fd );
        }
       
        @Override
        public void visitSerializableFields(Object object, Visitor visitor) {
            super.visitSerializableFields(object, new VisitorWrapper(visitor));
        }
       
        class VisitorWrapper implements ReflectionProvider.Visitor {

            Visitor wrapped;
           
            public VisitorWrapper( Visitor wrapped ) {
                this.wrapped = wrapped;
            }
           
            public void visit(String name, Class type, Class definedIn,
                    Object value) {
               
                //skip empty collections + maps
                if ( value instanceof Collection && ((Collection)value).isEmpty() ) {
                    return;
                }
                if ( value instanceof Map && ((Map)value).isEmpty() ) {
                    return;
                }
               
                //unwrap any proxies
                value = unwrapProxies(value);
                wrapped.visit( name, type, definedIn, value);
            }
           
        }
    }

    //
    // custom converters
    //
   
    //simple object converters
    /**
     * Map converter which encodes a map more breifly than the standard map converter.
     */
    protected class BreifMapConverter extends MapConverter {
       
        public BreifMapConverter() {
            super(getXStream().getMapper());
        }
       
        @Override
        public void marshal(Object source, HierarchicalStreamWriter writer,
                MarshallingContext context) {
       
            Map map = (Map) source;
            for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
                Map.Entry entry = (Map.Entry) iterator.next();
               
                if ( entry.getValue() == null ) {
                    continue;
                }
               
                writer.startNode("entry");
                writer.addAttribute( "key", entry.getKey().toString());
                if ( entry.getValue() != null ) {
                    writer.setValue(entry.getValue().toString());
                }
               
                writer.endNode();
            }
        }

        @Override
        protected void populateMap(HierarchicalStreamReader reader,
                UnmarshallingContext context, Map map) {
           
            while (reader.hasMoreChildren()) {
                reader.moveDown();
               
                //we support two syntaxes here:
                // 1) <key>value</key>
                // 2) <key><type>value</type></key>
                // 3) <entry key="">value</entry>
                // 4) <entry>
                //      <type>key</type>
                //      <type>value</type>
                //    </entry>
                String key = reader.getNodeName();
                Object value = null;
                if ( "entry".equals( key ) ) {
                    if ( reader.getAttribute( "key") != null ) {
                        //this is case 3
                        key = reader.getAttribute( "key" );
                        value = reader.getValue();
                    }
                    else if ( reader.hasMoreChildren() ){
                        //this is case 4
                        reader.moveDown();
                       
                        key = reader.getValue();
                       
                        reader.moveUp();
                        reader.moveDown();
                       
                        value = reader.getValue();
                       
                        reader.moveUp();
                    }

                }
                else {
                    boolean old = false;
                    if (reader.hasMoreChildren()) {
                        //this handles case 2
                        old = true;
                        reader.moveDown();   
                    }
                   
                    value = readItem(reader, context, map);
                   
                    if ( old ) {
                        reader.moveUp();   
                    }
                }

                map.put(key, value);

                reader.moveUp();
            }
        }

        @Override
        protected Object readItem(HierarchicalStreamReader reader, UnmarshallingContext context,
                Object current) {
            return reader.getValue();
        }
    }
   
    /**
     * Custom converter for the special metadata map.
     */
    class MetadataMapConverter extends BreifMapConverter {
       
        @Override
        public boolean canConvert(Class type) {
            return MetadataMap.class.equals(type) || super.canConvert(type);
        }
       
        @Override
        public void marshal(Object source, HierarchicalStreamWriter writer,
                MarshallingContext context) {
            if ( source instanceof MetadataMap) {
                MetadataMap mdmap = (MetadataMap) source;
                source = mdmap.getMap();
            }
           
            super.marshal(source, writer, context);
        }
       
        @Override
        public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
            Map map = (Map) super.unmarshal(reader, context);
            if ( !(map instanceof MetadataMap ) ) {
                map = new MetadataMap(map);
            }
            return map;
        }
    }
    /**
     * Converters which encodes an object by a reference, or its id.
     */
    //class ReferenceConverter extends AbstractSingleValueConverter {
    class ReferenceConverter implements Converter {
        Class clazz;
       
        public ReferenceConverter( Class clazz ) {
            this.clazz = clazz;
        }

        public boolean canConvert(Class type) {
            return clazz.isAssignableFrom( type );
        }

        public void marshal(Object source, HierarchicalStreamWriter writer,
                MarshallingContext context) {
            //could be a proxy, unwrap it
            source = CatalogImpl.unwrap( source );
           
            //gets its id
            String id = (String) OwsUtils.get( source, "id" );
            if ( id != null && !referenceByName) {
                writer.startNode("id");
                writer.setValue( id );
                writer.endNode();
                callback.postEncodeReference( source, id, writer, context );
            }
            else {
                //use name if no id set
                String name = (String) OwsUtils.get( source, "name" );
                if ( name != null ) {
                    writer.startNode("name");
                    writer.setValue( name );
                    writer.endNode();
                    callback.postEncodeReference( source, name, writer, context );
                }
                else {
                    throw new IllegalArgumentException( "Unable to marshal reference with no id or name.");
                }
            }
           
        }
       
        public Object unmarshal(HierarchicalStreamReader reader,
                UnmarshallingContext context) {
           
            String ref = null;
            if ( reader.hasMoreChildren() ) {
                reader.moveDown();
                ref = reader.getValue();
                reader.moveUp();
            }
            else {
                ref = reader.getValue();
            }
            Object proxy = ResolvingProxy.create( ref, clazz );
            Object resolved = proxy;
            if ( catalog != null ) {
                resolved = ResolvingProxy.resolve( catalog, proxy );
            }
           
            return CatalogImpl.unwrap( resolved );
        }
    }
    class ReferenceCollectionConverter extends CollectionConverter {
        Class clazz;
        public ReferenceCollectionConverter(Class clazz) {
            super( getXStream().getMapper() );
            this.clazz = clazz;
        }
       
        @Override
        protected void writeItem(Object item, MarshallingContext context,
                HierarchicalStreamWriter writer) {
            ClassAliasingMapper cam =
                (ClassAliasingMapper) mapper().lookupMapperOfType( ClassAliasingMapper.class );
           
            String elementName = cam.serializedClass( clazz );
            if ( elementName == null ) {
                elementName = cam.serializedClass( item.getClass() );
            }
            writer.startNode(elementName);
            if(item != null)
                context.convertAnother( item, new ReferenceConverter( clazz ) );
            writer.endNode();
        }
       
        @Override
        protected Object readItem(HierarchicalStreamReader reader,
                UnmarshallingContext context, Object current) {
            return context.convertAnother( current, clazz, new ReferenceConverter( clazz ) );
        }
    }
    /**
     * Converter which unwraps proxies in a collection.
     */
    class ProxyCollectionConverter extends CollectionConverter {

        public ProxyCollectionConverter(Mapper mapper) {
            super(mapper);
        }
       
        @Override
        protected void writeItem(Object item, MarshallingContext context,
                HierarchicalStreamWriter writer) {
                       
            super.writeItem(unwrapProxies(item), context, writer);
        }
    }
   
    /**
     * Converter for coordinate reference system objects that converts by SRS code.
     */
    static class SRSConverter extends AbstractSingleValueConverter {
       
        public boolean canConvert(Class type) {
            return CoordinateReferenceSystem.class.isAssignableFrom(type);
        }
       
        @Override
        public String toString(Object obj) {
            CoordinateReferenceSystem crs = (CoordinateReferenceSystem) obj;
            try {
                Integer epsg = CRS.lookupEpsgCode(crs, true);
                if (epsg != null) {
                    return "EPSG:" + epsg;
                }
            }
            catch (FactoryException e) {
                XStreamPersister.LOGGER.warning( "Could not determine epsg code of crs, encoding as WKT");
            }
           
            return crs.toWKT();
        }
       
        @Override
        public Object fromString(String str) {
            if ( str.toUpperCase().startsWith( "EPSG:") ) {
                try {
                    return CRS.decode( str );
                }
                catch (Exception e) {
                    XStreamPersister.LOGGER.log( Level.WARNING, "Error decode epsg code: "+str, e );
                }   
            }
            else {
                try {
                    return CRS.parseWKT( str );
                }
                catch (FactoryException e) {
                    XStreamPersister.LOGGER.log( Level.WARNING, "Error decode wkt: "+str, e );
                }
            }
            return null;
        }
    }
   
    /**
     * Converter for coordinate reference system objects that converts by WKT.
     *
     */
    static class CRSConverter extends AbstractSingleValueConverter {

        @Override
        public boolean canConvert(Class type) {
            return CoordinateReferenceSystem.class.isAssignableFrom(type);
        }

        @Override
        public String toString(Object obj) {
            return ((CoordinateReferenceSystem)obj).toWKT();
        }
       
        @Override
        public Object fromString(String str) {
            try {
                return CRS.parseWKT( str );
            }
            catch (Exception e) {
                try {
                    return new SRSConverter().fromString( str );
                }
                catch( Exception e1 ) {}
               
                throw new RuntimeException( e );
            }
        }
       
    }
   
    /**
     * Converter for coverage grid geometry.
     *
     */
    class GridGeometry2DConverter extends AbstractReflectionConverter {
        public GridGeometry2DConverter() {
            super( GridGeometry2D.class );
        }

        @Override
        protected void doMarshal(Object source,
                HierarchicalStreamWriter writer, MarshallingContext context) {
        
            GridGeometry2D g = (GridGeometry2D) source;
            MathTransform tx = g.getGridToCRS();

            writer.addAttribute("dimension", String.valueOf(g.getGridRange().getDimension()));
           
            //grid range
            StringBuffer low = new StringBuffer();
            StringBuffer high = new StringBuffer();
            for (int r = 0; r < g.getGridRange().getDimension(); r++) {
                low.append(g.getGridRange().getLow(r)).append(" ");
                high.append(g.getGridRange().getHigh(r)+1).append(" ");
            }
            low.setLength(low.length()-1);
            high.setLength(high.length()-1);
           
            writer.startNode("range");
            writer.startNode( "low" ); writer.setValue( low.toString() ); writer.endNode();
            writer.startNode( "high" ); writer.setValue( high.toString() ); writer.endNode();
            writer.endNode();
           
            //transform
            if (tx instanceof AffineTransform) {
                AffineTransform atx = (AffineTransform) tx;
               
                writer.startNode("transform");
                writer.startNode("scaleX"); writer.setValue(Double.toString( atx.getScaleX())); writer.endNode();
                writer.startNode("scaleY"); writer.setValue(Double.toString( atx.getScaleY())); writer.endNode();
                writer.startNode("shearX"); writer.setValue(Double.toString( atx.getShearX())); writer.endNode();
                writer.startNode("shearY"); writer.setValue(Double.toString( atx.getShearY())); writer.endNode();
                writer.startNode("translateX"); writer.setValue(Double.toString( atx.getTranslateX())); writer.endNode();
                writer.startNode("translateY"); writer.setValue(Double.toString( atx.getTranslateY())); writer.endNode();
                writer.endNode();
            }
           
            //crs
            writer.startNode("crs");
            context.convertAnother( g.getCoordinateReferenceSystem(),
                new SingleValueConverterWrapper( new SRSConverter() ) );
            writer.endNode();
          
        }
       
        @Override
        public Object unmarshal(HierarchicalStreamReader reader,
                UnmarshallingContext context) {
             int[] high,low;
           
            //reader.moveDown(); //grid
           
            reader.moveDown(); //range
           
            reader.moveDown(); //low
            low = toIntArray( reader.getValue() );
            reader.moveUp();
            reader.moveDown(); //high
            high = toIntArray( reader.getValue() );
            reader.moveUp();
           
            reader.moveUp(); //range
           
            if ( reader.hasMoreChildren() ) {
                reader.moveDown(); //transform or crs
            }
           
            AffineTransform2D gridToCRS = null;
            if ( "transform".equals( reader.getNodeName() ) ) {
                double sx,sy,shx,shy,tx,ty;
               
                reader.moveDown(); //scaleX
                sx = Double.parseDouble( reader.getValue() );
                reader.moveUp();
               
                reader.moveDown(); //scaleY
                sy = Double.parseDouble( reader.getValue() );
                reader.moveUp();
               
                reader.moveDown(); //shearX
                shx = Double.parseDouble( reader.getValue() );
                reader.moveUp();
               
                reader.moveDown(); //shearY
                shy = Double.parseDouble( reader.getValue() );
                reader.moveUp();
               
                reader.moveDown(); //translateX
                tx = Double.parseDouble( reader.getValue() );
                reader.moveUp();
               
                reader.moveDown(); //translateY
                ty = Double.parseDouble( reader.getValue() );
                reader.moveUp();
               

                // set tranform
                gridToCRS = new AffineTransform2D(sx, shx, shy, sy, tx, ty);
               
                reader.moveUp();
                if ( reader.hasMoreChildren() ) {
                    reader.moveDown(); //crs
                }
            }
           
            CoordinateReferenceSystem crs = null;
            if ( "crs".equals( reader.getNodeName() ) ) {
                crs = (CoordinateReferenceSystem) context.convertAnother( null, CoordinateReferenceSystem.class,
                    new SingleValueConverterWrapper( new SRSConverter() ));
                reader.moveUp();
            }
           
            // new grid range
            GeneralGridEnvelope gridRange = new GeneralGridEnvelope(low, high);
           
           
            GridGeometry2D gg = new GridGeometry2D( gridRange, gridToCRS, crs );
            return serializationMethodInvoker.callReadResolve(gg);
        }
           
        int[] toIntArray( String s ) {
            String[] split = s.split( " " );
            int[] ints = new int[split.length];
            for ( int i = 0; i < split.length; i++ ) {
                ints[i] = Integer.parseInt( split[i] );
            }
            return ints;
        }
    }
    class NumberRangeConverter extends AbstractReflectionConverter {
    
        @Override
        public boolean canConvert(Class clazz) {
            return NumberRange.class.isAssignableFrom( clazz );
        }
       
        @Override
        public void marshal(Object original, HierarchicalStreamWriter writer,
                MarshallingContext context) {
            NumberRange range = (NumberRange) original;
           
            writer.startNode("min");
            if ( Double.isInfinite( ((Number)range.getMinValue()).doubleValue() ) ) {
                context.convertAnother( "-inf" );
            }
            else {
                context.convertAnother( range.getMinValue() )
            }
            writer.endNode();
           
            writer.startNode("max");
            if ( Double.isInfinite( ((Number)range.getMaxValue()).doubleValue() )) {
                context.convertAnother( "inf");
            }
            else {
                context.convertAnother( range.getMaxValue() )
            }
            writer.endNode();
        }
       
        @Override
        public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
            //JD: we handle infinite manually b/c the json serializer chokes on inifinte values
            // b/c JSON does not support it
            Double min = null, max = null;
            while( reader.hasMoreChildren() ) {
                reader.moveDown();
                if ( "min".equals( reader.getNodeName() ) ) {
                    if ( !"-inf".equals( reader.getValue() ) ) {
                        min = Double.parseDouble( reader.getValue() );
                    }
                }
                if ( "max".equals( reader.getNodeName() ) ) {
                    if ( !"inf".equals( reader.getValue() ) ) {
                        max = Double.parseDouble( reader.getValue() );
                    }
                }
                reader.moveUp();
            }
           
            min = min != null ? min : Double.NEGATIVE_INFINITY;
            max = max != null ? max : Double.POSITIVE_INFINITY;
           
            return NumberRange.create( min.doubleValue(), true, max.doubleValue(), true );
        }
    }
   
    //catalog object converters
    /**
     * Base class for all custom reflection based converters.
     */
    class AbstractReflectionConverter extends ReflectionConverter {
        Class clazz;

        public AbstractReflectionConverter() {
            this(Object.class);
        }

        public AbstractReflectionConverter(Class clazz) {
            super(getXStream().getMapper(),getXStream().getReflectionProvider());
            this.clazz = clazz;
        }

        @Override
        public boolean canConvert(Class type) {
            return clazz.isAssignableFrom( type );
        }
       
        @Override
        protected void doMarshal(Object source,
                HierarchicalStreamWriter writer, MarshallingContext context) {
            super.doMarshal(source, writer, context);
            postDoMarshal(source,writer,context);
        }
       
        protected void postDoMarshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
        }
    }

    /**
     * Converter for workspaces and namespaces.
     */
    class SpaceInfoConverter extends AbstractReflectionConverter {
        @Override
        public boolean canConvert(Class type) {
            return WorkspaceInfo.class.isAssignableFrom(type) ||
                NamespaceInfo.class.isAssignableFrom(type);
        }
       
        @Override
        protected void postDoMarshal(Object source,
                HierarchicalStreamWriter writer, MarshallingContext context) {
            if ( source instanceof WorkspaceInfo ) {
                callback.postEncodeWorkspace( (WorkspaceInfo)source,writer,context );
            }
            else {
                callback.postEncodeNamespace( (NamespaceInfo) source,writer,context );
            }
        }
    }
    /**
     * Converter for data stores and coverage stores.
     */
    class StoreInfoConverter extends AbstractReflectionConverter {

        public StoreInfoConverter() {
            super(StoreInfo.class);
        }

        @Override
        protected void postDoMarshal(Object result,
                HierarchicalStreamWriter writer, MarshallingContext context) {
           
            StoreInfo store = (StoreInfo) result;
            if ( store instanceof DataStoreInfo ) {
                callback.postEncodeDataStore( (DataStoreInfo) store, writer, context );
            }
            else {
                callback.postEncodeCoverageStore( (CoverageStoreInfo) store, writer, context );
            }
           
        }
        public Object doUnmarshal(Object result,
                HierarchicalStreamReader reader, UnmarshallingContext context) {
            StoreInfo store = (StoreInfo) super.doUnmarshal(result, reader, context);
           
            LOGGER.info( "Loaded store '" +  store.getName() "', " + (store.isEnabled() ? "enabled" : "disabled") );
            return store;
        }
    }

    /**
     * Converter for multi hash maps containing coverage stores and data stores.
     */
    static class StoreMultiHashMapConverter implements Converter {
        public boolean canConvert(Class type) {
            return MultiHashMap.class.equals(type);
        }

        public void marshal(Object source, HierarchicalStreamWriter writer,
                MarshallingContext context) {
            MultiHashMap map = (MultiHashMap) source;
            for (Object v : map.values()) {
                if (v instanceof DataStoreInfo) {
                    writer.startNode("dataStore");
                    context.convertAnother(v);
                    writer.endNode();
                }
                if (v instanceof CoverageStoreInfo ) {
                    writer.startNode( "coverageStore" );
                    context.convertAnother(v);
                    writer.endNode();
                }
            }
        }

        public Object unmarshal(HierarchicalStreamReader reader,
                UnmarshallingContext context) {
            MultiHashMap map = new MultiHashMap();
           
            while( reader.hasMoreChildren() ) {
                reader.moveDown();
               
                Object o = 0;
                if ( "dataStore".equals( reader.getNodeName() ) ) {
                    o = context.convertAnother( map, DataStoreInfoImpl.class );
                }
                else {
                    o = context.convertAnother( map, CoverageStoreInfoImpl.class );
                }
                map.put( o.getClass(), o );
               
                reader.moveUp();
            }
           
            return map;
        }
    }

    /**
     * Converter for handling maps containing workspaces and namespaces.
     *
     */
    static class SpaceMapConverter implements Converter {

        String name;
       
        public SpaceMapConverter( String name ) {
            this.name = name;
        }
       
        public boolean canConvert(Class type) {
            return Map.class.isAssignableFrom(type);
        }

        public void marshal(Object source, HierarchicalStreamWriter writer,
                MarshallingContext context) {

            Map map = (Map) source;
           
            for (Object o : map.entrySet()) {
                Map.Entry e = (Map.Entry) o;
                if ( e.getKey() == null ) {
                    continue;
                }
               
                writer.startNode(name);
                if ( map.get( null ) == e.getValue() ) {
                    writer.addAttribute("default", "true");
                }
                context.convertAnother(e.getValue());
                writer.endNode();
            }
        }

        public Object unmarshal(HierarchicalStreamReader reader,
                UnmarshallingContext context) {
            Map map = new HashMap();
           
            while( reader.hasMoreChildren() ) {
               reader.moveDown();

               boolean def = "true".equals( reader.getAttribute( "default") );
              
               if ( "namespace".equals( name ) ) {
                   NamespaceInfoImpl ns = (NamespaceInfoImpl) context.convertAnother( map, NamespaceInfoImpl.class );
                   map.put( ns.getPrefix() , ns );
                   if ( def ) {
                       map.put( null, ns );
                   }
                   LOGGER.info( "Loading namespace '" + ns.getPrefix() + "'" );
               }
               else {
                   WorkspaceInfoImpl ws = (WorkspaceInfoImpl) context.convertAnother( map, WorkspaceInfoImpl.class );
                   map.put( ws.getName() , ws );
                   if ( def ) {
                       map.put( null, ws );
                   }
                   LOGGER.info( "Loading workspace '" + ws.getName() + "'" );
               }
              
               reader.moveUp();
            }
           
            return map;
        }
    }

    /**
     * Base converter for handling resources.
     */
    class ResourceInfoConverter extends AbstractReflectionConverter {
       
        public ResourceInfoConverter() {
            this(ResourceInfo.class);
        }
       
        public ResourceInfoConverter(Class clazz ) {
            super(clazz);
        }
       
        public Object doUnmarshal(Object result,
                HierarchicalStreamReader reader, UnmarshallingContext context) {
            ResourceInfo obj = (ResourceInfo) super.doUnmarshal(result, reader, context);
           
            String enabled = obj.isEnabled() ? "enabled" : "disabled";
            String type = obj instanceof CoverageInfo ? "coverage" :
                obj instanceof FeatureTypeInfo ? "feature type" : "resource";
           
            LOGGER.info( "Loaded " + type + " '" + obj.getName() + "', " + enabled );
           
            return obj;
        }
    }
   
    /**
     * Converter for feature types.
     */
    class FeatureTypeInfoConverter extends ResourceInfoConverter {

        public FeatureTypeInfoConverter() {
            super(FeatureTypeInfo.class);
        }
       
        @Override
        protected void postDoMarshal(Object result,
                HierarchicalStreamWriter writer, MarshallingContext context) {
            FeatureTypeInfoImpl featureType = (FeatureTypeInfoImpl) result;
           
            //ensure null list does not result
            if ( featureType.getAttributes() == null ){
                featureType.setAttributes(new ArrayList());
            }
           
            callback.postEncodeFeatureType(featureType, writer, context);
        }
    }
   
    /**
     * Converter for feature types.
     */
    class CoverageInfoConverter extends ResourceInfoConverter {
        public CoverageInfoConverter() {
            super( CoverageInfo.class );
        }
       
        @Override
        protected void postDoMarshal(Object result,
                HierarchicalStreamWriter writer, MarshallingContext context) {
            callback.postEncodeCoverage((CoverageInfo)result, writer, context);
        }
    }
   
    /**
     * Converter for layers.
     */
    class LayerInfoConverter extends AbstractReflectionConverter {

        public LayerInfoConverter() {
            super( LayerInfo.class );
        }
       
        @Override
        protected void doMarshal(Object source, HierarchicalStreamWriter writer,
                MarshallingContext context) {
            // write out the name, which is a derived property now
            // TODO: remove this when resource/publishing split is done
            LayerInfo l = (LayerInfo) source;
            writer.startNode("name");
            writer.setValue(l.getName());
            writer.endNode();

            super.doMarshal(source, writer, context);
        }
               
        @Override
        protected void postDoMarshal(Object result,
                HierarchicalStreamWriter writer, MarshallingContext context) {
            /*
            LayerInfo l = (LayerInfo) result;
            writer.startNode("resource");
            context.convertAnother( l.getResource(), new ReferenceConverter( ResourceInfo.class ) );
            writer.endNode();
            */
            callback.postEncodeLayer( (LayerInfo) result, writer, context );
        }
    }
 
    /**
     * Converter for layer groups.
     */
    class LayerGroupInfoConverter extends AbstractReflectionConverter {

        public LayerGroupInfoConverter() {
            super( LayerGroupInfo.class );
        }

        @Override
        protected void postDoMarshal(Object result,
                HierarchicalStreamWriter writer, MarshallingContext context) {
            callback.postEncodeLayerGroup((LayerGroupInfo)result, writer, context);
        }
    }
}
TOP

Related Classes of org.geoserver.config.util.XStreamPersister$FeatureTypeInfoConverter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.