/* * Constellation - An open source and standard compliant SDI * http://www.constellation-sdi.org * * Copyright 2014 Geomatys. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.constellation.provider.sld; import org.apache.sis.util.collection.Cache; import org.constellation.provider.AbstractStyleProvider; import org.geotoolkit.factory.FactoryFinder; import org.geotoolkit.factory.Hints; import org.geotoolkit.sld.MutableLayer; import org.geotoolkit.sld.MutableLayerStyle; import org.geotoolkit.sld.MutableNamedLayer; import org.geotoolkit.sld.MutableStyledLayerDescriptor; import org.geotoolkit.sld.MutableUserLayer; import org.geotoolkit.sld.xml.Specification.StyledLayerDescriptor; import org.geotoolkit.sld.xml.Specification.SymbologyEncoding; import org.geotoolkit.sld.xml.StyleXmlIO; import org.geotoolkit.style.MutableFeatureTypeStyle; import org.geotoolkit.style.MutableStyle; import org.geotoolkit.style.MutableStyleFactory; import org.opengis.parameter.ParameterValue; import org.opengis.parameter.ParameterValueGroup; import org.opengis.util.FactoryException; import javax.xml.bind.JAXBException; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import static org.constellation.provider.sld.SLDProviderFactory.FOLDER_DESCRIPTOR; /** * Style provider. index and cache MutableStyle within the given folder. * * @version $Id$ * * @author Johann Sorel (Geomatys) */ public class SLDProvider extends AbstractStyleProvider{ private static final MutableStyleFactory SF = (MutableStyleFactory)FactoryFinder.getStyleFactory( new Hints(Hints.STYLE_FACTORY, MutableStyleFactory.class)); private static final Collection<String> MASKS = new ArrayList<>(); static{ MASKS.add(".xml"); MASKS.add(".sld"); } private final StyleXmlIO sldParser = new StyleXmlIO(); private File folder; private final Map<String,File> index = new ConcurrentHashMap<>(); private final Cache<String,MutableStyle> cache = new Cache<>(20, 20, true); protected SLDProvider(String providerId,final SLDProviderFactory service, final ParameterValueGroup source){ super(providerId,service,source); reload(); } File getFolder() { return folder; } /** * {@inheritDoc } */ @Override public Set<String> getKeys() { return index.keySet(); } /** * {@inheritDoc } */ @Override public MutableStyle get(final String key) { MutableStyle value = cache.peek(key); if (value == null) { final Cache.Handler<MutableStyle> handler = cache.lock(key); try { value = handler.peek(); if (value == null) { final File f = index.get(key); if(f != null){ final String baseErrorMsg = "[PROVIDER]> SLD Style "; //try SLD 1.1 try { final MutableStyledLayerDescriptor sld = sldParser.readSLD(f, StyledLayerDescriptor.V_1_1_0); value = getFirstStyle(sld); if(value != null){ value.setName(key); LOGGER.log(Level.FINE, "{0}{1} is an SLD 1.1.0", new Object[]{baseErrorMsg, key}); return value; } } catch (JAXBException ex) { /* dont log*/ } catch (FactoryException ex) { /* dont log*/ } //try SLD 1.0 try { final MutableStyledLayerDescriptor sld = sldParser.readSLD(f, StyledLayerDescriptor.V_1_0_0); value = getFirstStyle(sld); if(value != null){ value.setName(key); LOGGER.log(Level.FINE, "{0}{1} is an SLD 1.0.0", new Object[]{baseErrorMsg, key}); return value; } } catch (JAXBException ex) { /*dont log*/ } catch (FactoryException ex) { /* dont log*/ } //try UserStyle SLD 1.1 try { value = sldParser.readStyle(f, SymbologyEncoding.V_1_1_0); if(value != null){ value.setName(key); LOGGER.log(Level.FINE, "{0}{1} is a UserStyle SLD 1.1.0", new Object[]{baseErrorMsg, key}); return value; } } catch (JAXBException ex) { /*dont log*/ } catch (FactoryException ex) { /* dont log*/ } //try UserStyle SLD 1.0 try { value = sldParser.readStyle(f, SymbologyEncoding.SLD_1_0_0); if(value != null){ value.setName(key); LOGGER.log(Level.FINE, "{0}{1} is a UserStyle SLD 1.0.0", new Object[]{baseErrorMsg, key}); return value; } } catch (JAXBException ex) { /*dont log*/ } catch (FactoryException ex) { /* dont log*/ } //try FeatureTypeStyle SE 1.1 try { final MutableFeatureTypeStyle fts = sldParser.readFeatureTypeStyle(f, SymbologyEncoding.V_1_1_0); value = SF.style(); value.featureTypeStyles().add(fts); if(value != null){ value.setName(key); LOGGER.log(Level.FINE, "{0}{1} is FeatureTypeStyle SE 1.1", new Object[]{baseErrorMsg, key}); return value; } } catch (JAXBException ex) { /*dont log*/ } catch (FactoryException ex) { /* dont log*/ } //try FeatureTypeStyle SLD 1.0 try { final MutableFeatureTypeStyle fts = sldParser.readFeatureTypeStyle(f, SymbologyEncoding.SLD_1_0_0); value = SF.style(); value.featureTypeStyles().add(fts); if(value != null){ value.setName(key); LOGGER.log(Level.FINE, "{0}{1} is an FeatureTypeStyle SLD 1.0", new Object[]{baseErrorMsg, key}); return value; } } catch (JAXBException ex) { /*dont log*/ } catch (FactoryException ex) { /* dont log*/ } LOGGER.log(Level.WARNING, "{0}{1} could not be parsed", new Object[]{baseErrorMsg, key}); } } } finally { handler.putAndUnlock(value); } } return value; } @Override public synchronized void set(final String key, final MutableStyle style){ File f = index.get(key); if(f == null){ //file doesnt exist, create it f = new File(folder, key+ ".xml"); } final StyleXmlIO util = new StyleXmlIO(); try { util.writeStyle(f, style, StyledLayerDescriptor.V_1_1_0); index.put(key, f); cache.clear(); fireUpdateEvent(); } catch (JAXBException ex) { LOGGER.log(Level.WARNING, ex.getLocalizedMessage(), ex); } } @Override public synchronized void rename(final String key, final String newName){ final File f = index.get(key); if(index.containsKey(newName)){ throw new IllegalArgumentException("Style name "+ newName +" already used."); } if(f == null){ throw new IllegalArgumentException("Style "+ newName +" do not exist."); } final File newFile = new File(f.getParentFile(), newName+".xml"); f.renameTo(newFile); reload(); } @Override public synchronized void remove(final String key){ final File f = index.get(key); if(f != null){ f.delete(); reload(); fireUpdateEvent(); } } /** * {@inheritDoc } */ @Override public void reload() { synchronized(this){ index.clear(); cache.clear(); final ParameterValueGroup srcConfig = getSource().groups( getFactory().getStoreDescriptor().getName().getCode()).get(0); final ParameterValue param = srcConfig.parameter(FOLDER_DESCRIPTOR.getName().getCode()); if(param == null || param.getValue() == null){ getLogger().log(Level.WARNING,"Provided File path is not defined."); return; } folder = new File(param.stringValue()); if(folder == null || !folder.exists() || !folder.isDirectory()){ getLogger().log(Level.WARNING,"Provided File does not exits or is not a folder."); return; } visit(folder); } fireUpdateEvent(); } /** * {@inheritDoc } */ @Override public void dispose() { synchronized(this){ index.clear(); cache.clear(); } } private void visit(final File file) { if (file.isDirectory()) { final File[] list = file.listFiles(); if (list != null) { for (int i = 0; i < list.length; i++) { visit(list[i]); } } }else{ test(file); } } private void test(final File candidate){ if(candidate.isFile()){ final String fullName = candidate.getName(); final String lowerCase = fullName.toLowerCase(); if(lowerCase.startsWith(".")) return; for(final String mask : MASKS){ if(lowerCase.endsWith(mask)){ final String name = fullName.substring(0, fullName.length()-4); index.put(name, candidate); } } } } private static MutableStyle getFirstStyle(final MutableStyledLayerDescriptor sld){ if(sld == null) return null; for(final MutableLayer layer : sld.layers()){ if(layer instanceof MutableNamedLayer){ final MutableNamedLayer mnl = (MutableNamedLayer) layer; for(final MutableLayerStyle stl : mnl.styles()){ if(stl instanceof MutableStyle){ return (MutableStyle) stl; } } }else if(layer instanceof MutableUserLayer){ final MutableUserLayer mnl = (MutableUserLayer) layer; for(final MutableStyle stl : mnl.styles()){ return stl; } } } return null; } @Override public boolean isSensorAffectable() { return false; } }