View Javadoc

1   /*
2    * Copyright 2006 - 2012 Christina Bohk and Roland Ewald
3    *  
4    * Licensed under the Apache License, Version 2.0 (the "License"); 
5    * you may not use this file except in compliance with the License. 
6    * You may obtain a copy of the License at 
7    *  
8    *  http://www.apache.org/licenses/LICENSE-2.0
9    *  
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License. 
15   */
16  package p3j.misc;
17  
18  import james.core.data.DBConnectionData;
19  
20  import java.io.File;
21  import java.io.FileNotFoundException;
22  import java.io.FileOutputStream;
23  import java.text.DecimalFormat;
24  import java.util.ArrayList;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Map.Entry;
29  
30  import javax.xml.transform.OutputKeys;
31  import javax.xml.transform.Transformer;
32  import javax.xml.transform.TransformerException;
33  import javax.xml.transform.TransformerFactory;
34  import javax.xml.transform.dom.DOMSource;
35  import javax.xml.transform.stream.StreamResult;
36  
37  import org.hibernate.dialect.HSQLDialect;
38  import org.hibernate.dialect.MySQL5Dialect;
39  import org.hsqldb.jdbcDriver;
40  import org.w3c.dom.Document;
41  
42  import p3j.database.DatabaseType;
43  import p3j.simulation.ExecutionMode;
44  
45  import com.mysql.jdbc.Driver;
46  
47  /**
48   * Miscellaneous (auxiliary) functions and constants. For simulation-related
49   * constants, see {@link p3j.simulation.calculation.deterministic.Constants}.
50   * 
51   * Created on February 12, 2007
52   * 
53   * @author Christina Bohk
54   * @author Roland Ewald
55   */
56  public final class Misc {
57  
58    /**
59     * The delimiter used by hibernate to name classes. For example, a hibernate
60     * object representing p3j.pppm.sets.Set would be of type
61     * p3j.pppm.sets.Set$$CGLibetc.
62     */
63    private static final String DELIMITER_OF_HIBERNATE_TYPES = "$$";
64  
65    /** Base of numerical system that is used. */
66    public static final int BASE_NUM = 10;
67  
68    /** Number format to be used when editing matrices. */
69    public static final DecimalFormat NUMBER_FORMAT = new DecimalFormat(
70        "0.00000000");
71  
72    /** Minimal precision value (used to avoid rounding problems). */
73    public static final double EPSILON = 0.0000000001;
74  
75    /** The name of the configuration file. */
76    public static final String CONFIG_FILE = "conf/config.xml";
77  
78    /** The default execution mode. */
79    public static final ExecutionMode DEFAULT_EXEC_MODE = ExecutionMode.MONTE_CARLO;
80  
81    /** The default location of the hibernate configuration file. */
82    public static final String DEFAULT_HIBERNATE_CONFIG_FILE = "conf/hibernate.cfg.xml";
83  
84    /** The default location of the hibernate configuration file for testing. */
85    public static final String TEST_HIBERNATE_CONFIG_FILE = "test.hibernate.cfg.xml";
86  
87    /** The default number of trials. */
88    public static final int DEFAULT_NUM_TRIALS = 1000;
89  
90    /** The default number of parallel threads. */
91    public static final int DEFAULT_NUM_PARALLEL_THREADS = 1;
92  
93    /**
94     * The maximal number of sub-node elements to be shown in the panel. Limit
95     * this improves GUI performance when selecting large aggregates, such as the
96     * overall trial results.
97     */
98    public static final int MAX_SUBNODE_SUMMARY_ELEMENTS = 200;
99  
100   // Keys for the configuration file:
101 
102   /** The key for the number of trials. */
103   public static final String PREF_DB_TYPE = "Database Type";
104 
105   /** The key for the database URL. */
106   public static final String PREF_DB_URL = "Database URL";
107 
108   /** The key for the database user name. */
109   public static final String PREF_DB_USER = "Database User Name";
110 
111   /** The key for the database password. */
112   public static final String PREF_DB_PWD = "Database Password (currently stored unencrpyted!)";
113 
114   /** The key for the number of trials. */
115   public static final String PREF_NUM_TRIALS = "Number of Trials (multiples of Parallel Threads only)";
116 
117   /** The key for the number of parallel threads. */
118   public static final String PREF_NUM_PARALLEL_THREADS = "Parallel Threads (Monte-Carlo only)";
119 
120   /** The key for the execution mode. */
121   public static final String PREF_EXECUTION_MODE = "Execution Mode";
122 
123   // Default database configuration
124 
125   /** The URL prefix for MySQL URLs. */
126   public static final String MYSQL_URL_PREFIX = "jdbc:mysql://";
127 
128   /** The URL prefix for HSQLDB URLs. */
129   public static final String HSQLDB_URL_PREFIX = "jdbc:hsqldb:file:";
130 
131   /** The file name for HSQLDB. */
132   public static final String HSQLDB_FILE_NAME = "pppm_db";
133 
134   /** The hibernate property to read out the dialect that is used. */
135   public static final String PREF_HIBERNATE_DIALECT_PROPERTY = "hibernate.dialect";
136 
137   /** The hibernate property to read out the JDBC driver that is used. */
138   public static final String PREF_HIBERNATE_DRIVER_PROPERTY = "hibernate.connection.driver_class";
139 
140   /** The default database type. */
141   public static final DatabaseType DEFAULT_DB_TYPE = DatabaseType.HSQLDB;
142 
143   /** The supported database dialects. */
144   public static final Map<DatabaseType, String> HIBERNATE_DIALECTS = new HashMap<>();
145   static {
146     HIBERNATE_DIALECTS.put(DatabaseType.HSQLDB,
147         HSQLDialect.class.getCanonicalName());
148     HIBERNATE_DIALECTS.put(DatabaseType.MYSQL,
149         MySQL5Dialect.class.getCanonicalName());
150     HIBERNATE_DIALECTS.put(DatabaseType.GENERIC,
151         MySQL5Dialect.class.getCanonicalName());
152   }
153 
154   public static final Map<DatabaseType, String> JDBC_DRIVERS = new HashMap<>();
155   static {
156     JDBC_DRIVERS.put(DatabaseType.HSQLDB, jdbcDriver.class.getCanonicalName());
157     JDBC_DRIVERS.put(DatabaseType.MYSQL, Driver.class.getCanonicalName());
158     JDBC_DRIVERS.put(DatabaseType.GENERIC, Driver.class.getCanonicalName());
159   }
160 
161   /** The default database URLs. */
162   public static final Map<DatabaseType, String> DEFAULT_DB_URLS = new HashMap<>();
163   static {
164     DEFAULT_DB_URLS.put(DatabaseType.HSQLDB, HSQLDB_URL_PREFIX + "."
165         + File.separator + HSQLDB_FILE_NAME);
166     DEFAULT_DB_URLS.put(DatabaseType.MYSQL, MYSQL_URL_PREFIX
167         + "localhost/pppm_db");
168     DEFAULT_DB_URLS.put(DatabaseType.GENERIC, MYSQL_URL_PREFIX
169         + "localhost/pppm_db");
170   }
171 
172   /** The default database user names. */
173   public static final Map<DatabaseType, String> DEFAULT_DB_USERS = new HashMap<>();
174   static {
175     DEFAULT_DB_USERS.put(DatabaseType.HSQLDB, "sa");
176     DEFAULT_DB_USERS.put(DatabaseType.MYSQL, "root");
177     DEFAULT_DB_USERS.put(DatabaseType.GENERIC, "root");
178   }
179 
180   /** The default database passwords. */
181   public static final Map<DatabaseType, String> DEFAULT_DB_PWDS = new HashMap<>();
182   static {
183     DEFAULT_DB_PWDS.put(DatabaseType.HSQLDB, "");
184     DEFAULT_DB_PWDS.put(DatabaseType.MYSQL, "root");
185     DEFAULT_DB_PWDS.put(DatabaseType.GENERIC, "");
186   }
187 
188   /** Default setup for database connection. */
189   public static final DBConnectionData DEFAULT_DB_CONN = DEFAULT_DB_TYPE
190       .getDefaults().getFirstValue();
191 
192   // Some common GUI Labels
193 
194   /** The Constant GUI_LABEL_PROBABILITY. */
195   public static final String GUI_LABEL_PROBABILITY = "Probability (in [0,1]):";
196 
197   /** The Constant GUI_LABEL_NAME. */
198   public static final String GUI_LABEL_NAME = "Name:";
199 
200   /** The Constant GUI_LABEL_DESCRIPTION. */
201   public static final String GUI_LABEL_DESCRIPTION = "Description:";
202 
203   /** The Constant GUI_LABEL_DEVIATION. */
204   public static final String GUI_LABEL_DEVIATION = "Standard deviation";
205 
206   /** The label for the jump-off year in the GUI. */
207   public static final String GUI_LABEL_JUMP_OFF_YEAR = "Jump-Off Year";
208 
209   /** The number of age classes. */
210   public static final String GUI_LABEL_NUM_AGE_CLASSES = "Number of Age Classes:";
211 
212   /** The number of descendant generations. */
213   public static final String GUI_LABEL_DESCENDANT_GENERATIONS = "Descendant Generations:";
214 
215   /** The projection horizon. */
216   public static final String GUI_LABEL_PROJECTION_HORIZON = "Projection Horizon:";
217 
218   /** The label for the JDBC driver. */
219   public static final String GUI_LABEL_DB_DRIVER_CLASS = "JDBC Driver (needs to be in classpath)";
220 
221   /** The label for the Hibernate dialect. */
222   public static final String GUI_LABEL_HIBERNATE_DIALECT = "Hibernate dialect class";
223 
224   /** The label for entering the location of the database. */
225   public static final String GUI_LABEL_DB_FILE_LOCATION = "Location of database";
226 
227   /**
228    * This class should not be instantiated.
229    */
230   private Misc() {
231   }
232 
233   /**
234    * Retrieves file ending.
235    * 
236    * @param file
237    *          the file name
238    * 
239    * @return file ending
240    */
241   public static String getFileEnding(File file) {
242     return file.getName().substring(file.getName().lastIndexOf('.') + 1);
243   }
244 
245   /**
246    * Parses string to double. Replaces ',' by '.'.
247    * 
248    * @param value
249    *          string to be parsed
250    * 
251    * @return double value
252    */
253   public static double parseToDouble(Object value) {
254     return Double.parseDouble(value.toString().replace(',', '.'));
255   }
256 
257   /**
258    * Converts a string into a probability (a double that has to be in [0,1]).
259    * 
260    * @param probString
261    *          string containing a probability
262    * 
263    * @return double value if valid, otherwise 0
264    */
265   public static double parseToDoubleProb(String probString) {
266     double probability = parseToDouble(probString);
267     if (probability < 0 || probability > 1) {
268       return 0;
269     }
270     return probability;
271   }
272 
273   /**
274    * Two values are recognised as equal as long as their absolute differece does
275    * not exceed EPSILON.
276    * 
277    * @param x
278    *          the first value
279    * @param y
280    *          the second value
281    * 
282    * @return true if |x-y| < e
283    */
284   public static boolean numEqual(double x, double y) {
285     return Math.abs(x - y) < EPSILON;
286   }
287 
288   /**
289    * Parses string to integer.
290    * 
291    * @param numString
292    *          the string containing the number
293    * 
294    * @return integer value of string
295    */
296   public static int parseToInt(String numString) {
297     return Integer.parseInt(numString);
298   }
299 
300   /**
301    * Rounds a value to a certain number of digits after the comma.
302    * 
303    * @param value
304    *          the value
305    * @param digits
306    *          the desired maximum number of digits after the comma
307    * 
308    * @return value value rounded to a given number of digits after the comma
309    */
310   public static double round(double value, int digits) {
311     return ((int) (value * Math.pow(BASE_NUM, digits)) / Math.pow(BASE_NUM,
312         digits));
313   }
314 
315   /**
316    * Writes a document to a file.
317    * 
318    * @param fileName
319    *          the name of the file
320    * @param document
321    *          the document to be written
322    * 
323    * @throws FileNotFoundException
324    *           if destination file could not be found/created
325    * @throws TransformerException
326    *           if transformation of document to XML failed
327    */
328   @Deprecated
329   public static void writeDocumentToFile(String fileName, Document document)
330       throws FileNotFoundException, TransformerException {
331 
332     File f = new File(fileName);
333 
334     FileOutputStream fop = new FileOutputStream(f);
335 
336     // Writing document...
337     TransformerFactory tff = TransformerFactory.newInstance();
338     Transformer tf = tff.newTransformer();
339 
340     tf.setOutputProperty(OutputKeys.METHOD, "xml");
341     tf.setOutputProperty(OutputKeys.INDENT, "yes");
342 
343     DOMSource source = new DOMSource(document);
344     StreamResult result = new StreamResult(fop);
345 
346     tf.transform(source, result);
347   }
348 
349   /**
350    * Auto-casts any kind of object to the desired case. This is a work-around
351    * for Java's many insufficiencies regarding generics etc. Handle with care,
352    * only use this when you know what you are doing!
353    * 
354    * @param o
355    *          the object
356    * 
357    * @param <X>
358    *          the desired type
359    * 
360    * @return the object, now casted to the desired type
361    */
362   @SuppressWarnings("unchecked")
363   public static <X> X autoCast(Object o) {
364     return (X) o;
365   }
366 
367   /**
368    * Checks whether two classes are the same. This is tricky here: Hibernate
369    * enhances/replaces existing types, so classes from DB and from JVM are not
370    * the same! Classes for the DB have '_$$_...' etc. with them.
371    * 
372    * @param class1
373    *          one class
374    * @param class2
375    *          the other class
376    * 
377    * @return true if classes are the same, otherwise false
378    */
379   public static boolean checkClassEquality(Class<?> class1, Class<?> class2) {
380     return getCleanedClassName(class1).compareTo(getCleanedClassName(class2)) == 0;
381   }
382 
383   /**
384    * Cleans up class name by removing everything after '_$$_', which is used by
385    * Hibernate to identify generated custom classes
386    * ('my.Class_$$_javassist...').
387    * 
388    * @param theClass
389    *          the class for which the name should be cleaned up
390    * 
391    * @return the cleaned-up class name
392    */
393   public static String getCleanedClassName(Class<?> theClass) {
394     String name = theClass.getCanonicalName();
395     int cutOffIndex = name.indexOf(DELIMITER_OF_HIBERNATE_TYPES);
396     return cutOffIndex < 0 ? name : name.substring(0, cutOffIndex);
397   }
398 
399   /**
400    * Merges list of lists to a newly created {@link ArrayList}.
401    * 
402    * @param listsToMerge
403    *          the lists to merge
404    * @param <D>
405    *          the type of the list elements
406    * @return the new list, containing all elements of the others
407    */
408   public static <D> List<D> mergeList(List<D>... listsToMerge) {
409     List<D> result = new ArrayList<D>();
410     for (List<D> listToMerge : listsToMerge) {
411       result.addAll(listToMerge);
412     }
413     return result;
414   }
415 
416   /**
417    * Inverts a map.
418    * 
419    * @param src
420    *          the content
421    * @param dest
422    *          the destination map to be filled (will be cleared at first)
423    * 
424    * @param <X>
425    *          the type of the original keys
426    * @param <Y>
427    *          the type of the original values
428    */
429   public static <X, Y> void invertMap(Map<X, Y> src, Map<Y, X> dest) {
430     dest.clear();
431     for (Entry<X, Y> entry : src.entrySet()) {
432       if (dest.containsKey(entry.getValue())) {
433         throw new IllegalArgumentException("Passed map contains value '"
434             + entry + "' twice, cannot be inverted.");
435       }
436       dest.put(entry.getValue(), entry.getKey());
437     }
438   }
439 
440 }