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