/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2008 - 2009, 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.style; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Collection; import java.util.List; import java.util.Objects; import javax.swing.event.EventListenerList; import org.geotoolkit.gui.swing.tree.Trees; import org.apache.sis.measure.NumberRange; import org.geotoolkit.util.collection.CollectionChangeEvent; import org.geotoolkit.util.collection.NotifiedCheckedList; import org.apache.sis.util.Classes; import org.opengis.filter.Filter; import org.opengis.metadata.citation.OnlineResource; import org.opengis.style.Description; import org.opengis.style.GraphicLegend; import org.opengis.style.StyleVisitor; import org.opengis.style.Symbolizer; import static org.apache.sis.util.ArgumentChecks.*; /** * Mutable implementation of Types Rule. * * @author Johann Sorel (Geomatys) * @module */ public class DefaultMutableRule implements MutableRule{ private final List<Symbolizer> symbols = new NotifiedCheckedList<Symbolizer>(Symbolizer.class) { @Override protected Object getLock() { return DefaultMutableRule.this; } @Override protected void notifyAdd(final Symbolizer item, final int index) { fireSymbolizerChange(CollectionChangeEvent.ITEM_ADDED, item, NumberRange.create(index, true, index, true) ); } @Override protected void notifyAdd(final Collection<? extends Symbolizer> items, final NumberRange<Integer> range) { fireSymbolizerChange(CollectionChangeEvent.ITEM_ADDED, items, range); } @Override protected void notifyRemove(final Symbolizer item, final int index) { fireSymbolizerChange(CollectionChangeEvent.ITEM_REMOVED, item, NumberRange.create(index, true, index, true) ); } @Override protected void notifyRemove(final Collection<? extends Symbolizer> items, final NumberRange<Integer> range) { fireSymbolizerChange(CollectionChangeEvent.ITEM_REMOVED, items, range ); } @Override protected void notifyChange(Symbolizer oldItem, Symbolizer newItem, int index) { fireSymbolizerChange(CollectionChangeEvent.ITEM_CHANGED, oldItem, NumberRange.create(index, true, index, true) ); } }; private final EventListenerList listeners = new EventListenerList(); private String name = null; private Description desc = StyleConstants.DEFAULT_DESCRIPTION; private GraphicLegend legend = null; private Filter filter = null; private boolean isElse = false; private double minscale = 0; private double maxScale = Double.MAX_VALUE; private OnlineResource online = null; /** * Create a default mutable rule. */ public DefaultMutableRule(){} /** * {@inheritDoc } * This method is thread safe. */ @Override public String getName() { return name; } /** * Set the name of the rule. * This method is thread safe. */ @Override public void setName(final String name) { final String oldName; synchronized (this) { oldName = this.name; if (Objects.equals(oldName, name)) { return; } this.name = name; } firePropertyChange(NAME_PROPERTY, oldName, this.name); } /** * {@inheritDoc } * This method is thread safe. */ @Override public Description getDescription() { return desc; } /** * Set the Description of the rule. * @param desc : Description can't be null */ @Override public void setDescription(final Description desc){ ensureNonNull("description", desc); final Description oldDesc; synchronized (this) { oldDesc = this.desc; if(oldDesc.equals(desc)){ return; } this.desc = desc; } firePropertyChange(DESCRIPTION_PROPERTY, oldDesc, this.desc); } /** * {@inheritDoc } * This method is thread safe. */ @Override public GraphicLegend getLegend() { return legend; } /** * Set the graphic legend of the rule. * @param legend : can be null. */ @Override public void setLegendGraphic(final GraphicLegend legend){ final GraphicLegend oldLegend; synchronized (this) { oldLegend = this.legend; if(Objects.equals(oldLegend, legend)){ return; } this.legend = legend; } firePropertyChange(LEGEND_PROPERTY, oldLegend, this.legend); } /** * {@inheritDoc } * This method is thread safe. */ @Override public Filter getFilter() { return filter; } /** * Set the feature filter of the rule. * The filter will limit the features that will be displayed * using the underneath symbolizers. * * @param filter : can be null. */ @Override public void setFilter(final Filter filter){ final Filter oldFilter; synchronized (this) { oldFilter = this.filter; if(Objects.equals(oldFilter, filter)){ return; } this.filter = filter; } firePropertyChange(FILTER_PROPERTY, oldFilter, this.filter); } /** * {@inheritDoc } * This method is thread safe. */ @Override public boolean isElseFilter() { return isElse; } /** * Set the "else" flag of the filter. * If a ruma has this flag then it will used only for the * feature that no other rule handle. * */ @Override public void setElseFilter(final boolean isElse){ final boolean oldIsElse; synchronized (this) { oldIsElse = this.isElse; if(oldIsElse == isElse){ return; } this.isElse = isElse; } firePropertyChange(ISELSE_FILTER_PROPERTY, oldIsElse, this.isElse); } /** * {@inheritDoc } * This method is thread safe. */ @Override public double getMinScaleDenominator() { return minscale; } /** * Set the minimum scale on wich this rul apply. * if the display device is under this scale then this rule * will not be tested. */ @Override public void setMinScaleDenominator(final double minScale){ final double oldMinScale; synchronized (this) { oldMinScale = this.minscale; if(oldMinScale == minScale){ return; } this.minscale = minScale; } firePropertyChange(MINIMUM_SCALE_PROPERTY, oldMinScale, this.minscale); } /** * {@inheritDoc } * This method is thread safe. */ @Override public double getMaxScaleDenominator() { return maxScale; } /** * Set the maximum scale on wich this rul apply. * if the display device is above this scale then this rule * will not be tested. */ @Override public void setMaxScaleDenominator(final double maxScale){ final double oldMaxScale; synchronized (this) { oldMaxScale = this.maxScale; if(oldMaxScale == maxScale){ return; } this.maxScale = maxScale; } firePropertyChange(MAXIMUM_SCALE_PROPERTY, oldMaxScale, this.maxScale); } /** * * @return live list */ @Override public List<Symbolizer> symbolizers() { return symbols; } /** * {@inheritDoc } * This method is thread safe. */ @Override public OnlineResource getOnlineResource() { return online; } /** * {@inheritDoc } * This method is thread safe. */ @Override public void setOnlineResource(final OnlineResource online) { final OnlineResource oldOnline; synchronized (this) { oldOnline = this.online; if(Objects.equals(oldOnline, online)){ return; } this.online = online; } firePropertyChange(ONLINE_PROPERTY, oldOnline, this.online); } /** * {@inheritDoc } */ @Override public Object accept(final StyleVisitor visitor, final Object extraData) { return visitor.visit(this,extraData); } /** * {@inheritDoc } */ @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("Rule : "); builder.append(Classes.getShortClassName(this)); builder.append(" ["); builder.append(desc); builder.append(']'); if(!symbols.isEmpty()){ builder.append(Trees.toString("", symbols)); } return builder.toString(); } //-------------------------------------------------------------------------- // listeners management ---------------------------------------------------- //-------------------------------------------------------------------------- protected void firePropertyChange(final String propertyName, final Object oldValue, final Object newValue){ //TODO make fire property change thread safe, preserve fire order final PropertyChangeEvent event = new PropertyChangeEvent(this,propertyName,oldValue,newValue); final PropertyChangeListener[] lists = listeners.getListeners(PropertyChangeListener.class); for(PropertyChangeListener listener : lists){ listener.propertyChange(event); } } protected void fireSymbolizerChange(final int type, final Symbolizer symbol, final NumberRange<Integer> range) { //TODO make fire property change thread safe, preserve fire order final CollectionChangeEvent<Symbolizer> event = new CollectionChangeEvent<Symbolizer>(this, symbol, type, range, null); final RuleListener[] lists = listeners.getListeners(RuleListener.class); for (RuleListener listener : lists) { listener.symbolizerChange(event); } } protected void fireSymbolizerChange(final int type, final Collection<? extends Symbolizer> symbol, final NumberRange<Integer> range){ //TODO make fire property change thread safe, preserve fire order final CollectionChangeEvent<Symbolizer> event = new CollectionChangeEvent<Symbolizer>(this,symbol,type,range, null); final RuleListener[] lists = listeners.getListeners(RuleListener.class); for(RuleListener listener : lists){ listener.symbolizerChange(event); } } /** * {@inheritDoc } */ @Override public void addListener(final RuleListener listener){ addListener((PropertyChangeListener)listener); } @Override public void addListener(PropertyChangeListener listener) { addPropertyChangeListener(listener); } @Override public void removeListener(PropertyChangeListener listener) { removePropertyChangeListener(listener); } /** * {@inheritDoc } */ @Override public void addPropertyChangeListener(final PropertyChangeListener listener){ listeners.add(PropertyChangeListener.class, listener); if(listener instanceof RuleListener){ listeners.add(RuleListener.class, (RuleListener)listener); } } /** * {@inheritDoc } */ @Override public void removePropertyChangeListener(final PropertyChangeListener listener){ listeners.remove(PropertyChangeListener.class, listener); if(listener instanceof RuleListener){ listeners.remove(RuleListener.class, (RuleListener)listener); } } }