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