package misc; import java.awt.Component; import java.awt.Point; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import java.text.DecimalFormat; import java.text.MessageFormat; import java.util.Date; import java.util.Locale; import java.util.Properties; import java.util.Vector; import javax.swing.JComboBox; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.ListModel; /** Managed the persistance key/value strings association. This class could be used to managed all constants values in a application. The values commme from a bundle jar (mix of class and data) or a dedicated directeory. */ public class Config { static public final String FS = System.getProperty ("file.separator"); static public final String dataDirname = "data", configDirname = "config", textsDirname = "texts", logDirname = "log", imagesDirname = "images", iconsDirname = "images", buttonDirname = "button", flagsDirname = "flags"; /** Path to configuration files in files system. */ static public final String configSystemDir = dataDirname+FS+configDirname+FS, logSystemDir = dataDirname+FS+logDirname+FS, buttonSystemDir = dataDirname+FS+imagesDirname+FS+buttonDirname+FS; /** Path to configuration files in jar. */ static public final String configJarDir = dataDirname+"/"+configDirname+"/", imagesJarDir = dataDirname+"/"+imagesDirname+"/", textsJarDir = dataDirname+"/"+textsDirname+"/"; /** Configuration file extension. */ static public final String configExt = ".xml", iconsExt = ".png", imagesExt = ".png", htmlExt = ".html", logExt = ".log"; /** Enconding charset for saving configuration. */ static public final String configEncoding = "UTF8"; /** Comment used as header configuration file. */ static public final String configHeaderFile = "This file is automaticaly generated by {0} application at {1,time,short} on {1,date}."; /** Somme postfix for constants name in configuration file. */ static public final String checkedPostfix = "Checked", undockedPostfix = "Undocked", locationPostfix = "Location"; /** Message shows when trying to get string with a null key. */ static private final String configNullKeyException = "No configuration associates with null key."; /** Message shows when trying to use configuration before load it. */ static private final String configUseException = "No configuration loaded."; /** Message shows when load exception appears. */ static private final String configLoadException = "Configuration {0} can''t be load (corrupted file ?)."; /** Message shows when save exception appears. */ static private final String configSaveException = "Configuration {0} can''t be save."; /** Liste of constants (key/value strings association). */ //static private Properties configuration; static public Properties configuration; /** True if configuration need to be saved. */ static private boolean configurationModified = false; // ======================================== static private File PWD; static private String[] dataPath; static { setPWD (Util.class); setDataPath (".:.."); } static public File getJarFileOrClassDir (Class applicationClass) { try { return new File (applicationClass.getProtectionDomain ().getCodeSource ().getLocation ().toURI ()); } catch (Exception e) { // dosn't work for applet return null; } } static public File getPWD () { return PWD; } // XXX static public String getPWD () { return PWD.getAbsolutePath (); } static public void setPWD (Class applicationClass) { try { PWD = getJarFileOrClassDir (applicationClass).getParentFile (); } catch (Exception e) { } } static public void setDataPath (String path) { dataPath = path.split (":"); } static public File findDataDir () { return findDataDir (false); } static public File findDataDir (boolean writable) { String name = dataDirname; try { // search from excecution dir File file = (new File (name)); if (file.exists () && file.isDirectory () && (!writable || file.canWrite ())) return file; } catch (Exception e) { } for (String path : dataPath) { try { // search in relative path from jar File file = (new File (PWD, path+FS+name)); if (file.exists () && file.isDirectory () && (!writable || file.canWrite ())) return file; } catch (Exception e) { } } return null; } static public URL getDataUrl (String... names) { if (names.length < 1) return null; String name = names[names.length - 1]; String jarPath = "", systemPath = ""; for (int i = 0; i < names.length - 1; i++) { jarPath += names[i]+"/"; systemPath += names[i]+FS; } try { // search from excecution dir File file = new File (systemPath+name); if (file.exists ()) return file.toURI ().toURL (); } catch (Exception e) { } for (String path : dataPath) { try { // search in relative path from jar File file = new File (PWD, path+FS+systemPath+name); if (file.exists ()) return file.getCanonicalFile ().toURI ().toURL (); } catch (Exception e) { } } try { // search in jar URL result = ClassLoader.getSystemResource (jarPath+name); return result; } catch (Exception e) { } return null; } // ======================================== /** Load XML configuration file contains constants (key/value strings association). @param applicationName the application name use to find file. */ static public final void load (String applicationName) { configuration = new Properties (); try { File configDir = new File (findDataDir (), configDirname); File configFile = new File (configDir, applicationName+configExt); FileInputStream fileInputStream = new FileInputStream (configFile); configuration.loadFromXML (fileInputStream); fileInputStream.close (); configurationModified = false; } catch (Exception e) { try { configuration.loadFromXML (ClassLoader.getSystemResourceAsStream (configJarDir+applicationName+configExt)); configurationModified = false; } catch (Exception e2) { try { configuration.loadFromXML (ClassLoader.getSystemResourceAsStream (configJarDir+applicationName+configExt)); configurationModified = false; } catch (Exception e3) { if (JOptionPane.YES_OPTION != JOptionPane.showConfirmDialog (null, "Would you like to continue?", " Can't load Configuration ", JOptionPane.YES_NO_OPTION)) throw new IllegalArgumentException (MessageFormat.format (configLoadException, applicationName)); } } } } // ======================================== /** Return the loading state of the configuration. @return true if the configuration is loaded. */ static public boolean isLoaded () { return configuration != null; } // ======================================== /** Save XML configuration file contains key/value strings association. The file is not writing if no modification occure. @param applicationName the application name use to save file. */ static public final void save (String applicationName) { File configDir = new File (findDataDir (true), configDirname); File configFile = new File (configDir, applicationName+configExt); try { save (applicationName, configFile); } catch (IOException e) { try { e.printStackTrace (); configDir.mkdirs (); save (applicationName,configFile); } catch (IOException e2) { System.err.println (MessageFormat.format (configSaveException, applicationName)); } } catch (NullPointerException e) { throw new IllegalArgumentException (configUseException); } } static public final void save (String applicationName, File file) throws IOException { if (!configurationModified && file.exists ()) return; if (!file.exists ()) file.createNewFile (); FileOutputStream fileOutputStream = new FileOutputStream (file); configuration.storeToXML (fileOutputStream, (new MessageFormat (configHeaderFile, Locale.US)).format (new Object[] {applicationName, new Date ()}, new StringBuffer(), null).toString (), configEncoding); fileOutputStream.close (); configurationModified = false; } // ======================================== // XXX defaultValue peut être placé systématiquement (voir à null) /** Searches for the value with the specified key in this configuration list. @param key the name of the configuration parameter. @return the configuration value associated with the key or null if not found. */ static public final String getString (String key) { if (key == null) throw new IllegalArgumentException (configNullKeyException); try { return configuration.getProperty (key); } catch (NullPointerException e) { throw new IllegalArgumentException (configUseException); } } /** Searches for the value with the specified key in this configuration list. @param key the name of the configuration parameter. @param defaultValue the defaultValue in case the key is not found. @return the configuration value associated with the key or defaultValue if not found. */ static public final String getString (String key, String defaultValue) { String value = configuration.getProperty (key); if (value != null) return value; configuration.setProperty (key, defaultValue); return defaultValue; } /** Change the value with the specified key in this configuration list. @param key the name of the configuration parameter. @param value the new configuration value associated with the key. */ static public final void setString (String key, String value) { try { configuration.setProperty (key, value); configurationModified = true; } catch (NullPointerException e) { throw new IllegalArgumentException (configUseException); } } // ======================================== static public final File getFile (String key) { String fileName = getString (key); if (fileName == null) return null; return new File (fileName); } static public final void setFile (String key, File file) { setString (key, file.getPath ()); } // ======================================== static public final boolean getBoolean (String key) { return Boolean.parseBoolean (getString (key)); } static public final boolean getBoolean (String key, boolean defaultValue) { return Boolean.parseBoolean (getString (key, ""+defaultValue)); } static public final void setBoolean (String key, boolean value) { setString (key, ""+value); } // ======================================== static public final int getInt (String key) { return Integer.parseInt (getString (key)); } static public final int getInt (String key, int defaultValue) { try { return Integer.parseInt (getString (key, ""+defaultValue)); } catch (Exception e) { return defaultValue; } } static public final void setInt (String key, int value) { setString (key, ""+value); } // ======================================== static public final float getFloat (String key) { return Float.parseFloat (getString (key)); } static public final float getFloat (String key, float defaultValue) { try { return Float.parseFloat (getString (key, ""+defaultValue)); } catch (Exception e) { return defaultValue; } } static public final void setFloat (String key, float value) { setString (key, ""+value); } // ======================================== static public final double getDouble (String key) { return Double.parseDouble (getString (key)); } static public final double getDouble (String key, double defaultValue) { try { return Double.parseDouble (getString (key, ""+defaultValue)); } catch (Exception e) { return defaultValue; } } static public final void setDouble (String key, double value) { setString (key, ""+value); } // ======================================== static public final T getEnum (String key, T defaultValue) { return Util.toEnum (getString (key, ""+defaultValue), defaultValue); } // ======================================== static public final DecimalFormat indexFormat = new DecimalFormat ("-0000"); static public final Vector getList (String key, String defaultValue) { Vector result = new Vector (); int size = Integer.parseInt (getString (key+"-size", "1")); result.add (getString (key, defaultValue)); for (int i = 1; i < size; i++) result.add (getString (key+indexFormat.format (i))); return result; } static public final void setList (String key, Vector value) { // XXX suppression des anciennes valeurs int size = value.size (); int max = Integer.parseInt (getString (key+"-max", "10")); setString (key+"-max", ""+max); setString (key+"-size", ""+size); setString (key, value.elementAt (0)); size = Math.min (size, max); for (int i = 1; i < size; i++) setString (key+indexFormat.format (i), value.elementAt (i)); } // ======================================== // XXX faire un defaultValue en vector static public final void loadJList (String key, JList jList, String defaultValue) { Vector list = getList (key, defaultValue); jList.setListData (list); jList.setSelectedIndex (0); } static public final void saveJList (String key, JList jList) { Vector result = new Vector (); ListModel model = jList.getModel (); int size = model.getSize (); for (int i = 0; i < size; i++) result.add (model.getElementAt (i)); int index = jList.getSelectedIndex (); if (index >= 0) { result.insertElementAt (result.remove (index), 0); jList.setListData (result); jList.setSelectedIndex (0); } setList (key, result); } // ======================================== // XXX faire un defaultValue en vector static public final void loadJComboBox (String key, JComboBox jComboBox, String defaultValue) { Vector list = getList (key, defaultValue); jComboBox.removeAllItems (); for (int i = 0; i < list.size (); i++) jComboBox.addItem (list.elementAt (i)); jComboBox.setSelectedIndex (0); } static public final void loadJComboBoxInteger (String key, JComboBox jComboBox, String defaultValue) { Vector list = getList (key, defaultValue); jComboBox.removeAllItems (); for (int i = 0; i < list.size (); i++) { try { jComboBox.addItem (Integer.parseInt (list.elementAt (i))); } catch (Exception e) { } } jComboBox.setSelectedIndex (0); } static public final void saveJComboBox (String key, JComboBox jComboBox) { // XX peut être ajouter la valeur éditer dans la liste Vector result = new Vector (); int size = jComboBox.getItemCount (); for (int i = 0; i < size; i++) result.add (jComboBox.getItemAt (i)); int index = jComboBox.getSelectedIndex (); if (index < 0) result.insertElementAt ((String) jComboBox.getSelectedItem (), 0); else result.insertElementAt (result.remove (index), 0); if (index != 0) { jComboBox.removeAllItems (); for (int i = 0; i < result.size (); i++) jComboBox.addItem (result.elementAt (i)); jComboBox.setSelectedIndex (0); } setList (key, result); } static public final void saveJComboBoxInteger (String key, JComboBox jComboBox) { // XX peut être ajouter la valeur éditer dans la liste Vector result = new Vector (); int size = jComboBox.getItemCount (); for (int i = 0; i < size; i++) result.add (""+jComboBox.getItemAt (i)); int index = jComboBox.getSelectedIndex (); if (index < 0) result.insertElementAt ((String) jComboBox.getSelectedItem (), 0); else result.insertElementAt (result.remove (index), 0); setList (key, result); } // ======================================== /** Text format used to represent coordonates (i.e. [x=123,y=456] ). */ static public final MessageFormat coordonateFormat = new MessageFormat ("[x={0,number,integer},y={1,number,integer}]"); /** Set component location from a constant coordonates in configuration. @param key the name used to denoted the contant. @param component modified by the coordonates retreived in configuration. @param defaultLocation default coordonates used if non key present. */ static public final void loadLocation (String key, Component component, Point defaultLocation) { try { Object [] location = coordonateFormat.parse (getString (key+locationPostfix), new java.text.ParsePosition (0)); component.setLocation (((Number) location [0]).intValue (), ((Number) location [1]).intValue ()); } catch (Exception e) { component.setLocation (defaultLocation); } } /** Save constant coordonates to configuration form a component location. @param key the name used to denoted the contant. @param component used to set the contant coordonates value. */ static public final void saveLocation (String key, Component component) { Point location = component.getLocation (); setString (key+locationPostfix, coordonateFormat.format (new Object [] {location.x, location.y}, new StringBuffer(), null).toString ()); } // ======================================== }