1 /* 2 StatCvs - CVS statistics generation 3 Copyright (C) 2002 Lukasz Pekacki <lukasz@pekacki.de> 4 http://statcvs.sf.net/ 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 This library is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with this library; if not, write to the Free Software 18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 20 $RCSfile: FileCollectionFormatter.java,v $ 21 Created on $Date: 2008/04/02 11:22:15 $ 22 */ 23 package net.sf.statcvs.renderer; 24 25 import java.util.ArrayList; 26 import java.util.Collection; 27 import java.util.Collections; 28 import java.util.Iterator; 29 import java.util.List; 30 import java.util.NoSuchElementException; 31 32 import net.sf.statcvs.util.IntegerMap; 33 34 /** 35 * Groups a set of file names by directory. Provides a list 36 * of directories in the file set, and lumps directories 37 * with only one file together with its parent directory. 38 * 39 * @author Richard Cyganiak 40 * @version $Id: FileCollectionFormatter.java,v 1.9 2008/04/02 11:22:15 benoitx Exp $ 41 */ 42 public class FileCollectionFormatter { 43 44 private final Collection files; 45 private final IntegerMap filesPerDir; 46 private final IntegerMap dirDepths; 47 48 /** 49 * Creates a new instance from a <code>Collection</code> of 50 * file names. 51 * @param files Collection containing the String representations of files 52 */ FileCollectionFormatter(final Collection files)53 public FileCollectionFormatter(final Collection files) { 54 this.files = files; 55 filesPerDir = createFilesPerDirCount(); 56 dirDepths = createDirDepths(); 57 } 58 createFilesPerDirCount()59 private IntegerMap createFilesPerDirCount() { 60 final IntegerMap result = new IntegerMap(); 61 final Iterator it = files.iterator(); 62 while (it.hasNext()) { 63 final String file = (String) it.next(); 64 result.addInt(getDirectory(file), 1); 65 } 66 return result; 67 } 68 createDirDepths()69 private IntegerMap createDirDepths() { 70 final IntegerMap result = new IntegerMap(); 71 final Iterator it = filesPerDir.iteratorSortedByKey(); 72 while (it.hasNext()) { 73 final String dir = (String) it.next(); 74 result.put(dir, getDepth(dir)); 75 } 76 return result; 77 } 78 79 /** 80 * Gets a list of <code>String</code>s containing the 81 * directories in the file set, ordered by name. 82 * @return a list of <code>String</code>s containing the 83 * directories in the file set, ordered by name. 84 */ getDirectories()85 public List getDirectories() { 86 final List result = new ArrayList(); 87 final Iterator it = dirDepths.iteratorSortedByKey(); 88 while (it.hasNext()) { 89 final String directory = (String) it.next(); 90 result.add(directory); 91 } 92 return result; 93 } 94 95 /** 96 * Gets the names of all files which reside in a given directory. 97 * The directory must be one from the {@link #getDirectories} 98 * list. Files will be relative to the directory. They will be 99 * ordered by name. 100 * @param directory to process 101 * @return the names of all files which reside in a given directory. 102 * The directory must be one from the {@link #getDirectories} 103 * list. Files will be relative to the directory. They will be 104 * ordered by name. 105 */ getFiles(final String directory)106 public List getFiles(final String directory) { 107 if (!dirDepths.contains(directory)) { 108 throw new NoSuchElementException("doesn't contain directory '" + directory + "'"); 109 } 110 final List result = new ArrayList(getFilesInDir(directory)); 111 Collections.sort(result); 112 final List allSubdirFiles = getFilesInSubdirs(directory); 113 Collections.sort(allSubdirFiles); 114 result.addAll(allSubdirFiles); 115 return result; 116 } 117 getFilesInSubdirs(final String directory)118 private List getFilesInSubdirs(final String directory) { 119 final List result = new ArrayList(); 120 final Iterator it = files.iterator(); 121 while (it.hasNext()) { 122 final String filename = (String) it.next(); 123 if (isInDirectory(filename, directory) && !getDirectory(filename).equals(directory) && !isInDeeperDirectory(filename, directory)) { 124 result.add(getRelativeFilename(filename, directory)); 125 } 126 } 127 return result; 128 } 129 isInDeeperDirectory(final String filename, final String directory)130 private boolean isInDeeperDirectory(final String filename, final String directory) { 131 String currentDir = getDirectory(filename); 132 int currentDepth = getDepth(currentDir); 133 final int directoryDepth = getDepth(directory); 134 while (currentDepth > directoryDepth) { 135 if (dirDepths.contains(currentDir)) { 136 return true; 137 } 138 currentDepth--; 139 currentDir = getParent(currentDir); 140 } 141 return false; 142 } 143 getFilesInDir(final String directory)144 private List getFilesInDir(final String directory) { 145 final List result = new ArrayList(); 146 final Iterator it = files.iterator(); 147 while (it.hasNext()) { 148 final String filename = (String) it.next(); 149 if (getDirectory(filename).equals(directory)) { 150 result.add(getRelativeFilename(filename, directory)); 151 } 152 } 153 return result; 154 } 155 156 /** 157 * Returns TRUE if file is in specified directroy, FALSE otherwise 158 * @param filename File to test 159 * @param directory Directory to test 160 * @return boolean TRUE if file is in specified directroy, FALSE otherwise 161 */ isInDirectory(final String filename, final String directory)162 protected static boolean isInDirectory(final String filename, final String directory) { 163 return getDirectory(filename).startsWith(directory); 164 } 165 166 /** 167 * Returns relative filename for specified file and directory 168 * @param filename file 169 * @param dir directory 170 * @return String relative filename for specified file and directory 171 */ getRelativeFilename(final String filename, final String dir)172 protected static String getRelativeFilename(final String filename, final String dir) { 173 return filename.substring(dir.length()); 174 } 175 176 /** 177 * Returns directory name of specified file 178 * @param filename file to compute 179 * @return String directory name of specified file 180 */ getDirectory(final String filename)181 protected static String getDirectory(final String filename) { 182 return filename.substring(0, filename.lastIndexOf("/") + 1); 183 } 184 185 /** 186 * Returns name of parent directory to specified directory 187 * @param directory to use 188 * @return String name of parent directory to specified directory 189 */ getParent(final String directory)190 protected static String getParent(final String directory) { 191 final int lastIndex = directory.lastIndexOf("/"); 192 if (lastIndex == -1) { 193 return ""; 194 } 195 return directory.substring(0, directory.lastIndexOf("/", lastIndex - 1) + 1); 196 } 197 198 /** 199 * Returns the depth of the directory 200 * @param directory to be analysed 201 * @return int the depth of the directory 202 */ getDepth(final String directory)203 protected static int getDepth(final String directory) { 204 int result = 0; 205 int index = 0; 206 while (directory.indexOf("/", index) != -1) { 207 index = directory.indexOf("/", index) + 1; 208 result++; 209 } 210 return result; 211 } 212 } 213