| /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under |
| * the terms of the Common Public License v1.0 which accompanies this distribution, |
| * and is available at http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * $Id: SourcePathCache.java,v 1.1.1.1 2004/05/09 16:57:39 vlad_r Exp $ |
| */ |
| package com.vladium.emma.report; |
| |
| import java.io.File; |
| import java.io.FileFilter; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import com.vladium.util.asserts.$assert; |
| |
| // ---------------------------------------------------------------------------- |
| /** |
| * @author Vlad Roubtsov, (C) 2003 |
| */ |
| public |
| final class SourcePathCache |
| { |
| // public: ................................................................ |
| |
| // TODO: use soft cache for m_packageCache? |
| |
| /** |
| * @param sourcepath [can be empty] |
| */ |
| public SourcePathCache (final String [] sourcepath, final boolean removeNonExistent) |
| { |
| if (sourcepath == null) throw new IllegalArgumentException ("null input: sourcepath"); |
| |
| final List _sourcepath = new ArrayList (sourcepath.length); |
| for (int i = 0; i < sourcepath.length; ++ i) |
| { |
| final File dir = new File (sourcepath [i]); |
| |
| if (! removeNonExistent || (dir.isDirectory () && dir.exists ())) |
| _sourcepath.add (dir); |
| } |
| |
| m_sourcepath = new File [_sourcepath.size ()]; |
| _sourcepath.toArray (m_sourcepath); |
| |
| m_packageCache = new HashMap (); |
| } |
| |
| /** |
| * @param sourcepath [can be empty] |
| */ |
| public SourcePathCache (final File [] sourcepath, final boolean removeNonExistent) |
| { |
| if (sourcepath == null) throw new IllegalArgumentException ("null input: sourcepath"); |
| |
| final List _sourcepath = new ArrayList (sourcepath.length); |
| for (int i = 0; i < sourcepath.length; ++ i) |
| { |
| final File dir = sourcepath [i]; |
| |
| if (! removeNonExistent || (dir.isDirectory () && dir.exists ())) |
| _sourcepath.add (dir); |
| } |
| |
| m_sourcepath = new File [_sourcepath.size ()]; |
| _sourcepath.toArray (m_sourcepath); |
| |
| m_packageCache = new HashMap (); |
| } |
| |
| /** |
| * @return absolute pathname [null if 'name' was not found in cache] |
| */ |
| public synchronized File find (final String packageVMName, final String name) |
| { |
| if (packageVMName == null) throw new IllegalArgumentException ("null input: packageVMName"); |
| if (name == null) throw new IllegalArgumentException ("null input: name"); |
| |
| if (m_sourcepath.length == 0) return null; |
| |
| CacheEntry entry = (CacheEntry) m_packageCache.get (packageVMName); |
| |
| if (entry == null) |
| { |
| entry = new CacheEntry (m_sourcepath.length); |
| m_packageCache.put (packageVMName, entry); |
| } |
| |
| final Set [] listings = entry.m_listings; |
| for (int p = 0; p < listings.length; ++ p) |
| { |
| Set listing = listings [p]; |
| if (listing == null) |
| { |
| listing = faultListing (m_sourcepath [p], packageVMName); |
| listings [p] = listing; |
| } |
| |
| // TODO: this is case-sensitive at this point |
| if (listing.contains (name)) |
| { |
| final File relativeFile = new File (packageVMName.replace ('/', File.separatorChar), name); |
| |
| return new File (m_sourcepath [p], relativeFile.getPath ()).getAbsoluteFile (); |
| } |
| } |
| |
| return null; |
| } |
| |
| // protected: ............................................................. |
| |
| // package: ............................................................... |
| |
| // private: ............................................................... |
| |
| |
| private static final class CacheEntry |
| { |
| CacheEntry (final int size) |
| { |
| m_listings = new Set [size]; |
| } |
| |
| |
| final Set /* String */ [] m_listings; |
| |
| } // end of nested class |
| |
| |
| // NOTE: because java.io.* implements file filtering in bytecode |
| // there is no real perf advantage in using a filter here (I might |
| // as well do list() and filter the result myself |
| |
| private static final class FileExtensionFilter implements FileFilter |
| { |
| public boolean accept (final File file) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (file != null, "file = null"); |
| |
| if (file.isDirectory ()) return false; // filter out directories |
| |
| final String name = file.getName (); |
| final int lastDot = name.lastIndexOf ('.'); |
| if (lastDot <= 0) return false; |
| |
| // [assertion: lastDot > 0] |
| |
| // note: match is case sensitive |
| return m_extension.equals (name.substring (lastDot)); |
| } |
| |
| public String toString () |
| { |
| return super.toString () + ", extension = [" + m_extension + "]"; |
| } |
| |
| FileExtensionFilter (final String extension) |
| { |
| if (extension == null) |
| throw new IllegalArgumentException ("null input: extension"); |
| |
| // ensure starting '.': |
| final String canonical = canonicalizeExtension (extension); |
| |
| if (extension.length () <= 1) |
| throw new IllegalArgumentException ("empty input: extension"); |
| |
| m_extension = canonical; |
| } |
| |
| private static String canonicalizeExtension (final String extension) |
| { |
| if (extension.charAt (0) != '.') |
| return ".".concat (extension); |
| else |
| return extension; |
| } |
| |
| |
| private final String m_extension; |
| |
| } // end of nested class |
| |
| |
| private Set /* String */ faultListing (final File dir, final String packageVMName) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (dir != null, "dir = null"); |
| if ($assert.ENABLED) $assert.ASSERT (packageVMName != null, "packageVMName = null"); |
| |
| final File packageFile = new File (dir, packageVMName.replace ('/', File.separatorChar)); |
| |
| final File [] listing = packageFile.listFiles (FILE_EXTENSION_FILTER); |
| |
| if ((listing == null) || (listing.length == 0)) |
| return Collections.EMPTY_SET; |
| else |
| { |
| final Set result = new HashSet (listing.length); |
| for (int f = 0; f < listing.length; ++ f) |
| { |
| result.add (listing [f].getName ()); |
| } |
| |
| return result; |
| } |
| } |
| |
| |
| private final File [] m_sourcepath; // never null |
| private final Map /* packageVMName:String->CacheEntry */ m_packageCache; // never null |
| |
| private static final FileExtensionFilter FILE_EXTENSION_FILTER; // set in <clinit> |
| |
| static |
| { |
| FILE_EXTENSION_FILTER = new FileExtensionFilter (".java"); |
| } |
| |
| } // end of class |
| // ---------------------------------------------------------------------------- |