001 package com.croftsoft.apps.skipper; 002 003 import java.awt.*; 004 import java.io.*; 005 import java.util.*; 006 import java.util.concurrent.*; 007 import java.util.logging.*; 008 009 import javax.imageio.*; 010 011 import com.croftsoft.core.lang.*; 012 import com.croftsoft.core.lang.lifecycle.*; 013 014 /*********************************************************************** 015 * Skipper Model. 016 * 017 * Maintains program state. 018 * 019 * Copyright 2007 David Wallace Croft. 020 * 021 * @version 022 * $Date: 2007/07/28 16:57:03 $ $Author: croft $ 023 * @since 024 * 2006-12-19 025 * @author 026 * <a href="https://www.croftsoft.com/">David Wallace Croft</a> 027 ***********************************************************************/ 028 029 public final class SkipperModel 030 implements Commissionable 031 //////////////////////////////////////////////////////////////////////// 032 //////////////////////////////////////////////////////////////////////// 033 { 034 035 private static final String CLASS_NAME 036 = SkipperModel.class.getName ( ); 037 038 private static final Double DEFAULT_WEIGHT = new Double ( 1 ); 039 040 private static final double REDUCTION_FACTOR = 0.5; 041 042 // 043 044 private final SkipperConfig skipperConfig; 045 046 private final Logger logger; 047 048 // 049 050 private String [ ] selections; 051 052 private double [ ] probabilities; 053 054 private double [ ] thresholds; 055 056 //////////////////////////////////////////////////////////////////////// 057 //////////////////////////////////////////////////////////////////////// 058 059 public SkipperModel ( final SkipperConfig skipperConfig ) 060 //////////////////////////////////////////////////////////////////////// 061 { 062 NullArgumentException.checkArgs ( 063 this.skipperConfig = skipperConfig ); 064 065 logger = Logger.getLogger ( CLASS_NAME ); 066 067 selections = new String [ 0 ]; 068 069 probabilities = new double [ 0 ]; 070 071 thresholds = new double [ 0 ]; 072 } 073 074 //////////////////////////////////////////////////////////////////////// 075 // accessor methods 076 //////////////////////////////////////////////////////////////////////// 077 078 public String [ ] getSelections ( ) 079 //////////////////////////////////////////////////////////////////////// 080 { 081 return selections; 082 } 083 084 public double [ ] getCumulativeProbabilityThresholds ( ) 085 //////////////////////////////////////////////////////////////////////// 086 { 087 return thresholds; 088 } 089 090 //////////////////////////////////////////////////////////////////////// 091 // lifecycle methods 092 //////////////////////////////////////////////////////////////////////// 093 094 public void init ( ) 095 //////////////////////////////////////////////////////////////////////// 096 { 097 Executors.newSingleThreadExecutor ( ).execute ( 098 new Runnable ( ) 099 { 100 public void run ( ) 101 { 102 final Map<String, Double> selectionToWeightMap 103 = loadSelections ( ); 104 105 loadWeights ( selectionToWeightMap ); 106 107 EventQueue.invokeLater ( 108 new Runnable ( ) 109 { 110 public void run ( ) 111 { 112 loadProbabilities ( selectionToWeightMap ); 113 114 divideEachProbabilityBySum ( ); 115 } 116 } ); 117 } 118 } ); 119 } 120 121 public void destroy ( ) 122 //////////////////////////////////////////////////////////////////////// 123 { 124 try 125 { 126 saveWeights ( ); 127 } 128 catch ( final Exception ex ) 129 { 130 logger.throwing ( CLASS_NAME, "destroy", ex ); 131 } 132 } 133 134 //////////////////////////////////////////////////////////////////////// 135 //////////////////////////////////////////////////////////////////////// 136 137 public void decreaseProbabilityOfSkipped ( final int imageIndex ) 138 //////////////////////////////////////////////////////////////////////// 139 { 140 if ( ( imageIndex >= 0 ) 141 && ( imageIndex < probabilities.length ) ) 142 { 143 probabilities [ imageIndex ] 144 = REDUCTION_FACTOR * probabilities [ imageIndex ]; 145 } 146 } 147 148 public void divideEachProbabilityBySum ( ) 149 //////////////////////////////////////////////////////////////////////// 150 { 151 if ( probabilities.length < 1 ) 152 { 153 return; 154 } 155 156 double sumOfProbabilities = 0; 157 158 for ( int i = 0; i < probabilities.length; i++ ) 159 { 160 sumOfProbabilities += probabilities [ i ]; 161 } 162 163 thresholds [ 0 ] = 0; 164 165 for ( int i = 0; i < probabilities.length; i++ ) 166 { 167 probabilities [ i ] = probabilities [ i ] / sumOfProbabilities; 168 169 if ( i < probabilities.length - 1 ) 170 { 171 thresholds [ i + 1 ] = thresholds [ i ] + probabilities [ i ]; 172 } 173 } 174 175 final StringBuffer stringBuffer 176 = new StringBuffer ( "cumulative probability thresholds: " ); 177 178 for ( int i = 0; i < thresholds.length; i++ ) 179 { 180 stringBuffer.append ( thresholds [ i ] + " " ); 181 } 182 183 logger.info ( stringBuffer.toString ( ) ); 184 } 185 186 //////////////////////////////////////////////////////////////////////// 187 // private methods 188 //////////////////////////////////////////////////////////////////////// 189 190 private void loadProbabilities ( 191 final Map<String, Double> selectionToWeightMap ) 192 //////////////////////////////////////////////////////////////////////// 193 { 194 selections 195 = selectionToWeightMap.keySet ( ).toArray ( new String [ 0 ] ); 196 197 final int selectionCount = selections.length; 198 199 logger.info ( "selectionCount = " + selectionCount ); 200 201 probabilities = new double [ selectionCount ]; 202 203 thresholds = new double [ selectionCount ]; 204 205 for ( int i = 0; i < selectionCount; i++ ) 206 { 207 final Double weight 208 = selectionToWeightMap.get ( selections [ i ] ); 209 210 probabilities [ i ] = weight.doubleValue ( ); 211 } 212 } 213 214 private Map<String, Double> loadSelections ( ) 215 //////////////////////////////////////////////////////////////////////// 216 { 217 try 218 { 219 logger.entering ( CLASS_NAME, "load" ); 220 221 final String [ ] suffixes = ImageIO.getReaderFileSuffixes ( ); 222 223 final String userHome = System.getProperty ( "user.home" ); 224 225 if ( userHome == null ) 226 { 227 return null; 228 } 229 230 final File homeDirectory = new File ( userHome ); 231 232 if ( !homeDirectory.exists ( ) 233 || !homeDirectory.isDirectory ( ) ) 234 { 235 return null; 236 } 237 238 File picturesDirectory = null; 239 240 String picturesPath = skipperConfig.getPicturesPath ( ); 241 242 if ( picturesPath == null ) 243 { 244 picturesDirectory = new File ( 245 homeDirectory, 246 skipperConfig.getDefaultPicturesPath ( ) ); 247 248 if ( picturesDirectory.exists ( ) 249 && picturesDirectory.isDirectory ( ) ) 250 { 251 picturesPath = picturesDirectory.getCanonicalPath ( ); 252 253 skipperConfig.setPicturesPath ( picturesPath ); 254 } 255 } 256 else 257 { 258 picturesDirectory = new File ( picturesPath ); 259 } 260 261 if ( ( picturesDirectory == null ) 262 || !picturesDirectory.exists ( ) 263 || !picturesDirectory.isDirectory ( ) ) 264 { 265 picturesDirectory = homeDirectory; 266 } 267 268 logger.info ( "picturesDirectory: " + picturesDirectory ); 269 270 final Map<String, Double> selectionToWeightMap 271 = new HashMap<String, Double> ( ); 272 273 final Stack<File> directoryStack = new Stack<File> ( ); 274 275 directoryStack.add ( picturesDirectory ); 276 277 File directory = null; 278 279 while ( !directoryStack.isEmpty ( ) ) 280 { 281 directory = directoryStack.pop ( ); 282 283 final String [ ] list = directory.list ( ); 284 285 for ( String filename : list ) 286 { 287 final File file = new File ( directory, filename ); 288 289 if ( file.isDirectory ( ) ) 290 { 291 directoryStack.add ( file ); 292 293 continue; 294 } 295 296 filename = filename.toLowerCase ( ); 297 298 for ( final String suffix : suffixes ) 299 { 300 if ( filename.endsWith ( "." + suffix ) ) 301 { 302 filename = file.getCanonicalPath ( ); 303 304 selectionToWeightMap.put ( filename, DEFAULT_WEIGHT ); 305 306 break; 307 } 308 } 309 } 310 } 311 312 return selectionToWeightMap; 313 } 314 catch ( final Exception ex ) 315 { 316 logger.throwing ( CLASS_NAME, "load", ex ); 317 } 318 finally 319 { 320 logger.exiting ( CLASS_NAME, "load" ); 321 } 322 323 return null; 324 } 325 326 private static void loadWeights ( 327 final Map<String, Double> selectionToWeightMap ) 328 //////////////////////////////////////////////////////////////////////// 329 { 330 try 331 { 332 final File weightsFile = SkipperConfig.createWeightsFile ( ); 333 334 if ( ( weightsFile == null ) 335 || !weightsFile.exists ( ) ) 336 { 337 return; 338 } 339 340 final BufferedReader bufferedReader 341 = new BufferedReader ( new FileReader ( weightsFile ) ); 342 343 try 344 { 345 String line = null; 346 347 while ( ( line = bufferedReader.readLine ( ) ) != null ) 348 { 349 final int index = line.trim ( ).indexOf ( ' ' ); 350 351 if ( index < 0 ) 352 { 353 continue; 354 } 355 356 final String weightString = line.substring ( 0, index ); 357 358 try 359 { 360 final double weight = Double.parseDouble ( weightString ); 361 362 final String imageFilename = line.substring ( index + 1 ); 363 364 if ( selectionToWeightMap.containsKey ( imageFilename ) ) 365 { 366 selectionToWeightMap.put ( 367 imageFilename, 368 new Double ( weight ) ); 369 } 370 } 371 catch ( NumberFormatException ex ) 372 { 373 Logger.getLogger ( CLASS_NAME ).warning ( 374 "Weight does not parse as number: " + weightString ); 375 } 376 } 377 } 378 finally 379 { 380 bufferedReader.close ( ); 381 } 382 } 383 catch ( final Exception ex ) 384 { 385 Logger.getLogger ( CLASS_NAME ).throwing ( 386 CLASS_NAME, "loadWeights", ex ); 387 } 388 } 389 390 private void saveWeights ( ) 391 throws IOException 392 //////////////////////////////////////////////////////////////////////// 393 { 394 if ( ( selections == null ) 395 || ( selections.length < 1 ) ) 396 { 397 return; 398 } 399 400 final File weightsFile = SkipperConfig.createWeightsFile ( ); 401 402 if ( weightsFile == null ) 403 { 404 return; 405 } 406 407 final BufferedWriter bufferedWriter 408 = new BufferedWriter ( new FileWriter ( weightsFile ) ); 409 410 try 411 { 412 final int selectionCount = selections.length; 413 414 for ( int i = 0; i < selectionCount; i++ ) 415 { 416 final double weight = probabilities [ i ] * selectionCount; 417 418 bufferedWriter.write ( weight + " " + selections [ i ] ); 419 420 bufferedWriter.newLine ( ); 421 } 422 } 423 finally 424 { 425 bufferedWriter.close ( ); 426 } 427 } 428 429 //////////////////////////////////////////////////////////////////////// 430 //////////////////////////////////////////////////////////////////////// 431 }