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