001/* ===========================================================
002 * Orson Charts : a 3D chart library for the Java(tm) platform
003 * ===========================================================
004 * 
005 * (C)opyright 2013-2016, by Object Refinery Limited.  All rights reserved.
006 * 
007 * http://www.object-refinery.com/orsoncharts/index.html
008 * 
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as published by
011 * the Free Software Foundation, either version 3 of the License, or
012 * (at your option) any later version.
013 *
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 *
019 * You should have received a copy of the GNU General Public License
020 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
021 * 
022 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
023 * Other names may be trademarks of their respective owners.]
024 * 
025 * If you do not wish to be bound by the terms of the GPL, an alternative
026 * commercial license can be purchased.  For details, please see visit the
027 * Orson Charts home page:
028 * 
029 * http://www.object-refinery.com/orsoncharts/index.html
030 * 
031 */
032
033package com.orsoncharts.data;
034
035import java.io.Serializable;
036import java.util.ArrayList;
037import java.util.List;
038
039import com.orsoncharts.util.ArgChecks;
040
041/**
042 * A list of {@code (key, value)} pairs.
043 * <br><br>
044 * This is the basic structure of the data required for a pie chart.
045 * <br><br>
046 * NOTE: This class is serializable, but the serialization format is subject 
047 * to change in future releases and should not be relied upon for persisting 
048 * instances of this class. 
049 */
050@SuppressWarnings("serial")
051public final class DefaultKeyedValues<T> implements KeyedValues<T>, 
052        Serializable {
053
054    /** Storage for the data items. */
055    private List<KeyedValue<T>> data;
056  
057    /**
058     * Creates a new (empty) list of keyed values.
059     */
060    public DefaultKeyedValues() {
061        this(new ArrayList<Comparable<?>>());
062    }
063  
064    /**
065     * Creates a new instance with the specified keys (each associated with
066     * a {@code null} value).  There is usually no need to specify any
067     * keys in advance, so you will normally use the default constructor.  This
068     * constructor is provided for the convenience of some internal code.
069     * 
070     * @param keys  the keys ({@code null} not permitted).
071     */
072    public DefaultKeyedValues(List<Comparable<?>> keys) {
073        ArgChecks.nullNotPermitted(keys, "keys");
074        this.data = new ArrayList<KeyedValue<T>>();
075        for (Comparable<?> key : keys) {
076            this.data.add(new DefaultKeyedValue<T>(key, null));
077        }
078    }
079  
080    /**
081     * Clears all the data.
082     */
083    public void clear() {
084        this.data.clear();
085    }
086 
087    /**
088     * Adds a value or, if there is an existing value with the same key, updates 
089     * an existing value.
090     * 
091     * @param key  the key ({@code null} not permitted)
092     * @param value  the value.
093     */
094    public void put(Comparable<?> key, T value) {
095        ArgChecks.nullNotPermitted(key, "key");
096        DefaultKeyedValue<T> dkv;
097        int index = getIndex(key);
098        if (index >= 0) {
099            dkv = (DefaultKeyedValue<T>) this.data.get(index);
100            dkv.setValue(value);
101        } else {
102            this.data.add(new DefaultKeyedValue<T>(key, value));
103        }
104    }
105  
106    /**
107     * Removes the item with the specified key, if there is one.
108     * 
109     * @param key  the key ({@code null} not permitted).
110     */
111    public void remove(Comparable<?> key) {
112        ArgChecks.nullNotPermitted(key, "key");
113        int index = getIndex(key);
114        if (index >= 0) {
115            remove(index);
116        }
117    }
118  
119    /**
120     * Removes the item with the specified index.
121     * 
122     * @param index  the index. 
123     */
124    public void remove(int index) {
125        this.data.remove(index);
126    }
127  
128    /**
129     * Returns the key for the item with the specified index.
130     * 
131     * @param index  the item index.
132     * 
133     * @return The key. 
134     */
135    @Override
136    public Comparable<?> getKey(int index) {
137        KeyedValue<T> kv = this.data.get(index);
138        return kv.getKey();
139    }
140
141    /**
142     * Returns the index of the item with the specified key, or {@code -1}
143     * if there is no such item.
144     * 
145     * @param key  the key ({@code null} not permitted).
146     * 
147     * @return The item index, or {@code -1}. 
148     */
149    @Override
150    public int getIndex(Comparable<?> key) {
151        ArgChecks.nullNotPermitted(key, "key");
152        for (int i = 0; i < this.data.size(); i++) {
153            KeyedValue<T> kv = this.data.get(i);
154            if (kv.getKey().equals(key)) {
155                return i;
156            }
157        }
158        return -1;
159    }
160
161    /**
162     * Returns a list of all the keys.  Note that the list will be a copy, so
163     * modifying it will not impact this data structure.
164     * 
165     * @return A list of keys (possibly empty, but never {@code null}).
166     */
167    @Override
168    public List<Comparable<?>> getKeys() {
169        List<Comparable<?>> keys = new ArrayList<Comparable<?>>();
170        for (KeyedValue<T> kv : this.data) {
171            keys.add(kv.getKey());
172        }
173        return keys;
174    }
175
176    /**
177     * Returns the value with the specified key.
178     * 
179     * @param key  the key ({@code null} not permitted).
180     * 
181     * @return The value (possibly {@code null}). 
182     */
183    @Override
184    public T getValue(Comparable<?> key) {
185        // arg checking is performed by getIndex()
186        int index = getIndex(key);
187        if (index < 0) {
188            return null;
189        }
190        return getValue(index);
191    }
192
193    /**
194     * Returns the number of items in the list.
195     * 
196     * @return The number of items in the list. 
197     */
198    @Override
199    public int getItemCount() {
200        return this.data.size();
201    }
202
203    /**
204     * Returns the value for the specified item.
205     * 
206     * @param item  the item index.
207     * 
208     * @return The value (possibly {@code null}). 
209     */
210    @Override
211    public T getValue(int item) {
212        KeyedValue<T> kv = this.data.get(item);
213        return kv.getValue();
214    }
215  
216    /**
217     * Returns the value for the specified item, as a double primitive, 
218     * provided that the data value is an instance of {@code Number}.
219     * 
220     * @param item  the item index.
221     * 
222     * @return The value.
223     */
224    @Override
225    public double getDoubleValue(int item) {  
226        T n = getValue(item);
227        if (n != null && n instanceof Number) {
228            return ((Number) n).doubleValue();
229        }
230        return Double.NaN;
231    }
232    
233    /**
234     * Tests this instance for equality with an arbitrary object.
235     * 
236     * @param obj  the object to test against ({@code null} permitted).
237     * 
238     * @return A boolean. 
239     */
240    @Override
241    public boolean equals(Object obj) {
242        if (obj == this) {
243            return true;
244        }
245        if (!(obj instanceof DefaultKeyedValues<?>)) {
246            return false;
247        }
248        DefaultKeyedValues<?> that = (DefaultKeyedValues<?>) obj;
249        if (!this.data.equals(that.data)) {
250            return false;
251        }
252        return true;
253    }
254}