Home | History | Annotate | Download | only in report
      1 /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
      2  *
      3  * This program and the accompanying materials are made available under
      4  * the terms of the Common Public License v1.0 which accompanies this distribution,
      5  * and is available at http://www.eclipse.org/legal/cpl-v10.html
      6  *
      7  * $Id: SourcePathCache.java,v 1.1.1.1 2004/05/09 16:57:39 vlad_r Exp $
      8  */
      9 package com.vladium.emma.report;
     10 
     11 import java.io.File;
     12 import java.io.FileFilter;
     13 import java.util.ArrayList;
     14 import java.util.Collections;
     15 import java.util.HashMap;
     16 import java.util.HashSet;
     17 import java.util.List;
     18 import java.util.Map;
     19 import java.util.Set;
     20 
     21 import com.vladium.util.asserts.$assert;
     22 
     23 // ----------------------------------------------------------------------------
     24 /**
     25  * @author Vlad Roubtsov, (C) 2003
     26  */
     27 public
     28 final class SourcePathCache
     29 {
     30     // public: ................................................................
     31 
     32     // TODO: use soft cache for m_packageCache?
     33 
     34     /**
     35      * @param sourcepath [can be empty]
     36      */
     37     public SourcePathCache (final String [] sourcepath, final boolean removeNonExistent)
     38     {
     39         if (sourcepath == null) throw new IllegalArgumentException ("null input: sourcepath");
     40 
     41         final List _sourcepath = new ArrayList (sourcepath.length);
     42         for (int i = 0; i < sourcepath.length; ++ i)
     43         {
     44             final File dir = new File (sourcepath [i]);
     45 
     46             if (! removeNonExistent || (dir.isDirectory () && dir.exists ()))
     47                 _sourcepath.add (dir);
     48         }
     49 
     50         m_sourcepath = new File [_sourcepath.size ()];
     51         _sourcepath.toArray (m_sourcepath);
     52 
     53         m_packageCache = new HashMap ();
     54     }
     55 
     56     /**
     57      * @param sourcepath [can be empty]
     58      */
     59     public SourcePathCache (final File [] sourcepath, final boolean removeNonExistent)
     60     {
     61         if (sourcepath == null) throw new IllegalArgumentException ("null input: sourcepath");
     62 
     63         final List _sourcepath = new ArrayList (sourcepath.length);
     64         for (int i = 0; i < sourcepath.length; ++ i)
     65         {
     66             final File dir = sourcepath [i];
     67 
     68             if (! removeNonExistent || (dir.isDirectory () && dir.exists ()))
     69                 _sourcepath.add (dir);
     70         }
     71 
     72         m_sourcepath = new File [_sourcepath.size ()];
     73         _sourcepath.toArray (m_sourcepath);
     74 
     75         m_packageCache = new HashMap ();
     76     }
     77 
     78     /**
     79      * @return absolute pathname [null if 'name' was not found in cache]
     80      */
     81     public synchronized File find (final String packageVMName, final String name)
     82     {
     83         if (packageVMName == null) throw new IllegalArgumentException ("null input: packageVMName");
     84         if (name == null) throw new IllegalArgumentException ("null input: name");
     85 
     86         if (m_sourcepath.length == 0) return null;
     87 
     88         CacheEntry entry = (CacheEntry) m_packageCache.get (packageVMName);
     89 
     90         if (entry == null)
     91         {
     92             entry = new CacheEntry (m_sourcepath.length);
     93             m_packageCache.put (packageVMName, entry);
     94         }
     95 
     96         final Set [] listings = entry.m_listings;
     97         for (int p = 0; p < listings.length; ++ p)
     98         {
     99             Set listing = listings [p];
    100             if (listing == null)
    101             {
    102                 listing = faultListing (m_sourcepath [p], packageVMName);
    103                 listings [p] = listing;
    104             }
    105 
    106             // TODO: this is case-sensitive at this point
    107             if (listing.contains (name))
    108             {
    109                 final File relativeFile = new File (packageVMName.replace ('/', File.separatorChar), name);
    110 
    111                 return new File (m_sourcepath [p], relativeFile.getPath ()).getAbsoluteFile ();
    112             }
    113         }
    114 
    115         return null;
    116     }
    117 
    118     // protected: .............................................................
    119 
    120     // package: ...............................................................
    121 
    122     // private: ...............................................................
    123 
    124 
    125     private static final class CacheEntry
    126     {
    127         CacheEntry (final int size)
    128         {
    129             m_listings = new Set [size];
    130         }
    131 
    132 
    133         final Set /* String */ [] m_listings;
    134 
    135     } // end of nested class
    136 
    137 
    138     // NOTE: because java.io.* implements file filtering in bytecode
    139     // there is no real perf advantage in using a filter here (I might
    140     // as well do list() and filter the result myself
    141 
    142     private static final class FileExtensionFilter implements FileFilter
    143     {
    144         public boolean accept (final File file)
    145         {
    146             if ($assert.ENABLED) $assert.ASSERT (file != null, "file = null");
    147 
    148             if (file.isDirectory ()) return false; // filter out directories
    149 
    150             final String name = file.getName ();
    151             final int lastDot = name.lastIndexOf ('.');
    152             if (lastDot <= 0) return false;
    153 
    154             // [assertion: lastDot > 0]
    155 
    156             // note: match is case sensitive
    157             return m_extension.equals (name.substring (lastDot));
    158         }
    159 
    160         public String toString ()
    161         {
    162             return super.toString () + ", extension = [" + m_extension + "]";
    163         }
    164 
    165         FileExtensionFilter (final String extension)
    166         {
    167             if (extension == null)
    168                 throw new IllegalArgumentException ("null input: extension");
    169 
    170             // ensure starting '.':
    171             final String canonical = canonicalizeExtension (extension);
    172 
    173             if (extension.length () <= 1)
    174                 throw new IllegalArgumentException ("empty input: extension");
    175 
    176             m_extension = canonical;
    177         }
    178 
    179         private static String canonicalizeExtension (final String extension)
    180         {
    181             if (extension.charAt (0) != '.')
    182                 return ".".concat (extension);
    183             else
    184                 return extension;
    185         }
    186 
    187 
    188         private final String m_extension;
    189 
    190     } // end of nested class
    191 
    192 
    193     private Set /* String */ faultListing (final File dir, final String packageVMName)
    194     {
    195         if ($assert.ENABLED) $assert.ASSERT (dir != null, "dir = null");
    196         if ($assert.ENABLED) $assert.ASSERT (packageVMName != null, "packageVMName = null");
    197 
    198         final File packageFile = new File (dir, packageVMName.replace ('/', File.separatorChar));
    199 
    200         final File [] listing = packageFile.listFiles (FILE_EXTENSION_FILTER);
    201 
    202         if ((listing == null) || (listing.length == 0))
    203             return Collections.EMPTY_SET;
    204         else
    205         {
    206             final Set result = new HashSet (listing.length);
    207             for (int f = 0; f < listing.length; ++ f)
    208             {
    209                 result.add (listing [f].getName ());
    210             }
    211 
    212             return result;
    213         }
    214     }
    215 
    216 
    217     private final File [] m_sourcepath; // never null
    218     private final Map /* packageVMName:String->CacheEntry */ m_packageCache; // never null
    219 
    220     private static final FileExtensionFilter FILE_EXTENSION_FILTER; // set in <clinit>
    221 
    222     static
    223     {
    224         FILE_EXTENSION_FILTER = new FileExtensionFilter (".java");
    225     }
    226 
    227 } // end of class
    228 // ----------------------------------------------------------------------------