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        }