/** * OrbisGIS is a java GIS application dedicated to research in GIScience. * OrbisGIS is developed by the GIS group of the DECIDE team of the * Lab-STICC CNRS laboratory, see <http://www.lab-sticc.fr/>. * * The GIS group of the DECIDE team is located at : * * Laboratoire Lab-STICC – CNRS UMR 6285 * Equipe DECIDE * UNIVERSITÉ DE BRETAGNE-SUD * Institut Universitaire de Technologie de Vannes * 8, Rue Montaigne - BP 561 56017 Vannes Cedex * * OrbisGIS is distributed under GPL 3 license. * * Copyright (C) 2007-2014 CNRS (IRSTV FR CNRS 2488) * Copyright (C) 2015-2017 CNRS (Lab-STICC UMR CNRS 6285) * * This file is part of OrbisGIS. * * OrbisGIS is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * OrbisGIS 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along with * OrbisGIS. If not, see <http://www.gnu.org/licenses/>. * * For more information, please consult: <http://www.orbisgis.org/> * or contact directly: * info_at_ orbisgis.org */ package org.orbisgis.legend.thematic.map; import org.orbisgis.legend.LookupFieldName; import org.orbisgis.legend.structure.parameter.ParameterVisitor; import org.orbisgis.legend.structure.recode.FieldAggregatorVisitor; import org.orbisgis.legend.structure.recode.KeysRetriever; import org.orbisgis.legend.structure.recode.SetFieldVisitor; import org.orbisgis.legend.thematic.SymbolParameters; import org.orbisgis.legend.thematic.SymbolizerLegend; import java.util.*; /** * This abstract class gathers methods that will be common to instances of unique value classifications and interval * classifications. It presents a mapping between the keys and configurations of the associated classification. * @author alexis */ public abstract class MappedLegend<T,U extends SymbolParameters> extends SymbolizerLegend implements Map<T,U>, LookupFieldName { private Comparator<T> comparator; /** * Apply the given visitor to all the Legend that are used in this {@code MappedLegend}. * @param rpv The visitor that will be used in each inner {@code MappedLegend}. */ public abstract void applyGlobalVisitor(ParameterVisitor rpv); /** * Search in this legend for a key that is not already used. If the given * key is not already in use, it is returned. Otherwise, a new one is * created. * * @param orig The original key on which to base the search * @return An unused key */ public abstract T getNotUsedKey(T orig); @Override public String getLookupFieldName(){ FieldAggregatorVisitor fav = new FieldAggregatorVisitor(); applyGlobalVisitor(fav); Set<String> fields = fav.getFields(); if(fields.size() > 1){ throw new IllegalStateException("We won't be able to handle a MappedLegend with two analysis fields."); } else { Iterator<String> it = fields.iterator(); if(it.hasNext()){ return it.next(); } else { return ""; } } } @Override public void setLookupFieldName(String field) { SetFieldVisitor sfv = new SetFieldVisitor(field); applyGlobalVisitor(sfv); } /** * Gets the keys currently used in the analysis. * @return The keys used in a Set of T. */ @Override public SortedSet<T> keySet() { KeysRetriever<T> kr; if(comparator != null){ kr = new KeysRetriever(comparator); } else { kr = new KeysRetriever(); } applyGlobalVisitor(kr); return kr.getKeys(); } /** * Returns the number of elements contained in this classification. * @return The number of elements contained in this classification. */ @Override public int size(){ return keySet().size(); } /** * Returns true if there is nothing in this map. * @return {@code true} if we don't have any key-value mapping. */ @Override public boolean isEmpty(){ return keySet().isEmpty(); } /** * Put all the entries found in the given map as entries in this MappedLegend. * @param input The input map. */ @Override public void putAll(Map<? extends T, ? extends U> input){ Set<? extends Map.Entry<? extends T, ? extends U>> entries = input.entrySet(); for(Map.Entry<? extends T, ? extends U> m : entries){ put(m.getKey(),m.getValue()); } } /** * Gets a {@code Set} representation of the key-value mapping we have in this {@code MappedLegend}. * @return The mapping in a set of {@code Map.Entry}. */ @Override public Set<Map.Entry<T, U>> entrySet(){ Set<T> keys = keySet(); HashSet<Map.Entry<T,U>> out = new HashSet<Map.Entry<T,U>>(); for(T s : keys){ Map.Entry<T, U> ent = new MappedLegendEntry(s, get(s)); out.add(ent); } return out; } /** * Removes all the entries in this classification. */ @Override public void clear() { Set<T> keys = keySet(); for (T next : keys) { remove(next); } } /** * Checks if {@code lp} is a value contained in this {@code MappedLegend} * @param lp The value we search * @return {@code true} if we have a mapping to {@code lp}. */ @Override public boolean containsValue(Object lp){ return values().contains(lp); } /** * Checks whether s is a key of this mapping. * @param s The key we search * @return true if we have a mapping with s as a key. * @throws ClassCastException if the key is of an inappropriate type for this map */ @Override public boolean containsKey(Object s){ return keySet().contains(s); } @Override public boolean equals(Object o){ if(o instanceof Map){ return entrySet().equals(((Map) o).entrySet()); } else { return false; } } /** * Gets all the LineParameters stored in this RecodedLine, default one excepted. * @return The {@link org.orbisgis.legend.thematic.LineParameters} in a Collection. */ public Collection<U> values(){ Set<T> keys = keySet(); LinkedList<U> out = new LinkedList<U>(); for(T s : keys){ out.add(get(s)); } return out; } /** * Sets the comparator we want to use for elements ordering. * @param comp The comparator we want to use. */ public void setComparator(Comparator<T> comp){ comparator = comp; } /** * Sets the fallback configuration according to the one given in argument. * @param fallback The new basis for the fallback configuration. */ public abstract void setFallbackParameters(U fallback); /** * Gets the configuration used to draw features we can't get a value for in the map in the style . * @return The fallback configuration. */ public abstract U getFallbackParameters(); /** * MapEntry dedicated to MappedLegend instances. */ protected class MappedLegendEntry implements Map.Entry<T, U>{ private U lp; private final T s; public MappedLegendEntry(T s, U lp){ this.s = s; this.lp = lp; } @Override public T getKey() { return s; } @Override public U getValue() { return lp; } @Override public U setValue(U value) { MappedLegend<T,U> outer = MappedLegend.this; if(value == null){ throw new NullPointerException("Null values are not allowed in RecodedLines."); } outer.put(s,value); U ret = lp; lp = value; return ret; } @Override public boolean equals(Object o){ if(o instanceof Map.Entry){ Map.Entry me = (Map.Entry) o; return (s == null? me.getKey() == null : s.equals(me.getKey())) && (lp == null ? me.getValue() == null : lp.equals(me.getValue())); } else { return false; } } @Override public int hashCode(){ return (s==null ? 0 : s.hashCode()) ^ (lp==null ? 0 : lp.hashCode()); } } /** * This comparator intends to transform the input String instances * to Double instances and to compare them thereafter. Use with care. */ public static class NumComparator implements Comparator<String> { @Override public int compare(String integer, String integer2) { Double i1 = Double.valueOf(integer); Double i2 = Double.valueOf(integer2); return i1.compareTo(i2); } } }