Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/eireneh/config/Config.java


1   
2   package com.eireneh.config;
3   
4   import java.io.*;
5   import java.util.*;
6   import java.net.*;
7   
8   import com.eireneh.config.choices.*;
9   import com.eireneh.util.*;
10  
11  /**
12   * Config is the core part of the configuration system; it is simply a
13   * Collection of <code>Choice</code>s.
14   *
15   * <p>Config does the following things:<ul>
16   *   <li>Provides a GUI independant API with which to create GUIs</li>
17   *   <li>Stores a local store of settings</li>
18   *   <li>Allows updates to the local store</li>
19   * </ul></p>
20   *
21   * <p>Config does not attempt to make permanent copies of the config data
22   * because different apps may wish to store the data in different ways.
23   * Possible storage mechanisms include:<ul>
24   *   <li>Properties Files</li>
25   *   <li>Resource Objects (Merlin, JDK 1.4)</li>
26   *   <li>Network Sockets (see Remote)</li>
27   * </ul></p>
28   *
29   * The Config class stored the current Choices, and moves the data
30   * between the various places that it is stored. There are 4 storage
31   * areas:<ul>
32   * <li><b>Permanent:</b> This can be local file, a URL, or a remote server
33   *     Data is stored here between invocations of the program.
34   * <li><b>Application:</b> This is the actual working copy of the data.
35   * <li><b>Screen:</b> This copy of the data is shown on screen whist a
36   *     Config dialog box is showing.
37   * <li><b>Local:</b> This is required so that we can tell which bits of
38   *     data have been changed in the screen data, and so that we can
39   *     load data from disk to screen without involving the app.
40   * </ul>
41   * TODO: Questions that fail on load - ask
42   * TODO: I18N
43   *
44   * <table border='1' cellPadding='3' cellSpacing='0' width="100%">
45   * <tr><td bgColor='white'class='TableRowColor'><font size='-7'>
46   * Distribution Licence:<br />
47   * Project B is free software; you can redistribute it
48   * and/or modify it under the terms of the GNU General Public License,
49   * version 2 as published by the Free Software Foundation.<br />
50   * This program is distributed in the hope that it will be useful,
51   * but WITHOUT ANY WARRANTY; without even the implied warranty of
52   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
53   * General Public License for more details.<br />
54   * The License is available on the internet
55   * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, by writing to
56   * <i>Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
57   * MA 02111-1307, USA</i>, Or locally at the Licence link below.<br />
58   * The copyright to this program is held by it's authors.
59   * </font></td></tr></table>
60   * @see <a href='http://www.eireneh.com/servlets/Web'>Project B Home</a>
61   * @see docs.Licence
62   * @author Joe Walker
63   */
64  public class Config implements Serializable
65  {
66      /**
67       * Ensure that we can not be instansiated
68       * @param title The name for dialog boxes and properties files
69       */
70      public Config(String title)
71      {
72          this.title = title;
73      }
74  
75      /**
76       */
77      public String getTitle()
78      {
79          return title;
80      }
81  
82      /**
83       * Add a key/model pairing
84       * @param key The new name
85       * @param model The Field model to map to the key
86       */
87      public void add(String key, Choice model)
88      {
89          log.config("Adding key="+key);
90  
91          keys.addElement(key);
92          models.addElement(model);
93  
94          String value = model.getString();
95          if (value == null)
96          {
97              value = "";
98              log.config("key="+key+" has a null value");
99          }
100 
101         local.put(key, value);
102 
103         fireChoiceAdded(key, model);
104     }
105 
106     /**
107      * Remove a key/model pairing
108      * @param key The name to kill
109      */
110     public void remove(String key)
111     {
112         Choice model = getChoice(key);
113         keys.removeElement(key);
114         models.removeElement(model);
115 
116         // Leave the pair in local?
117         //local.put(key, value);
118 
119         fireChoiceRemoved(key, model);
120     }
121 
122     /**
123      * The set of Choice Names that we are controlling
124      * @return An enumeration over the keys
125      */
126     public Enumeration getPaths()
127     {
128         Vector paths = new Vector();
129 
130         Enumeration en = keys.elements();
131         while (en.hasMoreElements())
132         {
133             String key = (String) en.nextElement();
134             String path = getPath(key);
135 
136             if (!paths.contains(path))
137                 paths.addElement(path);
138         }
139 
140         return paths.elements();
141     }
142 
143     /**
144      * The set of Choice Names that we are controlling
145      * @return An enumeration over the keys
146      */
147     public Enumeration getNames()
148     {
149         return keys.elements();
150     }
151 
152     /**
153      * Step through the keys
154      * @return an enum of the keys
155      */
156     public Choice getChoice(String key)
157     {
158         int index = keys.indexOf(key);
159         if (index == -1)
160             return null;
161 
162         return (Choice) models.elementAt(index);
163     }
164 
165     /**
166      * The number of Choices
167      * @return The number of Choices
168      */
169     public int size()
170     {
171         return keys.size();
172     }
173 
174     /**
175      * Set a configuration Choice (by name) to a new value. This method
176      * is only of use to classes displaying config information
177      */
178     public void setLocal(String name, String value)
179     {
180         local.put(name, value);
181     }
182 
183     /**
184      * Get a configuration Choice (by name). This method
185      * is only of use to classes displaying config information
186      */
187     public String getLocal(String name)
188     {
189         return local.getProperty(name);
190     }
191 
192     /**
193      * Take the data in the application and copy it to the local
194      * storage area.
195      */
196     public void applicationToLocal()
197     {
198         Enumeration en = keys.elements();
199         while (en.hasMoreElements())
200         {
201             String key = (String) en.nextElement();
202 
203             try
204             {
205                 Choice model = getChoice(key);
206                 String value = model.getString();
207                 local.put(key, value);
208             }
209             catch (Throwable ex)
210             {
211                 log.warning("Failure with setting "+key);
212                 Reporter.informUser(this, ex);
213             }
214         }
215     }
216 
217     /**
218      * Take the data in the local storage area and copy it to the
219      * application.
220      * @param force If the new value is the same as the current do we set anyway
221      */
222     public void localToApplication(boolean force)
223     {
224         int highest_change = Choice.PRIORITY_LOWEST;
225 
226         if (force)
227             log.config("Force=true, all changes will propogate regardless");
228 
229         for (int priority=Choice.PRIORITY_SYSTEM; priority>=Choice.PRIORITY_LOWEST; priority--)
230         {
231             log.config("Settings for priority level="+priority);
232 
233             Enumeration en = keys.elements();
234             while (en.hasMoreElements())
235             {
236                 String key = (String) en.nextElement();
237                 Choice model = getChoice(key);
238 
239                 if (model.priority() == priority)
240                 {
241                     String old_value = model.getString();
242                     String new_value = local.getProperty(key);
243 
244                     // The new value shouldn't really be blank - obviously this
245                     // choice has just been added, substitute the default.
246                     if (new_value == null)
247                     {
248                         local.put(key, old_value);
249                         new_value = old_value;
250                     }
251 
252                     try
253                     {
254                         // If a value has not changed, we only call setString()
255                         // if force==true or if a higher priority choice has
256                         // changed.
257                         if (force ||
258                             priority < highest_change ||
259                             !new_value.equals(old_value))
260                         {
261                             log.config("Setting "+key+"="+new_value+" (was "+old_value+")");
262                             model.setString(new_value);
263 
264                             if (priority > highest_change)
265                             {
266                                 highest_change = priority;
267 
268                                 if (!force)
269                                     log.config("Change at level "+highest_change+", all changes will propogate regardless");
270                             }
271                         }
272                     }
273                     catch (Throwable ex)
274                     {
275                         log.warning("Failure with "+key+"="+new_value);
276                         Reporter.informUser(this, ex);
277                     }
278                 }
279             }
280         }
281     }
282 
283     /**
284      * Take the data stored permanetly and copy it to the local
285      * storage area, using the specified stream
286      */
287     public void setProperties(Properties prop)
288     {
289         Enumeration en = prop.keys();
290         while (en.hasMoreElements())
291         {
292             String key = (String) en.nextElement();
293             String value = prop.getProperty(key);
294 
295             if (value != null)
296                 local.put(key, value);
297         }
298     }
299 
300     /**
301      * Take the data in the local storage area and store it permanently,
302      * using the specified stream.
303      * @param out an output stream.
304      */
305     public Properties getProperties()
306     {
307         Properties prop = new Properties();
308 
309         Enumeration en = keys.elements();
310         while (en.hasMoreElements())
311         {
312             String key = (String) en.nextElement();
313             String value = local.getProperty(key);
314 
315             Choice model = getChoice(key);
316             if (model.isSaveable())
317                 prop.put(key, value);
318             else
319                 prop.remove(key);
320         }
321 
322         return prop;
323     }
324 
325     /**
326      * Take the data stored permanently and copy it to the local
327      * storage area, using the configured storage area
328      */
329     public void permanentToLocal(URL url) throws IOException
330     {
331         InputStream in = url.openStream();
332 
333         Properties prop = new Properties();
334         PropertiesUtil.load(prop, in);
335 
336         setProperties(prop);
337     }
338 
339     /**
340      * Take the data in the local storage area and store it permanently,
341      * using the configured storage area.
342      */
343     public void localToPermanent(URL url) throws IOException
344     {
345         File file = new File(url.getFile());
346         OutputStream out = new FileOutputStream(file);
347 
348         // Send our updates
349         PropertiesUtil.save(getProperties(), out, title);
350     }
351 
352     /**
353      * Take the data stored permanently and copy it to the local
354      * storage area, using the configured storage area
355      */
356     public void permanentToLocal(String host, int port) throws IOException
357     {
358         try
359         {
360             Socket sock = new Socket(host, port);
361             InputStream in = sock.getInputStream();
362             ObjectInputStream sin = new ObjectInputStream(in);
363             Config config = (Config) sin.readObject();
364 
365             Properties prop = new Properties();
366             PropertiesUtil.load(prop, in);
367 
368             // Politeness: Send nothing to the server in return.
369             PropertiesUtil.save(new Properties(), sock.getOutputStream(), "Dump");
370             sock.close();
371 
372             setProperties(prop);
373         }
374         catch (ClassNotFoundException ex)
375         {
376             throw new IOException("Serialization Error: "+ex);
377         }
378     }
379 
380     /**
381      * Take the data in the local storage area and store it permanently,
382      * using the configured storage area.
383      */
384     public void localToPermanent(String host, int port) throws IOException
385     {
386         try
387         {
388             Socket sock = new Socket(host, port);
389             OutputStream out = sock.getOutputStream();
390 
391             // Politeness: Read the stuff the server sends to us, but ignore it.
392             InputStream in = sock.getInputStream();
393 
394             ObjectInputStream sin = new ObjectInputStream(in);
395             Config config = (Config) sin.readObject();
396 
397             PropertiesUtil.load(new Properties(), in);
398 
399             // Send our updates
400             PropertiesUtil.save(getProperties(), out, title);
401 
402             sock.close();
403         }
404         catch (ClassNotFoundException ex)
405         {
406             throw new IOException("Serialization Error");
407         }
408     }
409 
410     /**
411      * What is the Path of this key
412      */
413     public static String getPath(String key)
414     {
415         int last_dot = key.lastIndexOf('.');
416         if (last_dot == -1)
417             throw new IllegalArgumentException("key="+key+" does not contain a dot.");
418 
419         return key.substring(0, last_dot);
420     }
421 
422     /**
423      * What is the Path of this key
424      */
425     public static String getLeaf(String key)
426     {
427         int last_dot = key.lastIndexOf('.');
428         if (last_dot == -1)
429             throw new IllegalArgumentException("key="+key+" does not contain a dot.");
430 
431         return key.substring(last_dot+1);
432     }
433 
434     /**
435      * Add an Exception listener to the list of things wanting
436      * to know whenever we capture an Exception
437      */
438     public void addConfigListener(ConfigListener li)
439     {
440         listener_list.add(ConfigListener.class, li);
441     }
442 
443     /**
444      * Remove an Exception listener from the list of things wanting
445      * to know whenever we capture an Exception
446      */
447     public void removeConfigListener(ConfigListener li)
448     {
449         listener_list.remove(ConfigListener.class, li);
450     }
451 
452     /**
453      * A Choice got added
454      */
455     protected void fireChoiceAdded(String key, Choice model)
456     {
457         // Guaranteed to return a non-null array
458         Object[] listeners = listener_list.getListenerList();
459 
460         // Process the listeners last to first, notifying
461         // those that are interested in this event
462         ConfigEvent ev = null;
463         for (int i=listeners.length-2; i>=0; i-=2)
464         {
465             if (listeners[i] == ConfigListener.class)
466             {
467                 if (ev == null)
468                     ev = new ConfigEvent(this, key, model);
469 
470                 ((ConfigListener) listeners[i+1]).choiceAdded(ev);
471             }
472         }
473     }
474 
475     /**
476      * A Choice got added
477      */
478     protected void fireChoiceRemoved(String key, Choice model)
479     {
480         // Guaranteed to return a non-null array
481         Object[] listeners = listener_list.getListenerList();
482 
483         // Process the listeners last to first, notifying
484         // those that are interested in this event
485         ConfigEvent ev = null;
486         for (int i=listeners.length-2; i>=0; i-=2)
487         {
488             if (listeners[i] == ConfigListener.class)
489             {
490                 if (ev == null)
491                     ev = new ConfigEvent(this, key, model);
492 
493                 ((ConfigListener) listeners[i+1]).choiceRemoved(ev);
494             }
495         }
496     }
497 
498     /** The log stream */
499     protected static Logger log = Logger.getLogger("util.config");
500 
501     /** The name for dialog boxes and properties files */
502     protected String title;
503 
504     /** The array that stores the keys */
505     protected RobustList keys = new RobustList();
506 
507     /** The array that stores the models */
508     protected RobustList models = new RobustList();
509 
510     /** The set of local values */
511     protected Properties local = new Properties();
512 
513     /** The list of listeners */
514     protected EventListenerList listener_list = new EventListenerList();
515 }