Coverage Report - de.smartics.util.lang.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;
 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  
 /**
 33  
  * Lists the content of a directory on the class path.
 34  
  * <p>
 35  
  * Currently only the protocols
 36  
  * </p>
 37  
  * <ol>
 38  
  * <li>file system (<code>file</code>) and</li>
 39  
  * <li>JAR files (<code>jar</code>)</li>
 40  
  * </ol>
 41  
  * <p>
 42  
  * are supported.
 43  
  * </p>
 44  
  */
 45  
 public class ClassPathDirectoryListing
 46  
 {
 47  
   // ********************************* Fields *********************************
 48  
 
 49  
   // --- constants ------------------------------------------------------------
 50  
 
 51  
   // --- members --------------------------------------------------------------
 52  
 
 53  
   /**
 54  
    * The context to load the directory listings.
 55  
    */
 56  
   private final ClassPathContext context;
 57  
 
 58  
   // ****************************** Initializer *******************************
 59  
 
 60  
   // ****************************** Constructors ******************************
 61  
 
 62  
   /**
 63  
    * Default constructor.
 64  
    *
 65  
    * @param classLoader the class loader to load the directory listings.
 66  
    * @throws NullPointerException if {@code classLoader} is <code>null</code>.
 67  
    */
 68  
   public ClassPathDirectoryListing(final ClassLoader classLoader)
 69  
     throws NullPointerException
 70  
   {
 71  0
     this(new ClassPathContext(classLoader, null));
 72  0
   }
 73  
 
 74  
   /**
 75  
    * Default constructor.
 76  
    *
 77  
    * @param context the context to load the directory listings.
 78  
    * @throws NullPointerException if {@code classLoader} is <code>null</code>.
 79  
    */
 80  
   public ClassPathDirectoryListing(final ClassPathContext context)
 81  
     throws NullPointerException
 82  0
   {
 83  0
     Arg.checkNotNull("context", context);
 84  0
     this.context = context;
 85  0
   }
 86  
 
 87  
   // ****************************** Inner Classes *****************************
 88  
 
 89  
   // ********************************* Methods ********************************
 90  
 
 91  
   // --- init -----------------------------------------------------------------
 92  
 
 93  
   // --- get&set --------------------------------------------------------------
 94  
 
 95  
   /**
 96  
    * Returns the context to load the directory listings.
 97  
    *
 98  
    * @return the context to load the directory listings.
 99  
    */
 100  
   public final ClassPathContext getClassPathContext()
 101  
   {
 102  0
     return context;
 103  
   }
 104  
 
 105  
   // --- business -------------------------------------------------------------
 106  
 
 107  
   /**
 108  
    * Lists the contents of the resource path.
 109  
    *
 110  
    * @param resourcePath the path to the resource whose contents is to be
 111  
    *          listed. The empty string returns the contents of the class
 112  
    *          loader's root directory (which is usually the parent class
 113  
    *          loader's root).
 114  
    * @return the contents of the resource as names. The list may be empty, but
 115  
    *         is never <code>null</code>.
 116  
    * @throws NullPointerException if {@code resourcePath} is <code>null</code>.
 117  
    * @throws IllegalArgumentException if resource path cannot be resolved to
 118  
    *           determine the contents. Either the protocol is unknown or there
 119  
    *           is a problem to access the resource's content physically.
 120  
    * @impl Sub classes may override this method to add additional protocol
 121  
    *       handlers. Call this method, if the derived handler does not handle
 122  
    *       the protocol.
 123  
    */
 124  
   public List<String> list(final String resourcePath)
 125  
     throws NullPointerException, IllegalArgumentException
 126  
   {
 127  0
     Arg.checkNotNull("resourcePath", resourcePath);
 128  
 
 129  0
     final URL resourcePathUrl = context.getResource(resourcePath);
 130  0
     if (resourcePathUrl == null)
 131  
     {
 132  0
       throw new IllegalArgumentException("Cannot find resource '"
 133  
                                          + resourcePath + "' on class path.");
 134  
     }
 135  
 
 136  0
     final String protocol = resourcePathUrl.getProtocol();
 137  0
     if ("file".equals(protocol))
 138  
     {
 139  0
       return handleFile(resourcePath, resourcePathUrl);
 140  
     }
 141  0
     else if ("jar".equals(protocol))
 142  
     {
 143  0
       return handleJar(resourcePath, resourcePathUrl);
 144  
     }
 145  
 
 146  0
     throw new IllegalArgumentException(
 147  
         "Protocol '" + protocol + "' is not supported to resolve resource '"
 148  
             + resourcePath + "'.");
 149  
   }
 150  
 
 151  
   private List<String> handleFile(final String resourcePath,
 152  
       final URL resourcePathUrl) throws IllegalArgumentException
 153  
   {
 154  
     try
 155  
     {
 156  0
       final String[] contentsArray = new File(resourcePathUrl.toURI()).list();
 157  0
       return Arrays.asList(contentsArray);
 158  
     }
 159  0
     catch (final URISyntaxException e)
 160  
     {
 161  0
       throw new IllegalArgumentException(
 162  
           "Cannot read URL derived from resource '" + resourcePath
 163  
               + "' on the class path.", e);
 164  
     }
 165  
   }
 166  
 
 167  
   private List<String> handleJar(final String resourcePath,
 168  
       final URL resourcePathUrl) throws IllegalArgumentException
 169  
   {
 170  
     try
 171  
     {
 172  0
       final List<String> contents = new ArrayList<String>();
 173  
 
 174  0
       final int separatorIndex = resourcePathUrl.getPath().indexOf('!');
 175  0
       final String jarPath =
 176  
           resourcePathUrl.getPath().substring(5, separatorIndex);
 177  0
       final JarFile jar = new JarFile(URLDecoder.decode(jarPath, "UTF-8"));
 178  0
       traverseJarEntries(resourcePath, contents, jar);
 179  
 
 180  0
       return contents;
 181  
     }
 182  0
     catch (final IOException e)
 183  
     {
 184  0
       throw new IllegalArgumentException("Read from JAR '" + resourcePath
 185  
                                          + "'.", e);
 186  
     }
 187  
   }
 188  
 
 189  
   private void traverseJarEntries(final String resourcePath,
 190  
       final List<String> contents, final JarFile jar)
 191  
   {
 192  0
     final int resourcePathLength = resourcePath.length();
 193  
 
 194  0
     final Enumeration<JarEntry> entries = jar.entries();
 195  0
     while (entries.hasMoreElements())
 196  
     {
 197  0
       final String name = entries.nextElement().getName();
 198  0
       if (name.startsWith(resourcePath))
 199  
       {
 200  0
         final String entry = name.substring(resourcePathLength);
 201  0
         final String normalized = normalize(entry);
 202  
 
 203  0
         if (normalized != null && !contents.contains(normalized))
 204  
         {
 205  0
           contents.add(normalized);
 206  
         }
 207  
       }
 208  0
     }
 209  0
   }
 210  
 
 211  
   private static String normalize(final String entry)
 212  
   {
 213  0
     if (StringUtils.isBlank(entry))
 214  
     {
 215  0
       return null;
 216  
     }
 217  
 
 218  0
     String normalized = entry;
 219  0
     if (normalized.charAt(0) == '/')
 220  
     {
 221  0
       if (entry.length() == 1)
 222  
       {
 223  0
         return null;
 224  
       }
 225  0
       normalized = normalized.substring(1);
 226  
     }
 227  
 
 228  0
     final int subDirIndex = normalized.indexOf('/');
 229  0
     if (subDirIndex != -1)
 230  
     {
 231  0
       normalized = normalized.substring(0, subDirIndex);
 232  
     }
 233  
 
 234  0
     return normalized;
 235  
   }
 236  
 
 237  
   // --- object basics --------------------------------------------------------
 238  
 
 239  
 }