Coverage Report - de.smartics.util.lang.classpath.ClassPathDirectoryListing
 
Classes in this File Line Coverage Branch Coverage Complexity
ClassPathDirectoryListing
0%
0/51
0%
0/22
3,875
 
 1  
 /*
 2  
  * Copyright 2011-2013 smartics, Kronseder & Reiner GmbH
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *     http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package de.smartics.util.lang.classpath;
 17  
 
 18  
 import java.io.File;
 19  
 import java.io.IOException;
 20  
 import java.net.URISyntaxException;
 21  
 import java.net.URL;
 22  
 import java.net.URLDecoder;
 23  
 import java.util.ArrayList;
 24  
 import java.util.Arrays;
 25  
 import java.util.Enumeration;
 26  
 import java.util.List;
 27  
 import java.util.jar.JarEntry;
 28  
 import java.util.jar.JarFile;
 29  
 
 30  
 import org.apache.commons.lang.StringUtils;
 31  
 
 32  
 import de.smartics.util.lang.Arg;
 33  
 
 34  
 /**
 35  
  * Lists the content of a directory on the class path.
 36  
  * <p>
 37  
  * Currently only the protocols
 38  
  * </p>
 39  
  * <ol>
 40  
  * <li>file system (<code>file</code>) and</li>
 41  
  * <li>JAR files (<code>jar</code>)</li>
 42  
  * </ol>
 43  
  * <p>
 44  
  * are supported.
 45  
  * </p>
 46  
  */
 47  
 public class ClassPathDirectoryListing
 48  
 {
 49  
   // ********************************* Fields *********************************
 50  
 
 51  
   // --- constants ------------------------------------------------------------
 52  
 
 53  
   // --- members --------------------------------------------------------------
 54  
 
 55  
   /**
 56  
    * The context to load the directory listings.
 57  
    */
 58  
   private final ClassPathContext context;
 59  
 
 60  
   // ****************************** Initializer *******************************
 61  
 
 62  
   // ****************************** Constructors ******************************
 63  
 
 64  
   /**
 65  
    * Default constructor.
 66  
    *
 67  
    * @param classLoader the class loader to load the directory listings.
 68  
    * @throws NullPointerException if {@code classLoader} is <code>null</code>.
 69  
    */
 70  
   public ClassPathDirectoryListing(final ClassLoader classLoader)
 71  
     throws NullPointerException
 72  
   {
 73  0
     this(new ClassPathContext(classLoader, null));
 74  0
   }
 75  
 
 76  
   /**
 77  
    * Default constructor.
 78  
    *
 79  
    * @param context the context to load the directory listings.
 80  
    * @throws NullPointerException if {@code classLoader} is <code>null</code>.
 81  
    */
 82  
   public ClassPathDirectoryListing(final ClassPathContext context)
 83  
     throws NullPointerException
 84  0
   {
 85  0
     Arg.checkNotNull("context", context);
 86  0
     this.context = context;
 87  0
   }
 88  
 
 89  
   // ****************************** Inner Classes *****************************
 90  
 
 91  
   // ********************************* Methods ********************************
 92  
 
 93  
   // --- init -----------------------------------------------------------------
 94  
 
 95  
   // --- get&set --------------------------------------------------------------
 96  
 
 97  
   /**
 98  
    * Returns the context to load the directory listings.
 99  
    *
 100  
    * @return the context to load the directory listings.
 101  
    */
 102  
   public final ClassPathContext getClassPathContext()
 103  
   {
 104  0
     return context;
 105  
   }
 106  
 
 107  
   // --- business -------------------------------------------------------------
 108  
 
 109  
   /**
 110  
    * Lists the contents of the resource path.
 111  
    *
 112  
    * @param resourcePath the path to the resource whose contents is to be
 113  
    *          listed. The empty string returns the contents of the class
 114  
    *          loader's root directory (which is usually the parent class
 115  
    *          loader's root).
 116  
    * @return the contents of the resource as names. The list may be empty, but
 117  
    *         is never <code>null</code>.
 118  
    * @throws NullPointerException if {@code resourcePath} is <code>null</code>.
 119  
    * @throws IllegalArgumentException if resource path cannot be resolved to
 120  
    *           determine the contents. Either the protocol is unknown or there
 121  
    *           is a problem to access the resource's content physically.
 122  
    * @impl Sub classes may override this method to add additional protocol
 123  
    *       handlers. Call this method, if the derived handler does not handle
 124  
    *       the protocol.
 125  
    */
 126  
   public List<String> list(final String resourcePath)
 127  
     throws NullPointerException, IllegalArgumentException
 128  
   {
 129  0
     Arg.checkNotNull("resourcePath", resourcePath);
 130  
 
 131  0
     final URL resourcePathUrl = context.getResource(resourcePath);
 132  0
     if (resourcePathUrl == null)
 133  
     {
 134  0
       throw new IllegalArgumentException("Cannot find resource '"
 135  
                                          + resourcePath + "' on class path.");
 136  
     }
 137  
 
 138  0
     final String protocol = resourcePathUrl.getProtocol();
 139  0
     if ("file".equals(protocol))
 140  
     {
 141  0
       return handleFile(resourcePath, resourcePathUrl);
 142  
     }
 143  0
     else if ("jar".equals(protocol))
 144  
     {
 145  0
       return handleJar(resourcePath, resourcePathUrl);
 146  
     }
 147  
 
 148  0
     throw new IllegalArgumentException(
 149  
         "Protocol '" + protocol + "' is not supported to resolve resource '"
 150  
             + resourcePath + "'.");
 151  
   }
 152  
 
 153  
   private List<String> handleFile(final String resourcePath,
 154  
       final URL resourcePathUrl) throws IllegalArgumentException
 155  
   {
 156  
     try
 157  
     {
 158  0
       final String[] contentsArray = new File(resourcePathUrl.toURI()).list();
 159  0
       return Arrays.asList(contentsArray);
 160  
     }
 161  0
     catch (final URISyntaxException e)
 162  
     {
 163  0
       throw new IllegalArgumentException(
 164  
           "Cannot read URL derived from resource '" + resourcePath
 165  
               + "' on the class path.", e);
 166  
     }
 167  
   }
 168  
 
 169  
   private List<String> handleJar(final String resourcePath,
 170  
       final URL resourcePathUrl) throws IllegalArgumentException
 171  
   {
 172  
     try
 173  
     {
 174  0
       final List<String> contents = new ArrayList<String>();
 175  
 
 176  0
       final int separatorIndex = resourcePathUrl.getPath().indexOf('!');
 177  0
       final String jarPath =
 178  
           resourcePathUrl.getPath().substring(5, separatorIndex);
 179  0
       final JarFile jar = new JarFile(URLDecoder.decode(jarPath, "UTF-8"));
 180  0
       traverseJarEntries(resourcePath, contents, jar);
 181  
 
 182  0
       return contents;
 183  
     }
 184  0
     catch (final IOException e)
 185  
     {
 186  0
       throw new IllegalArgumentException("Read from JAR '" + resourcePath
 187  
                                          + "'.", e);
 188  
     }
 189  
   }
 190  
 
 191  
   private void traverseJarEntries(final String resourcePath,
 192  
       final List<String> contents, final JarFile jar)
 193  
   {
 194  0
     final int resourcePathLength = resourcePath.length();
 195  
 
 196  0
     final Enumeration<JarEntry> entries = jar.entries();
 197  0
     while (entries.hasMoreElements())
 198  
     {
 199  0
       final String name = entries.nextElement().getName();
 200  0
       if (name.startsWith(resourcePath))
 201  
       {
 202  0
         final String entry = name.substring(resourcePathLength);
 203  0
         final String normalized = normalize(entry);
 204  
 
 205  0
         if (normalized != null && !contents.contains(normalized))
 206  
         {
 207  0
           contents.add(normalized);
 208  
         }
 209  
       }
 210  0
     }
 211  0
   }
 212  
 
 213  
   private static String normalize(final String entry)
 214  
   {
 215  0
     if (StringUtils.isBlank(entry))
 216  
     {
 217  0
       return null;
 218  
     }
 219  
 
 220  0
     String normalized = entry;
 221  0
     if (normalized.charAt(0) == '/')
 222  
     {
 223  0
       if (entry.length() == 1)
 224  
       {
 225  0
         return null;
 226  
       }
 227  0
       normalized = normalized.substring(1);
 228  
     }
 229  
 
 230  0
     final int subDirIndex = normalized.indexOf('/');
 231  0
     if (subDirIndex != -1)
 232  
     {
 233  0
       normalized = normalized.substring(0, subDirIndex);
 234  
     }
 235  
 
 236  0
     return normalized;
 237  
   }
 238  
 
 239  
   // --- object basics --------------------------------------------------------
 240  
 
 241  
 }