001    package net.sf.cpsolver.ifs.util;
002    
003    import java.io.ByteArrayOutputStream;
004    import java.io.File;
005    import java.io.FileInputStream;
006    import java.io.IOException;
007    import java.io.OutputStream;
008    import java.io.PrintStream;
009    import java.util.ArrayList;
010    import java.util.Collection;
011    import java.util.Date;
012    import java.util.Iterator;
013    import java.util.List;
014    import java.util.Map;
015    import java.util.Properties;
016    import java.util.Random;
017    import java.util.StringTokenizer;
018    import java.util.TreeSet;
019    
020    import org.apache.log4j.Level;
021    import org.apache.log4j.Logger;
022    import org.apache.log4j.PropertyConfigurator;
023    
024    /**
025     * Several auxiliary static methods.
026     * 
027     * @version IFS 1.2 (Iterative Forward Search)<br>
028     *          Copyright (C) 2006 - 2010 Tomas Muller<br>
029     *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
030     *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
031     * <br>
032     *          This library is free software; you can redistribute it and/or modify
033     *          it under the terms of the GNU Lesser General Public License as
034     *          published by the Free Software Foundation; either version 3 of the
035     *          License, or (at your option) any later version. <br>
036     * <br>
037     *          This library is distributed in the hope that it will be useful, but
038     *          WITHOUT ANY WARRANTY; without even the implied warranty of
039     *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
040     *          Lesser General Public License for more details. <br>
041     * <br>
042     *          You should have received a copy of the GNU Lesser General Public
043     *          License along with this library; if not see
044     *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
045     */
046    public class ToolBox {
047        private static long sSeed = System.currentTimeMillis();
048        private static Random sRandom = new Random(sSeed);
049    
050        /** Returns random number (int) from the set 0 .. limit - 1 */
051        public static int random(int limit) {
052            return (int) (random() * limit);
053        }
054    
055        /** Returns random element from the given set of elements */
056        public static <E> E random(Collection<E> set) {
057            switch (set == null ? 0 : set.size()) {
058                case 0:
059                    return null;
060                case 1:
061                    return set.iterator().next();
062                case 2:
063                    Iterator<E> i = set.iterator();
064                    if (sRandom.nextBoolean()) i.next();
065                    return i.next();
066                default:
067                    List<E> v = (set instanceof List<?> ? (List<E>) set : new ArrayList<E>(set));
068                    return v.get(random(v.size()));
069            }
070        }
071    
072        /**
073         * Returns a randomly generated subset of the given set
074         * 
075         * @param set
076         *            set
077         * @param part
078         *            probability of selection of an element into the resultant
079         *            subset
080         */
081        public static <E> Collection<E> subSet(Collection<E> set, double part) {
082            return subSet(set, part, 1);
083        }
084    
085        /** Swaps two elements in the list */
086        private static <E> void swap(List<E> list, int first, int second) {
087            E o = list.get(first);
088            list.set(first, list.get(second));
089            list.set(second, o);
090        }
091    
092        /**
093         * Returns a randomly generated subset of the given set
094         * 
095         * @param set
096         *            set
097         * @param part
098         *            probability of selection of an element into the resultant
099         *            subset
100         * @param minSize
101         *            minimal size of the returned subset
102         */
103        public static <E> Collection<E> subSet(Collection<E> set, double part, int minSize) {
104            if (set.size() <= minSize || part >= 1.0)
105                return set;
106            ArrayList<E> subSet = new ArrayList<E>(set);
107            int size = set.size();
108            int numberToSelect = Math.max(minSize, (int) (part * set.size()));
109            for (int idx = 0; idx < numberToSelect; idx++) {
110                swap(subSet, idx, idx + (int) (random() * (size - idx)));
111            }
112            return subSet.subList(0, numberToSelect);
113        }
114    
115        /** Trim a string to have given length */
116        public static String trim(String s, int length) {
117            if (s.length() > length)
118                return s.substring(0, length);
119            StringBuffer sb = new StringBuffer(s);
120            while (sb.length() < length)
121                sb.append(" ");
122            return sb.toString();
123        }
124    
125        /** Multiline representation of a colection */
126        public static String col2string(Collection<?> col, int tab) {
127            StringBuffer tabsb = new StringBuffer();
128            while (tabsb.length() < 2 * tab)
129                tabsb.append("  ");
130            StringBuffer sb = new StringBuffer("[\n");
131            for (Iterator<?> i = col.iterator(); i.hasNext();) {
132                sb.append(tabsb + "  " + i.next() + (i.hasNext() ? "," : "") + "\n");
133            }
134            sb.append(tabsb + "]");
135            return sb.toString();
136        }
137    
138        /** Multiline representation of a dictionary */
139        public static <K, V> String dict2string(Map<K, V> dict, int tab) {
140            StringBuffer tabsb = new StringBuffer();
141            while (tabsb.length() < 2 * tab)
142                tabsb.append("  ");
143            StringBuffer sb = new StringBuffer("[\n");
144            TreeSet<K> keys = new TreeSet<K>(dict.keySet());
145            for (K key : keys) {
146                V value = dict.get(key);
147                sb.append(tabsb + "  " + key + ": " + value + "\n");
148            }
149            sb.append(tabsb + "]");
150            return sb.toString();
151        }
152    
153        /**
154         * Root mean square
155         * 
156         * @param n
157         *            number of tests
158         * @param x
159         *            total value of all tests
160         * @param x2
161         *            total value^2 of all tests
162         */
163        public static double rms(int n, double x, double x2) {
164            double var = x2 / n;
165            double mean = x / n;
166            return Math.sqrt(Math.abs(var - mean * mean));
167        }
168    
169        /** Merge source with target */
170        public static <E> void merge(List<E> target, Collection<E> source) {
171            for (E o : source) {
172                if (!target.contains(o))
173                    target.add(o);
174            }
175        }
176    
177        /** Returns intersection of two collections */
178        public static <E> List<E> intersect(Collection<E> source1, Collection<E> source2) {
179            List<E> target = new ArrayList<E>();
180            for (E o : source1) {
181                if (!source2.contains(o))
182                    target.add(o);
183            }
184            return target;
185        }
186    
187        /**
188         * Sets seeds for {@link ToolBox#getRandom()} and {@link ToolBox#random()}
189         * methods.
190         */
191        public static void setSeed(long seed) {
192            sSeed = seed;
193            sRandom = new Random(sSeed);
194        }
195    
196        /** Gets current seed */
197        public static long getSeed() {
198            return sSeed;
199        }
200    
201        /** Gets random number generator */
202        public static Random getRandom() {
203            return sRandom;
204        }
205    
206        /** Generates random double number */
207        public static double random() {
208            return sRandom.nextDouble();
209        }
210    
211        /** Configurates log4j loging */
212        public static void configureLogging() {
213            Properties props = new Properties();
214            props.setProperty("log4j.rootLogger", "DEBUG, A1");
215            props.setProperty("log4j.appender.A1", "org.apache.log4j.ConsoleAppender");
216            props.setProperty("log4j.appender.A1.layout", "org.apache.log4j.PatternLayout");
217            props.setProperty("log4j.appender.A1.layout.ConversionPattern", "%-5p %c{2}: %m%n");
218            props.setProperty("log4j.logger.net", "INFO");
219            props.setProperty("log4j.logger.net.sf.cpsolver", "DEBUG");
220            props.setProperty("log4j.logger.org", "INFO");
221            PropertyConfigurator.configure(props);
222        }
223    
224        /**
225         * Configurates log4j loging
226         * 
227         * @param logDir
228         *            output folder
229         * @param properties
230         *            some other log4j properties
231         */
232        public static String configureLogging(String logDir, Properties properties) {
233            return configureLogging(logDir, properties, false);
234        }
235    
236        public static String configureLogging(String logDir, Properties properties, boolean timeInFileName) {
237            return configureLogging(logDir, properties, timeInFileName, true);
238        }
239    
240        /**
241         * Configurates log4j loging
242         * 
243         * @param logDir
244         *            output folder
245         * @param properties
246         *            some other log4j properties
247         * @param timeInFileName
248         *            if true log file is named debug_yyyy-MM-dd_(HH.mm.ss).log, it
249         *            is named debug.log otherwise
250         */
251        public static String configureLogging(String logDir, Properties properties, boolean timeInFileName,
252                boolean includeSystemOuts) {
253            String time = new java.text.SimpleDateFormat("yyyy-MM-dd_(HH.mm.ss)", java.util.Locale.US).format(new Date());
254            (new File(logDir)).mkdirs();
255            String fileName = logDir + File.separator + (timeInFileName ? "debug_" + time : "debug") + ".log";
256            Properties props = (properties != null ? properties : new Properties());
257            if (!props.containsKey("log4j.rootLogger")) {
258                props.setProperty("log4j.rootLogger", "debug, LogFile");
259                if (timeInFileName)
260                    props.setProperty("log4j.appender.LogFile", "org.apache.log4j.FileAppender");
261                else {
262                    props.setProperty("log4j.appender.LogFile", "org.apache.log4j.DailyRollingFileAppender");
263                    props.setProperty("log4j.appender.LogFile.DatePattern", "'.'yyyy-MM-dd");
264                }
265                props.setProperty("log4j.appender.LogFile.File", fileName);
266                props.setProperty("log4j.appender.LogFile.layout", "org.apache.log4j.PatternLayout");
267                props.setProperty("log4j.appender.LogFile.layout.ConversionPattern",
268                        "%d{dd-MMM-yy HH:mm:ss.SSS} [%t] %-5p %c{2}> %m%n");
269            }
270            PropertyConfigurator.configure(props);
271            Logger log = Logger.getRootLogger();
272            log.info("-----------------------------------------------------------------------");
273            log.info("IFS debug file");
274            log.info("");
275            log.info("Created: " + new Date());
276            log.info("");
277            log.info("System info:");
278            log.info("System:      " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " "
279                    + System.getProperty("os.arch"));
280            log.info("CPU:         " + System.getProperty("sun.cpu.isalist") + " endian:"
281                    + System.getProperty("sun.cpu.endian") + " encoding:" + System.getProperty("sun.io.unicode.encoding"));
282            log.info("Java:        " + System.getProperty("java.vendor") + ", " + System.getProperty("java.runtime.name")
283                    + " " + System.getProperty("java.runtime.version", System.getProperty("java.version")));
284            log.info("User:        " + System.getProperty("user.name"));
285            log.info("Timezone:    " + System.getProperty("user.timezone"));
286            log.info("Working dir: " + System.getProperty("user.dir"));
287            log.info("Classpath:   " + System.getProperty("java.class.path"));
288            log.info("");
289            if (includeSystemOuts) {
290                System.setErr(new PrintStream(new LogOutputStream(System.err, Logger.getLogger("STDERR"), Level.ERROR)));
291                System.setOut(new PrintStream(new LogOutputStream(System.out, Logger.getLogger("STDOUT"), Level.DEBUG)));
292            }
293            return fileName;
294        }
295    
296        /**
297         * Loads data properties. If there is INCLUDE property available, it is
298         * interpreted as semi-colon separated list of porperty files which should
299         * be also loaded (works recursively).
300         * 
301         */
302        public static DataProperties loadProperties(File propertyFile) {
303            FileInputStream is = null;
304            try {
305                DataProperties ret = new DataProperties();
306                is = new FileInputStream(propertyFile);
307                ret.load(is);
308                is.close();
309                is = null;
310                if (ret.getProperty("INCLUDE") != null) {
311    
312                    StringTokenizer stk = new StringTokenizer(ret.getProperty("INCLUDE"), ";");
313                    while (stk.hasMoreTokens()) {
314                        String aFile = stk.nextToken();
315                        System.out.println("  Loading included file '" + aFile + "' ... ");
316                        if ((new File(aFile)).exists())
317                            is = new FileInputStream(aFile);
318                        if ((new File(propertyFile.getParent() + File.separator + aFile)).exists())
319                            is = new FileInputStream(propertyFile.getParent() + File.separator + aFile);
320                        if (is == null)
321                            System.err.println("Unable to find include file '" + aFile + "'.");
322                        ret.load(is);
323                        is.close();
324                        is = null;
325                    }
326                    ret.remove("INCLUDE");
327                }
328                return ret;
329            } catch (Exception e) {
330                System.err.println("Unable to load property file " + propertyFile);
331                e.printStackTrace();
332                return new DataProperties();
333            } finally {
334                try {
335                    if (is != null)
336                        is.close();
337                } catch (IOException e) {
338                }
339            }
340        }
341    
342        public static boolean equals(Object o1, Object o2) {
343            return (o1 == null ? o2 == null : o1.equals(o2));
344        }
345    
346        private static class LogOutputStream extends OutputStream {
347            private Logger iLogger = null;
348            private Level iLevel = null;
349            private OutputStream iOldOutputStream;
350            private ByteArrayOutputStream iOut = new ByteArrayOutputStream();
351    
352            public LogOutputStream(OutputStream oldOutputStream, Logger logger, Level level) {
353                iLogger = logger;
354                iLevel = level;
355                iOldOutputStream = oldOutputStream;
356            }
357    
358            @Override
359            public void write(int b) throws IOException {
360                iOldOutputStream.write(b);
361                if (b == '\r')
362                    return;
363                if (b == '\n') {
364                    iOut.flush();
365                    iLogger.log(iLevel, new String(iOut.toByteArray()));
366                    iOut.reset();
367                } else
368                    iOut.write(b);
369            }
370        }
371        
372        public static <E> List<E> toList(E... obj) {
373            List<E> ret = new ArrayList<E>(obj == null ? 0 : obj.length);
374            if (obj != null)
375                for (E e: obj)
376                    ret.add(e);
377            return ret;
378        }
379    }