001 /* 002 * Cobertura - http://cobertura.sourceforge.net/ 003 * 004 * Copyright (C) 2005 Jeremy Thomerson 005 * Copyright (C) 2005 Grzegorz Lukasik 006 * 007 * Cobertura is free software; you can redistribute it and/or modify 008 * it under the terms of the GNU General Public License as published 009 * by the Free Software Foundation; either version 2 of the License, 010 * or (at your option) any later version. 011 * 012 * Cobertura is distributed in the hope that it will be useful, but 013 * WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU General Public License 018 * along with Cobertura; if not, write to the Free Software 019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 020 * USA 021 */ 022 package net.sourceforge.cobertura.util; 023 024 import java.io.File; 025 import java.io.IOException; 026 import java.util.ArrayList; 027 import java.util.HashMap; 028 import java.util.HashSet; 029 import java.util.Iterator; 030 import java.util.List; 031 import java.util.Map; 032 import java.util.Set; 033 034 import org.apache.log4j.Logger; 035 036 037 /** 038 * Maps source file names to existing files. After adding description 039 * of places files can be found in, it can be used to localize 040 * the files. 041 * 042 * <p> 043 * FileFinder supports two types of source files locations: 044 * <ul> 045 * <li>source root directory, defines the directory under 046 * which source files are located,</li> 047 * <li>pair (base directory, file path relative to base directory).</li> 048 * </ul> 049 * The difference between these two is that in case of the first you add all 050 * source files under the specified root directory, and in the second you add 051 * exactly one file. In both cases file to be found has to be located under 052 * subdirectory that maps to package definition provided with the source file name. 053 * 054 * @author Jeremy Thomerson 055 */ 056 public class FileFinder { 057 058 private static Logger LOGGER = Logger.getLogger(FileFinder.class); 059 060 // Contains Strings with directory paths 061 private Set sourceDirectories = new HashSet(); 062 063 // Contains pairs (String directoryRoot, Set fileNamesRelativeToRoot) 064 private Map sourceFilesMap = new HashMap(); 065 066 /** 067 * Adds directory that is a root of sources. A source file 068 * that is under this directory will be found if relative 069 * path to the file from root matches package name. 070 * <p> 071 * Example: 072 * <pre> 073 * fileFinder.addSourceDirectory( "C:/MyProject/src/main"); 074 * fileFinder.addSourceDirectory( "C:/MyProject/src/test"); 075 * </pre> 076 * In path both / and \ can be used. 077 * </p> 078 * 079 * @param directory The root of source files 080 * @throws NullPointerException if <code>directory</code> is <code>null</code> 081 */ 082 public void addSourceDirectory( String directory) { 083 if( LOGGER.isDebugEnabled()) 084 LOGGER.debug( "Adding sourceDirectory=[" + directory + "]"); 085 086 // Change \ to / in case of Windows users 087 directory = getCorrectedPath(directory); 088 sourceDirectories.add(directory); 089 } 090 091 /** 092 * Adds file by specifying root directory and relative path to the 093 * file in it. Adds exactly one file, relative path should match 094 * package that the source file is in, otherwise it will be not 095 * found later. 096 * <p> 097 * Example: 098 * <pre> 099 * fileFinder.addSourceFile( "C:/MyProject/src/main", "com/app/MyClass.java"); 100 * fileFinder.addSourceFile( "C:/MyProject/src/test", "com/app/MyClassTest.java"); 101 * </pre> 102 * In paths both / and \ can be used. 103 * </p> 104 * 105 * @param baseDir sources root directory 106 * @param file path to source file relative to <code>baseDir</code> 107 * @throws NullPointerException if either <code>baseDir</code> or <code>file</code> is <code>null</code> 108 */ 109 public void addSourceFile( String baseDir, String file) { 110 if( LOGGER.isDebugEnabled()) 111 LOGGER.debug( "Adding sourceFile baseDir=[" + baseDir + "] file=[" + file + "]"); 112 113 if( baseDir==null || file==null) 114 throw new NullPointerException(); 115 116 // Change \ to / in case of Windows users 117 file = getCorrectedPath( file); 118 baseDir = getCorrectedPath( baseDir); 119 120 // Add file to sourceFilesMap 121 Set container = (Set) sourceFilesMap.get(baseDir); 122 if( container==null) { 123 container = new HashSet(); 124 sourceFilesMap.put( baseDir, container); 125 } 126 container.add( file); 127 } 128 129 /** 130 * Maps source file name to existing file. 131 * When mapping file name first values that were added with 132 * {@link #addSourceDirectory} and later added with {@link #addSourceFile} are checked. 133 * 134 * @param fileName source file to be mapped 135 * @return existing file that maps to passed sourceFile 136 * @throws IOException if cannot map source file to existing file 137 * @throws NullPointerException if fileName is null 138 */ 139 public File getFileForSource(String fileName) throws IOException { 140 // Correct file name 141 if( LOGGER.isDebugEnabled()) 142 LOGGER.debug( "Searching for file, name=[" + fileName + "]"); 143 fileName = getCorrectedPath( fileName); 144 145 // Check inside sourceDirectories 146 for( Iterator it=sourceDirectories.iterator(); it.hasNext();) { 147 String directory = (String)it.next(); 148 File file = new File( directory, fileName); 149 if( file.isFile()) { 150 LOGGER.debug( "Found inside sourceDirectories"); 151 return file; 152 } 153 } 154 155 // Check inside sourceFilesMap 156 for( Iterator it=sourceFilesMap.keySet().iterator(); it.hasNext();) { 157 String directory = (String)it.next(); 158 Set container = (Set) sourceFilesMap.get(directory); 159 if( !container.contains( fileName)) 160 continue; 161 File file = new File( directory, fileName); 162 if( file.isFile()) { 163 LOGGER.debug( "Found inside sourceFilesMap"); 164 return file; 165 } 166 } 167 168 // Have not found? Throw an error. 169 LOGGER.debug( "File not found"); 170 throw new IOException( "Cannot find source file, name=["+fileName+"]"); 171 } 172 173 /** 174 * Returns a list with string for all source directories. 175 * Example: <code>[C:/MyProject/src/main,C:/MyProject/src/test]</code> 176 * 177 * @return list with Strings for all source roots, or empty list if no source roots were specified 178 */ 179 public List getSourceDirectoryList() { 180 // Get names from sourceDirectories 181 List result = new ArrayList(); 182 for( Iterator it=sourceDirectories.iterator(); it.hasNext();) { 183 result.add( it.next()); 184 } 185 186 // Get names from sourceFilesMap 187 for( Iterator it=sourceFilesMap.keySet().iterator(); it.hasNext();) { 188 result.add(it.next()); 189 } 190 191 // Return combined names 192 return result; 193 } 194 195 private String getCorrectedPath(String path) { 196 return path.replace('\\', '/'); 197 } 198 199 /** 200 * Returns string representation of FileFinder. 201 */ 202 public String toString() { 203 return "FileFinder, source directories: " + getSourceDirectoryList().toString(); 204 } 205 }