Coverage Report - org.jbehave.core.io.StoryFinder
 
Classes in this File Line Coverage Branch Coverage Complexity
StoryFinder
96%
50/52
100%
12/12
1.44
StoryFinder$1
100%
3/3
N/A
1.44
StoryFinder$2
100%
3/3
N/A
1.44
StoryFinder$3
100%
5/5
100%
2/2
1.44
 
 1  
 package org.jbehave.core.io;
 2  
 
 3  
 import java.io.File;
 4  
 import java.net.URL;
 5  
 import java.util.ArrayList;
 6  
 import java.util.Collections;
 7  
 import java.util.Comparator;
 8  
 import java.util.List;
 9  
 
 10  
 import org.apache.commons.collections.CollectionUtils;
 11  
 import org.apache.commons.collections.Transformer;
 12  
 import org.apache.commons.lang.StringUtils;
 13  
 import org.codehaus.plexus.util.DirectoryScanner;
 14  
 
 15  
 import static java.util.Arrays.asList;
 16  
 
 17  
 /**
 18  
  * Finds stories by scanning source paths, which can be either filesystem
 19  
  * directories or jars. Jars are identified by paths ending in ".jar".
 20  
  * 
 21  
  * Stories can be either in the form of class names or story paths.
 22  
  * 
 23  
  * Stories can be sorted by providing a sorting comparator.
 24  
  */
 25  
 public class StoryFinder {
 26  
 
 27  
     private static final String JAR = ".jar";
 28  
     private static final String JAVA = ".java";
 29  
     private final String classNameExtension;
 30  
     private final Comparator<? super String> sortingComparator;
 31  
 
 32  
     public StoryFinder() {
 33  16
         this(JAVA);
 34  16
     }
 35  
 
 36  
     public StoryFinder(String classNameExtension) {
 37  17
         this(classNameExtension, null);
 38  17
     }
 39  
 
 40  
     public StoryFinder(Comparator<? super String> sortingComparator) {
 41  1
         this(JAVA, sortingComparator);
 42  1
     }
 43  
 
 44  18
     private StoryFinder(String classNameExtension, Comparator<? super String> sortingComparator) {
 45  18
         this.classNameExtension = classNameExtension;
 46  18
         this.sortingComparator = sortingComparator;
 47  18
     }
 48  
 
 49  
     /**
 50  
      * Finds Java classes from a source path, allowing for includes/excludes,
 51  
      * and converts them to class names.
 52  
      * 
 53  
      * @param searchIn the path to search in
 54  
      * @param includes the List of include patterns, or <code>null</code> if
 55  
      *            none
 56  
      * @param excludes the List of exclude patterns, or <code>null</code> if
 57  
      *            none
 58  
      * @return A List of class names found
 59  
      */
 60  
     public List<String> findClassNames(String searchIn, List<String> includes, List<String> excludes) {
 61  4
         return classNames(normalise(sort(scan(searchIn, includes, excludes))));
 62  
     }
 63  
 
 64  
     /**
 65  
      * Finds paths from a source URL, allowing for single include/exclude
 66  
      * pattern. Paths found are normalised by {@link
 67  
      * StoryFinder#normalise(List<String>)}
 68  
      * 
 69  
      * @param searchIn the source URL to search in
 70  
      * @param include the include pattern, or <code>""</code> if none
 71  
      * @param exclude the exclude pattern, or <code>""</code> if none
 72  
      * @return A List of paths found
 73  
      */
 74  
     public List<String> findPaths(URL searchIn, String include, String exclude) {
 75  2
         return findPaths(CodeLocations.getPathFromURL(searchIn), asCSVList(include), asCSVList(exclude));
 76  
     }
 77  
 
 78  
     /**
 79  
      * Finds paths from a source URL, allowing for includes/excludes patterns.
 80  
      * Paths found are normalised by {@link StoryFinder#normalise(List<String>)}
 81  
      * 
 82  
      * @param searchIn the source URL to search in
 83  
      * @param includes the Array of include patterns, or <code>null</code> if
 84  
      *            none
 85  
      * @param excludes the Array of exclude patterns, or <code>null</code> if
 86  
      *            none
 87  
      * @return A List of paths found
 88  
      */
 89  
     public List<String> findPaths(URL searchIn, String[] includes, String[] excludes) {
 90  0
         return findPaths(CodeLocations.getPathFromURL(searchIn), asList(includes), asList(excludes));
 91  
     }
 92  
 
 93  
     /**
 94  
      * Finds paths from a source path, allowing for include/exclude patterns,
 95  
      * which can be comma-separated values of multiple patterns.
 96  
      * 
 97  
      * Paths found are normalised by {@link StoryFinder#normalise(List<String>)}
 98  
      * 
 99  
      * @param searchIn the source path to search in
 100  
      * @param include the CSV include pattern, or <code>null</code> if none
 101  
      * @param exclude the CSV exclude pattern, or <code>null</code> if none
 102  
      * @return A List of paths found
 103  
      */
 104  
     public List<String> findPaths(String searchIn, String include, String exclude) {
 105  3
         return findPaths(searchIn, asCSVList(include), asCSVList(exclude));
 106  
     }
 107  
 
 108  
     /**
 109  
      * Finds paths from a source path, allowing for include/exclude patterns.
 110  
      * Paths found are normalised by {@link StoryFinder#normalise(List<String>)}
 111  
      * 
 112  
      * @param searchIn the source path to search in
 113  
      * @param includes the Array of include patterns, or <code>null</code> if
 114  
      *            none
 115  
      * @param excludes the Array of exclude patterns, or <code>null</code> if
 116  
      *            none
 117  
      * @return A List of paths found
 118  
      */
 119  
     public List<String> findPaths(String searchIn, String[] includes, String[] excludes) {
 120  1
         return findPaths(searchIn, asList(includes), asList(excludes));
 121  
     }
 122  
 
 123  
     /**
 124  
      * Finds paths from a source URL, allowing for includes/excludes patterns.
 125  
      * Paths found are normalised by {@link StoryFinder#normalise(List<String>)}
 126  
      * 
 127  
      * @param searchIn the source URL to search in
 128  
      * @param includes the List of include patterns, or <code>null</code> if
 129  
      *            none
 130  
      * @param excludes the List of exclude patterns, or <code>null</code> if
 131  
      *            none
 132  
      * @return A List of paths found
 133  
      */
 134  
     public List<String> findPaths(URL searchIn, List<String> includes, List<String> excludes) {
 135  0
         return findPaths(CodeLocations.getPathFromURL(searchIn), includes, excludes);
 136  
     }
 137  
 
 138  
     /**
 139  
      * Finds paths from a source path, allowing for include/exclude patterns.
 140  
      * Paths found are normalised by {@link StoryFinder#normalise(List<String>)}
 141  
      * .
 142  
      * 
 143  
      * @param searchIn the source path to search in
 144  
      * @param includes the List of include patterns, or <code>null</code> if
 145  
      *            none
 146  
      * @param excludes the List of exclude patterns, or <code>null</code> if
 147  
      *            none
 148  
      * @return A List of paths found
 149  
      */
 150  
     public List<String> findPaths(String searchIn, List<String> includes, List<String> excludes) {
 151  9
         return normalise(sort(scan(searchIn, includes, excludes)));
 152  
     }
 153  
 
 154  
     /**
 155  
      * Finds paths from a source path, allowing for includes/excludes. Paths
 156  
      * found are prefixed with specified path by {@link
 157  
      * StoryFinder#prefix(String, List<String>)} and normalised by {@link
 158  
      * StoryFinder#normalise(List<String>)}.
 159  
      * 
 160  
      * @param searchIn the source path to search in
 161  
      * @param includes the List of include patterns, or <code>null</code> if
 162  
      *            none
 163  
      * @param excludes the List of exclude patterns, or <code>null</code> if
 164  
      *            none
 165  
      * @param prefixWith the root path prefixed to all paths found, or
 166  
      *            <code>null</code> if none
 167  
      * @return A List of paths found
 168  
      */
 169  
     public List<String> findPaths(String searchIn, List<String> includes, List<String> excludes, String prefixWith) {
 170  2
         return normalise(prefix(prefixWith, sort(scan(searchIn, includes, excludes))));
 171  
     }
 172  
 
 173  
     protected List<String> normalise(List<String> paths) {
 174  17
         List<String> transformed = new ArrayList<String>(paths);
 175  17
         CollectionUtils.transform(transformed, new Transformer() {
 176  
             public Object transform(Object input) {
 177  131
                 String path = (String) input;
 178  131
                 return path.replace('\\', '/');
 179  
             }
 180  
         });
 181  17
         return transformed;
 182  
     }
 183  
 
 184  
     protected List<String> prefix(final String prefixWith, List<String> paths) {
 185  2
         if (StringUtils.isBlank(prefixWith)) {
 186  1
             return paths;
 187  
         }
 188  1
         List<String> transformed = new ArrayList<String>(paths);
 189  1
         CollectionUtils.transform(transformed, new Transformer() {
 190  
             public Object transform(Object input) {
 191  4
                 String path = (String) input;
 192  4
                 return prefixWith + path;
 193  
             }
 194  
         });
 195  1
         return transformed;
 196  
     }
 197  
 
 198  
     protected List<String> classNames(List<String> paths) {
 199  4
         List<String> trasformed = new ArrayList<String>(paths);
 200  4
         CollectionUtils.transform(trasformed, new Transformer() {
 201  
             public Object transform(Object input) {
 202  8
                 String path = (String) input;
 203  8
                 if (!StringUtils.endsWithIgnoreCase(path, classNameExtension())) {
 204  1
                     return input;
 205  
                 }
 206  7
                 return StringUtils.removeEndIgnoreCase(path, classNameExtension()).replace('/', '.');
 207  
             }
 208  
         });
 209  4
         return trasformed;
 210  
     }
 211  
 
 212  
     protected String classNameExtension() {
 213  15
         return classNameExtension;
 214  
     }
 215  
 
 216  
     protected List<String> sort(List<String> input) {
 217  15
         List<String> sorted = new ArrayList<String>(input);
 218  15
         Collections.sort(sorted, sortingComparator());
 219  15
         return sorted;
 220  
     }
 221  
 
 222  
     /**
 223  
      * Comparator used for sorting. A <code>null</code> comparator means that
 224  
      * {@link Collections#sort()} will use natural ordering.
 225  
      * 
 226  
      * @return A Comparator or <code>null</code> for natural ordering.
 227  
      */
 228  
     protected Comparator<? super String> sortingComparator() {
 229  15
         return sortingComparator;
 230  
     }
 231  
 
 232  
     protected List<String> scan(String source, List<String> includes, List<String> excludes) {
 233  15
         if (source.endsWith(JAR)) {
 234  2
             return scanJar(source, includes, excludes);
 235  
         }
 236  13
         return scanDirectory(source, includes, excludes);
 237  
     }
 238  
 
 239  
     private List<String> asCSVList(String pattern) {
 240  
         List<String> list;
 241  10
         if (pattern == null) {
 242  2
             list = asList();
 243  
         } else {
 244  8
             list = asList(pattern.split(","));
 245  
         }
 246  10
         return list;
 247  
     }
 248  
 
 249  
     private List<String> scanDirectory(String basedir, List<String> includes, List<String> excludes) {
 250  13
         DirectoryScanner scanner = new DirectoryScanner();
 251  13
         if (!new File(basedir).exists()) {
 252  1
             return new ArrayList<String>();
 253  
         }
 254  12
         scanner.setBasedir(basedir);
 255  12
         if (includes != null) {
 256  11
             scanner.setIncludes(includes.toArray(new String[includes.size()]));
 257  
         }
 258  12
         if (excludes != null) {
 259  11
             scanner.setExcludes(excludes.toArray(new String[excludes.size()]));
 260  
         }
 261  12
         scanner.scan();
 262  12
         return asList(scanner.getIncludedFiles());
 263  
     }
 264  
 
 265  
     protected List<String> scanJar(String jarPath, List<String> includes, List<String> excludes) {
 266  2
         return new JarFileScanner(jarPath, includes, excludes).scan();
 267  
     }
 268  
 
 269  
 }