/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2014, 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; 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. */ package org.geotoolkit.owc.xml; import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.ServiceLoader; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.PropertyException; import javax.xml.bind.Unmarshaller; import javax.xml.namespace.QName; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLEventWriter; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.Result; import javax.xml.transform.Source; import org.apache.sis.storage.DataStoreException; import org.apache.sis.util.iso.SimpleInternationalString; import org.apache.sis.xml.MarshallerPool; import org.geotoolkit.georss.xml.v100.WhereType; import org.geotoolkit.gml.xml.v311.DirectPositionType; import org.geotoolkit.gml.xml.v311.EnvelopeType; import org.geotoolkit.map.MapBuilder; import org.geotoolkit.map.MapContext; import org.geotoolkit.map.MapItem; import org.geotoolkit.map.MapLayer; import org.geotoolkit.metadata.Citations; import org.geotoolkit.owc.gtkext.ObjectFactory; import org.geotoolkit.owc.xml.v10.ContentType; import org.geotoolkit.owc.xml.v10.OfferingType; import org.geotoolkit.referencing.IdentifiedObjects; import org.opengis.util.FactoryException; import org.w3._2005.atom.EntryType; import org.w3._2005.atom.FeedType; import org.w3._2005.atom.LinkType; import org.w3._2005.atom.TextType; import static org.geotoolkit.owc.xml.OwcMarshallerPool.*; import org.geotoolkit.owc.xml.v10.StyleSetType; import org.geotoolkit.sld.xml.Specification; import org.geotoolkit.sld.xml.StyleXmlIO; import org.geotoolkit.sld.xml.v110.UserStyle; import org.geotoolkit.style.DefaultDescription; import org.geotoolkit.style.MutableStyle; import org.opengis.geometry.Envelope; import org.opengis.style.Description; import org.w3._2005.atom.IdType; import org.w3._2005.atom.TextTypeType; import org.w3c.dom.Node; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; /** * Read and write MapContext objects. * * @author Samuel Andrés (Geomatys) * @author Johann Sorel (Geomatys) */ public class OwcXmlIO { private static final ObjectFactory GEOTK_FACTORY = new ObjectFactory(); private static final OwcExtension[] EXTENSIONS; static { final ServiceLoader<OwcExtension> loader = ServiceLoader.load(OwcExtension.class); final Iterator<OwcExtension> ite = loader.iterator(); final List<OwcExtension> lst = new ArrayList<>(); while(ite.hasNext()) lst.add(ite.next()); //sort by priority Collections.sort(lst,Collections.reverseOrder()); EXTENSIONS = lst.toArray(new OwcExtension[0]); } public static OwcExtension[] getExtensions() { return EXTENSIONS.clone(); } public static void write(final Object output, final MapContext context) throws PropertyException, JAXBException, FactoryException{ final FeedType feed = write(context); final MarshallerPool pool = OwcMarshallerPool.getPool(); final Marshaller marshaller = pool.acquireMarshaller(); try{ marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); if(output instanceof ContentHandler) marshaller.marshal(feed, (ContentHandler)output); else if(output instanceof File) marshaller.marshal(feed, (File)output); else if(output instanceof Node) marshaller.marshal(feed, (Node)output); else if(output instanceof OutputStream) marshaller.marshal(feed, (OutputStream)output); else if(output instanceof Result) marshaller.marshal(feed, (Result)output); else if(output instanceof Writer) marshaller.marshal(feed, (Writer)output); else if(output instanceof XMLEventWriter) marshaller.marshal(feed, (XMLEventWriter)output); else if(output instanceof XMLStreamWriter) marshaller.marshal(feed, (XMLStreamWriter)output); else{ throw new JAXBException("Unsupported output type : "+output); } }finally{ pool.recycle(marshaller); } } private static FeedType write(final MapContext context) throws FactoryException{ final FeedType feed = ATOM_FACTORY.createFeedType(); final LinkType link = ATOM_FACTORY.createLinkType(); link.setRel("profile"); link.setHref("http://www.opengis.net/spec/owc-atom/1.0/req/core"); link.setTitle(context.getName()==null ? "" : context.getName()); feed.getAuthorOrCategoryOrContributor().add(ATOM_FACTORY.createFeedTypeLink(link)); final TextType title = ATOM_FACTORY.createTextType(); title.getContent().add(context.getName()==null ? "" : context.getName()); feed.getAuthorOrCategoryOrContributor().add(ATOM_FACTORY.createFeedTypeTitle(title)); final Envelope aoi = context.getAreaOfInterest(); if(aoi!=null){ final String ogc = IdentifiedObjects.lookupIdentifier(Citations.URN_OGC, aoi.getCoordinateReferenceSystem(), true); final WhereType where = GEORSS_FACTORY.createWhereType(); final DirectPositionType lowerCorner = new DirectPositionType(aoi.getLowerCorner()); final DirectPositionType upperCorner = new DirectPositionType(aoi.getUpperCorner()); final EnvelopeType envelopeType = new EnvelopeType(null, lowerCorner, upperCorner, ogc); envelopeType.setSrsDimension(2); where.setEnvelope(envelopeType); feed.getAuthorOrCategoryOrContributor().add(GEORSS_FACTORY.createWhere(where)); } for(final MapItem mapItem : context.items()){ toEntry(null, mapItem, feed.getAuthorOrCategoryOrContributor()); } return feed; } private static void toEntry(String parentPath, final MapItem item, List entries){ final EntryType entry = ATOM_FACTORY.createEntryType(); entries.add(ATOM_FACTORY.createFeedTypeEntry(entry)); //store other informations final String name = ((parentPath!=null)?parentPath:"") + item.getName(); final Description description = item.getDescription(); if(name!=null){ final IdType atom = new IdType(); atom.setValue(name); entry.getAuthorOrCategoryOrContent().add(ATOM_FACTORY.createEntryTypeId(atom)); } if(description!=null && description.getTitle()!=null){ final TextType atom = new TextType(); atom.setType(TextTypeType.TEXT); atom.getContent().add(description.getTitle().toString()); entry.getAuthorOrCategoryOrContent().add(ATOM_FACTORY.createEntryTypeTitle(atom)); } if(description!=null && description.getAbstract()!=null){ final TextType atom = new TextType(); atom.setType(TextTypeType.TEXT); atom.getContent().add(description.getAbstract().toString()); entry.getAuthorOrCategoryOrContent().add(ATOM_FACTORY.createEntryTypeSummary(atom)); } if(item instanceof MapLayer){ final MapLayer layer = (MapLayer) item; entry.getAuthorOrCategoryOrContent().add(GEOTK_FACTORY.createVisible(layer.isVisible())); entry.getAuthorOrCategoryOrContent().add(GEOTK_FACTORY.createSelectable(layer.isSelectable())); entry.getAuthorOrCategoryOrContent().add(GEOTK_FACTORY.createOpacity(layer.getOpacity())); OfferingType offering = null; for(OwcExtension ext : getExtensions()){ if(ext.canHandle(layer)){ offering = ext.createOffering(layer); entry.getAuthorOrCategoryOrContent().add(OWC_FACTORY.createOffering(offering)); break; } } //store styles if(offering!=null){ if(layer.getStyle()!=null){ final StyleSetType styleBase = toStyleSet(layer.getStyle(), true); offering.getOperationOrContentOrStyleSet().add(OWC_FACTORY.createOfferingTypeStyleSet(styleBase)); } if(layer.getSelectionStyle()!=null){ final StyleSetType styleSelection = toStyleSet(layer.getSelectionStyle(), false); offering.getOperationOrContentOrStyleSet().add(OWC_FACTORY.createOfferingTypeStyleSet(styleSelection)); } } }else{ final ContentType content = OWC_FACTORY.createContentType(); content.setType(item.getName()); //encode children for(MapItem child : item.items()){ toEntry(name+"/", child, entries); } entry.getAuthorOrCategoryOrContent().add(OWC_FACTORY.createOfferingTypeContent(content)); } } private static StyleSetType toStyleSet(MutableStyle style, boolean def){ final StyleSetType styleSet = OWC_FACTORY.createStyleSetType(); styleSet.setDefault(def); final ContentType content = OWC_FACTORY.createContentType(); final StyleXmlIO io = new StyleXmlIO(); final UserStyle jaxbStyle = io.getTransformerXMLv110().visit(style, null); content.getContent().add(jaxbStyle); styleSet.getNameOrTitleOrAbstract().add(OWC_FACTORY.createStyleSetTypeContent(content)); return styleSet; } public static MapContext read(final Object input) throws JAXBException, FactoryException, DataStoreException{ final MarshallerPool pool = OwcMarshallerPool.getPool(); final Unmarshaller unmarshaller = pool.acquireUnmarshaller(); final FeedType feed; try{ final Object jax; if(input instanceof File) jax = unmarshaller.unmarshal((File)input); else if(input instanceof Node) jax = unmarshaller.unmarshal((Node)input); else if(input instanceof InputSource) jax = unmarshaller.unmarshal((InputSource)input); else if(input instanceof InputStream) jax = unmarshaller.unmarshal((InputStream)input); else if(input instanceof Source) jax = unmarshaller.unmarshal((Source)input); else if(input instanceof Reader) jax = unmarshaller.unmarshal((Reader)input); else if(input instanceof XMLEventReader) jax = unmarshaller.unmarshal((XMLEventReader)input); else if(input instanceof XMLStreamReader) jax = unmarshaller.unmarshal((XMLStreamReader)input); else{ throw new JAXBException("Unsupported input type : "+input); } feed = (FeedType) ((JAXBElement)jax).getValue(); }finally{ pool.recycle(unmarshaller); } return read(feed); } private static MapContext read(final FeedType feed) throws JAXBException, FactoryException, DataStoreException{ final MapContext context = MapBuilder.createContext(); for(Object o : feed.getAuthorOrCategoryOrContributor()){ if(o instanceof JAXBElement){ o = ((JAXBElement)o).getValue(); } if(o instanceof TextType){ final TextType title = (TextType) o; title.getContent(); }else if(o instanceof WhereType){ final WhereType where = (WhereType) o; final EnvelopeType envelopeType = where.getEnvelope(); context.setAreaOfInterest(envelopeType); }else if(o instanceof EntryType){ final EntryType entry = (EntryType) o; final MapItem item = readEntry(entry); //find insert parent final String[] path = item.getName().split("/"); MapItem parent = context; for(int i=0;i<path.length-1;i++){ parent = findItem(parent, path[i]); } item.setName(path[path.length-1]); parent.items().add(item); } } return context; } private static MapItem findItem(MapItem parent, String name){ for(MapItem mi : parent.items()){ if(mi.getName().equals(name)){ return mi; } } //does not exist, create it final MapItem np = MapBuilder.createItem(); parent.items().add(np); return np; } private static MapItem readEntry(final EntryType entry) throws JAXBException, FactoryException, DataStoreException{ final List<Object> entryContent = entry.getAuthorOrCategoryOrContent(); String layerName = ""; String layerTitle = ""; String layerAbstract = ""; boolean visible = true; boolean selectable = true; double layerOpacity = 1.0; MapItem mapItem = null; MutableStyle baseStyle = null; MutableStyle selectionStyle = null; final List<MapItem> children = new ArrayList<>(); for(Object o : entryContent){ QName name = null; if(o instanceof JAXBElement){ final JAXBElement jax = (JAXBElement) o; name = jax.getName(); o = jax.getValue(); if(GEOTK_FACTORY._Visible_QNAME.equals(name)){ visible = (Boolean)o; }else if(GEOTK_FACTORY._Selectable_QNAME.equals(name)){ selectable = (Boolean)o; }else if(GEOTK_FACTORY._Opacity_QNAME.equals(name)){ layerOpacity = (Double)o; } } if(o instanceof OfferingType){ final OfferingType offering = (OfferingType) o; for(OwcExtension ext : getExtensions()){ if(ext.getCode().equals(offering.getCode())){ mapItem = ext.createLayer(offering); break; } } //search for styles baseStyle = readStyle(offering,true); selectionStyle = readStyle(offering,false); }else if(o instanceof ContentType){ //decode children final ContentType content = (ContentType) o; final List<Object> contentContent = content.getContent(); for(Object co : contentContent){ if(co instanceof JAXBElement){ co = ((JAXBElement)o).getValue(); } if(co instanceof EntryType){ children.add(readEntry((EntryType)co)); } } }else if(o instanceof IdType){ final IdType idType = (IdType) o; final String value = idType.getValue(); layerName = value; }else if(o instanceof TextType){ final TextType tt = (TextType) o; if(ATOM_FACTORY._EntryTypeTitle_QNAME.equals(name)){ if(!tt.getContent().isEmpty()){ layerTitle = (String) tt.getContent().get(0); } }else if(ATOM_FACTORY._EntryTypeSummary_QNAME.equals(name)){ if(!tt.getContent().isEmpty()){ layerAbstract = (String) tt.getContent().get(0); } } } } if(mapItem==null){ mapItem = MapBuilder.createItem(); }else if(mapItem instanceof MapLayer){ if(baseStyle!=null){ ((MapLayer)mapItem).setStyle(baseStyle); } if(selectionStyle!=null){ ((MapLayer)mapItem).setSelectionStyle(selectionStyle); } ((MapLayer)mapItem).setSelectable(selectable); ((MapLayer)mapItem).setOpacity(layerOpacity); } mapItem.setName(layerName); mapItem.setDescription(new DefaultDescription( new SimpleInternationalString(layerTitle), new SimpleInternationalString(layerAbstract))); mapItem.setName(layerName); mapItem.setVisible(visible); mapItem.items().addAll(children); return mapItem; } private static MutableStyle readStyle(OfferingType offering, boolean def) throws JAXBException, FactoryException{ final List<Object> content = offering.getOperationOrContentOrStyleSet(); for(Object co : content){ if(co instanceof JAXBElement) co = ((JAXBElement)co).getValue(); if(!(co instanceof StyleSetType)) continue; final StyleSetType sst = (StyleSetType)co; if(sst.isDefault() != def) continue; final List<Object> ssc = sst.getNameOrTitleOrAbstract(); for(Object ss : ssc){ if(ss instanceof JAXBElement) ss = ((JAXBElement)ss).getValue(); if(!(ss instanceof ContentType)) continue; final ContentType ct = (ContentType) ss; final List<Object> subcs = ct.getContent(); for(Object subc : subcs){ if(subc instanceof JAXBElement) subc = ((JAXBElement)subc).getValue(); if(!(subc instanceof UserStyle)) continue; final StyleXmlIO io = new StyleXmlIO(); return io.readStyle(subc, Specification.SymbologyEncoding.V_1_1_0); } } } return null; } }