001package net.sf.cpsolver.ifs.util; 002 003import java.io.ByteArrayOutputStream; 004import java.io.File; 005import java.io.FileInputStream; 006import java.io.IOException; 007import java.io.OutputStream; 008import java.io.PrintStream; 009import java.util.ArrayList; 010import java.util.Collection; 011import java.util.Date; 012import java.util.Iterator; 013import java.util.List; 014import java.util.Map; 015import java.util.Properties; 016import java.util.Random; 017import java.util.StringTokenizer; 018import java.util.TreeSet; 019 020import org.apache.log4j.Level; 021import org.apache.log4j.Logger; 022import 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 Tomáš Müller<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 */ 046public 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}