1   /**
2    * Distribution License:
3    * JSword is free software; you can redistribute it and/or modify it under
4    * the terms of the GNU Lesser General Public License, version 2.1 as published by
5    * the Free Software Foundation. This program is distributed in the hope
6    * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7    * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8    * See the GNU Lesser General Public License for more details.
9    *
10   * The License is available on the internet at:
11   *       http://www.gnu.org/copyleft/lgpl.html
12   * or by writing to:
13   *      Free Software Foundation, Inc.
14   *      59 Temple Place - Suite 330
15   *      Boston, MA 02111-1307, USA
16   *
17   * Copyright: 2005
18   *     The copyright to this program is held by it's authors.
19   *
20   * ID: $Id: MapTableModel.java 2230 2012-02-08 00:00:10Z dmsmith $
21   */
22  package org.crosswire.common.swing;
23  
24  import java.io.IOException;
25  import java.io.ObjectInputStream;
26  import java.io.Serializable;
27  import java.util.ArrayList;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  
32  import javax.swing.table.AbstractTableModel;
33  
34  import org.crosswire.common.util.Convert;
35  
36  /**
37   * TableModel using a Map internally. Note that an AbstractTableModel (this is-a
38   * AbstractTableModel) reports changes to the data to the table itself. However
39   * since a Map does not have a addChangeListener interface we can't do the same
40   * - SO if you change the Map while we are displaying it then don't expect the
41   * changes to be automatically reflected in the JTable.
42   * 
43   * @see gnu.lgpl.License for license details.<br>
44   *      The copyright to this program is held by it's authors.
45   * @author Joe Walker [joe at eireneh dot com]
46   */
47  public class MapTableModel extends AbstractTableModel {
48      /**
49       * Create an internal store from a 2D array
50       */
51      public MapTableModel() {
52          this(null);
53      }
54  
55      /**
56       * Create an internal store from a 2D array
57       * 
58       * @param map
59       *            The table to model
60       */
61      public MapTableModel(Map map) {
62          list = new ArrayList<StringPair>();
63          setMap(map);
64      }
65  
66      /**
67       * Change the map that we report on
68       * 
69       * @param map
70       *            The map we are getting our data from
71       */
72      public final void setMap(Map<Object, Object> map) {
73          this.map = map;
74          list.clear();
75          if (map != null) {
76              Iterator<Map.Entry<Object, Object>> iter = map.entrySet().iterator();
77              while (iter.hasNext()) {
78                  Map.Entry<Object, Object> me = iter.next();
79                  Object k = me.getKey();
80                  Object v = me.getValue();
81                  if (k == null || v == null) {
82                      // perhaps log a warning?
83                      continue;
84                  }
85                  String key = k.toString().trim();
86                  String value = v.toString().trim();
87                  if (key.length() == 0) {
88                      // perhaps log a warning?
89                      continue;
90                  }
91                  list.add(new StringPair(key, value));
92              }
93          }
94          fireTableDataChanged();
95      }
96  
97      /**
98       * @param key
99       * @param aValue
100      */
101     public void add(String key, String aValue) {
102         String value = aValue;
103         if (value == null) {
104             value = "";
105         }
106 
107         if (key == null || key.length() == 0) {
108             return;
109         }
110 
111         if (map.containsKey(key) && value.equals(map.get(key))) {
112             return;
113         }
114 
115         map.put(key, value);
116         setMap(map);
117     }
118 
119     /**
120      * @param key
121      */
122     public void remove(String key) {
123         if (map != null) {
124             map.remove(key);
125             setMap(map);
126         }
127     }
128 
129     /**
130      * @param oldkey
131      * @param newkey
132      * @param newvalue
133      */
134     public void update(String oldkey, String newkey, String newvalue) {
135         if (map != null) {
136             if (!oldkey.equals(newkey)) {
137                 map.remove(oldkey);
138             }
139 
140             add(newkey, newvalue);
141         }
142     }
143 
144     /**
145      * Return a string version of the current value
146      * 
147      * @return The current value
148      */
149     public String getValue() {
150         return Convert.map2String(map);
151     }
152 
153     /**
154      * How many Cols are there in this store
155      * 
156      * @return The number of columns
157      */
158     public int getColumnCount() {
159         if (list == null) {
160             return 0;
161         }
162         return 2;
163     }
164 
165     /**
166      * How many Rows are there in this store
167      * 
168      * @return the number of row in the TableModel = elements in the map
169      */
170     public int getRowCount() {
171         if (list == null) {
172             return 0;
173         }
174         return list.size();
175     }
176 
177     /**
178      * Return the Object at row, col
179      * 
180      * @param row
181      *            The element in the list
182      * @param col
183      *            1=keys, 2=values
184      * @return The key/value of the given element
185      */
186     public Object getValueAt(int row, int col) {
187         if (list == null) {
188             return null;
189         }
190 
191         StringPair entry = list.get(row);
192         if (col == 0) {
193             return entry.getKey();
194         }
195         return entry.getValue();
196     }
197 
198     /**
199      * Get the default class
200      * 
201      * @param col
202      *            1=keys, 2=values
203      * @return String.class
204      */
205     @Override
206     public Class<String> getColumnClass(int col) {
207         return String.class;
208     }
209 
210     /**
211      * The name of the of the <code>col</code>th column
212      * 
213      * @param col
214      *            The column index
215      * @return The column name
216      */
217     @Override
218     public String getColumnName(int col) {
219         return colNames[col];
220     }
221 
222     /**
223      * The name of the of the <code>col</code>th column
224      * 
225      * @param col
226      *            The column index
227      * @param name
228      *            The column name
229      */
230     public void setColumnName(int col, String name) {
231         colNames[col] = name;
232     }
233 
234     /**
235      * Serialization support.
236      * 
237      * @param is
238      * @throws IOException
239      * @throws ClassNotFoundException
240      */
241     private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException {
242         list = new ArrayList<StringPair>();
243         is.defaultReadObject();
244     }
245 
246     /**
247      * The List that is a copy of the list. A list is used for direct access
248      * performance.
249      */
250     private transient List<StringPair> list;
251 
252     /**
253      * The backing map
254      */
255     private Map<Object, Object> map;
256 
257     /**
258      * The default column names
259      */
260     private String[] colNames = new String[] {
261             CWOtherMsg.lookupText("Keys"), CWOtherMsg.lookupText("Values"),
262     };
263 
264     /**
265      * Serialization ID
266      */
267     private static final long serialVersionUID = 3546365041277352241L;
268 
269     /**
270      * A simple holder of a key/value pair of Strings.
271      */
272     private static final class StringPair implements Serializable {
273         /**
274          * @param k
275          *            The non-null key.
276          * @param v
277          *            The non-null value.
278          */
279         public StringPair(String k, String v) {
280             key = k;
281             value = v;
282         }
283 
284         /**
285          * @return Returns the key.
286          */
287         public String getKey() {
288             return key;
289         }
290 
291         /**
292          * @return Returns the value.
293          */
294         public String getValue() {
295             return value;
296         }
297 
298         /**
299          * <code>key</code> is the string representation of a Map entry key
300          */
301         private String key;
302 
303         /**
304          * <code>value</code> is the string representation of a Map entry value
305          */
306         private String value;
307 
308         /**
309          * Serialization ID
310          */
311         private static final long serialVersionUID = 1730905297956834949L;
312 
313     }
314 }
315