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 }