Coverage Report - org.apache.creadur.whisker.scan.FromFileSystem
 
Classes in this File Line Coverage Branch Coverage Complexity
FromFileSystem
0%
0/3
N/A
2.333
FromFileSystem$Builder
0%
0/26
0%
0/8
2.333
FromFileSystem$Builder$Work
0%
0/56
0%
0/38
2.333
 
 1  
 /**
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  *  to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  *  with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.creadur.whisker.scan;
 20  
 
 21  
 import java.io.File;
 22  
 import java.io.IOException;
 23  
 import java.util.ArrayList;
 24  
 import java.util.Collection;
 25  
 import java.util.LinkedList;
 26  
 import java.util.Queue;
 27  
 import java.util.Set;
 28  
 import java.util.TreeSet;
 29  
 
 30  
 /**
 31  
  * Scans directories for resources, within a file system.
 32  
  */
 33  
 public class FromFileSystem {
 34  
 
 35  
     /**
 36  
      * Base constructor.
 37  
      */
 38  
     public FromFileSystem() {
 39  0
         super();
 40  0
     }
 41  
 
 42  
     /**
 43  
      * Builds description based on given directory.
 44  
      * @param base names the base directory, not null
 45  
      * @return collected directories within the base, not null
 46  
      * @throws IOException when the scanning fails
 47  
      */
 48  
     public Collection<Directory> withBase(final String base)
 49  
             throws IOException {
 50  0
         return new Builder(base).build();
 51  
     }
 52  
 
 53  
     /**
 54  
      * Builds a description of a file system.
 55  
      */
 56  
     private final static class Builder {
 57  
         /** Initial capacity for the backing array. */
 58  
         private static final int DEFAULT_INITIAL_CAPACITY = 64;
 59  
         /** Directory scanning base. */
 60  
         private final File base;
 61  
         /** Directories scanned. */
 62  
         private final Set<Directory> directories;
 63  
         /** Queues work not yet complete. */
 64  
         private final Queue<Work> workInProgress;
 65  
         /** Stores work done. */
 66  
         private final Collection<Work> workDone;
 67  
 
 68  
         /**
 69  
          * Constructs a builder with given base
 70  
          * (and default backing array).
 71  
          * @param base not null
 72  
          */
 73  
         public Builder(final String base) {
 74  0
             this(base, DEFAULT_INITIAL_CAPACITY);
 75  0
         }
 76  
 
 77  
         /**
 78  
          * Constructs a builder.
 79  
          * @param base not null
 80  
          * @param initialCapacity initial capacity for backing array
 81  
          */
 82  
         public Builder(final String base, final int initialCapacity) {
 83  0
             super();
 84  0
             this.base = new File(base);
 85  0
             directories = new TreeSet<Directory>();
 86  0
             workInProgress = new LinkedList<Work>();
 87  0
             workDone = new ArrayList<Work>(initialCapacity);
 88  0
         }
 89  
 
 90  
         /**
 91  
          * Builds directories.
 92  
          * @return not null
 93  
          * @throws IOException when scanning fails
 94  
          */
 95  
         public Collection<Directory> build() throws IOException {
 96  0
             put(base).andWork().untilDone();
 97  0
             return directories;
 98  
         }
 99  
 
 100  
         /**
 101  
          * Waiting until work done.
 102  
          */
 103  0
         private void untilDone() { }
 104  
 
 105  
         /**
 106  
          * Adds file work to the queue.
 107  
          * @param file not null
 108  
          * @return this, not null
 109  
          */
 110  
         private Builder put(final File file) {
 111  0
             return put(new Work(file));
 112  
         }
 113  
 
 114  
         /**
 115  
          * Queues work.
 116  
          * @param work not null
 117  
          * @return this, not null
 118  
          */
 119  
         private Builder put(final Work work) {
 120  0
             if (work != null) {
 121  0
                 if (workDone.contains(work)) {
 122  0
                     alreadyDone(work);
 123  
                 } else {
 124  0
                     this.workInProgress.add(work);
 125  
                 }
 126  
             }
 127  0
             return this;
 128  
         }
 129  
 
 130  
         /**
 131  
          * Notes that work has already been done.
 132  
          * @param work not null
 133  
          */
 134  
         private void alreadyDone(final Work work) {
 135  0
             System.out.println("Already done " + work);
 136  0
         }
 137  
 
 138  
         /**
 139  
          * Starts work.
 140  
          * @return this, not null
 141  
          */
 142  
         private Builder andWork() {
 143  0
             while (!workInProgress.isEmpty()) {
 144  0
                 workDone.add(workOn(workInProgress.poll()));
 145  
             }
 146  0
             return this;
 147  
         }
 148  
 
 149  
         /**
 150  
          * Performs work.
 151  
          * @param next not null
 152  
          * @return the work done, not null
 153  
          */
 154  
         private Work workOn(final Work next) {
 155  0
             for (final String name: next.contents()) {
 156  0
                 put(next.whenDirectory(name));
 157  
             }
 158  0
             directories.add(next.build());
 159  0
             return next;
 160  
         }
 161  
 
 162  
         /**
 163  
          * Computes the contents of a directory.
 164  
          */
 165  
         private static final class Work {
 166  
             /** Represents base directory. */
 167  
             private static final String BASE_DIRECTORY = ".";
 168  
             /** Names the directory. */
 169  
             private final String name;
 170  
             /** The directory worked on. */
 171  
             private final File file;
 172  
 
 173  
             /**
 174  
              * Constructs work.
 175  
              * @param file not null
 176  
              */
 177  
             public Work(final File file) {
 178  0
                 this(BASE_DIRECTORY, file);
 179  0
             }
 180  
 
 181  
             /**
 182  
              * Constructs work.
 183  
              * @param name not null
 184  
              * @param file not null
 185  
              */
 186  0
             public Work(final String name, final File file) {
 187  0
                 if (!file.exists()) {
 188  0
                     throw new IllegalArgumentException(
 189  
                             "Expected '" + file.getAbsolutePath() + "' to exist");
 190  
                 }
 191  0
                 if (!file.isDirectory()) {
 192  0
                     throw new IllegalArgumentException(
 193  
                             "Expected '" + file.getAbsolutePath() + "' to be a directory");
 194  
                 }
 195  0
                 this.name = name;
 196  0
                 this.file = file;
 197  0
             }
 198  
 
 199  
             /**
 200  
              * Gets the contents of the work directory.
 201  
              * @return not null
 202  
              */
 203  
             public String[] contents() {
 204  0
                 final String[] contents = file.list();
 205  0
                 if (contents == null) {
 206  0
                     throw new IllegalArgumentException("Cannot list content of " + file);
 207  
                 }
 208  0
                 return contents;
 209  
             }
 210  
 
 211  
             /**
 212  
              * Builds a directory.
 213  
              * @return not null
 214  
              */
 215  
             public Directory build() {
 216  0
                 final Directory result = new Directory().setName(name);
 217  0
                 for (final String name : contents()) {
 218  0
                     if (isResource(name)) {
 219  0
                         result.addResource(name);
 220  
                     }
 221  
                 }
 222  0
                 return result;
 223  
             }
 224  
 
 225  
             /**
 226  
              * Is the named file a resource?
 227  
              * @param name not null
 228  
              * @return true when the named file is a resource,
 229  
              * false otherwise
 230  
              */
 231  
             private boolean isResource(final String name) {
 232  0
                 return !isDirectory(name);
 233  
             }
 234  
 
 235  
             /**
 236  
              * Is the named file a directory?
 237  
              * @param name not null
 238  
              * @return true when the named file is a directory,
 239  
              * false otherwise
 240  
              */
 241  
             private boolean isDirectory(final String name) {
 242  0
                 return file(name).isDirectory();
 243  
             }
 244  
 
 245  
             /**
 246  
              * Creates new work.
 247  
              * @param name not null
 248  
              * @return work for the named directory,
 249  
              * or null when the resource named is not a directory
 250  
              */
 251  
             public Work whenDirectory(final String name) {
 252  0
                 final File file = file(name);
 253  
                 final Work result;
 254  0
                 if (file.isDirectory()) {
 255  0
                     result = new Work(path(name), file);
 256  
                 } else {
 257  0
                     result = null;
 258  
                 }
 259  0
                 return result;
 260  
             }
 261  
 
 262  
             /**
 263  
              * Converts a name to a path relative to base.
 264  
              * @param name not null
 265  
              * @return not null
 266  
              */
 267  
             private String path(final String name) {
 268  
                 final String result;
 269  0
                 if (isBaseDirectory()) {
 270  0
                     result = name;
 271  
                 } else {
 272  0
                     result = this.name + "/" + name;
 273  
                 }
 274  0
                 return result;
 275  
             }
 276  
 
 277  
             /**
 278  
              * This the work done in the base directory.
 279  
              * @return true when this is the base, false otherwise.
 280  
              */
 281  
             private boolean isBaseDirectory() {
 282  0
                 return BASE_DIRECTORY.equals(this.name);
 283  
             }
 284  
 
 285  
             /**
 286  
              * Creates a file.
 287  
              * @param name not null
 288  
              * @return file with given name
 289  
              */
 290  
             private File file(String name) {
 291  0
                 return new File(this.file, name);
 292  
             }
 293  
 
 294  
             /**
 295  
              * Computes some suitable hash.
 296  
              * @return a hash code
 297  
              * @see java.lang.Object#hashCode()
 298  
              */
 299  
             @Override
 300  
             public int hashCode() {
 301  0
                 final int prime = 31;
 302  0
                 int result = 1;
 303  0
                 result = prime * result
 304  
                         + ((file == null) ? 0 : file.hashCode());
 305  0
                 result = prime * result
 306  
                         + ((name == null) ? 0 : name.hashCode());
 307  0
                 return result;
 308  
             }
 309  
 
 310  
             /**
 311  
              * Equal when both name and file are equal.
 312  
              * @param obj possibly null
 313  
              * @return true when equal, false otherwise
 314  
              * @see java.lang.Object#equals(java.lang.Object)
 315  
              */
 316  
             @Override
 317  
             public boolean equals(final Object obj) {
 318  0
                 if (this == obj) {
 319  0
                     return true;
 320  
                 }
 321  0
                 if (obj == null) {
 322  0
                     return false;
 323  
                 }
 324  0
                 if (getClass() != obj.getClass()) {
 325  0
                     return false;
 326  
                 }
 327  0
                 final Work other = (Work) obj;
 328  0
                 if (file == null) {
 329  0
                     if (other.file != null)
 330  0
                         return false;
 331  0
                 } else if (!file.equals(other.file))
 332  0
                     return false;
 333  0
                 if (name == null) {
 334  0
                     if (other.name != null) {
 335  0
                         return false;
 336  
                     }
 337  0
                 } else if (!name.equals(other.name)) {
 338  0
                     return false;
 339  
                 }
 340  0
                 return true;
 341  
             }
 342  
 
 343  
             /**
 344  
              * Something suitable for logging.
 345  
              * @return not null
 346  
              * @see java.lang.Object#toString()
 347  
              */
 348  
             @Override
 349  
             public String toString() {
 350  0
                 return "Work [name=" + name + ", file=" + file + "]";
 351  
             }
 352  
         }
 353  
     }
 354  
 }