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 */ SourcePathCache(final String [] sourcepath, final boolean removeNonExistent)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 */ SourcePathCache(final File [] sourcepath, final boolean removeNonExistent)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 */ find(final String packageVMName, final String name)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 { CacheEntry(final int size)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 { accept(final File file)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 toString()160 public String toString () 161 { 162 return super.toString () + ", extension = [" + m_extension + "]"; 163 } 164 FileExtensionFilter(final String extension)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 canonicalizeExtension(final String extension)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 faultListing(final File dir, final String packageVMName)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 // ----------------------------------------------------------------------------