Coverage Report - at.ipsquare.commons.core.util.LocalResources
 
Classes in this File Line Coverage Branch Coverage Complexity
LocalResources
90%
48/53
88%
23/26
3.111
 
 1  
 /**
 2  
  * Copyright (C) 2013 Matthias Langer
 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 at.ipsquare.commons.core.util;
 17  
 
 18  
 
 19  
 import java.io.File;
 20  
 import java.io.FileNotFoundException;
 21  
 import java.io.IOException;
 22  
 import java.io.InputStream;
 23  
 import java.net.URL;
 24  
 import java.net.URLClassLoader;
 25  
 import java.net.URLDecoder;
 26  
 import java.util.Collection;
 27  
 import java.util.HashSet;
 28  
 import java.util.List;
 29  
 import java.util.Set;
 30  
 import java.util.TreeSet;
 31  
 
 32  
 import org.apache.commons.collections.EnumerationUtils;
 33  
 
 34  
 /**
 35  
  * For loading local resources from the classpath.
 36  
  * 
 37  
  * @author Matthias Langer
 38  
  * @since 2.0.0
 39  
  */
 40  
 public final class LocalResources
 41  
 {
 42  
   /**
 43  
    * Returns a file object for the given path.
 44  
    * 
 45  
    * Please note that this method does not work for resources that lie within JAR files. Use either {@link #getStream(String)}
 46  
    * or {@link #getUrl(String)} for these.
 47  
    * 
 48  
    * @param path a path relative to the current classpath.
 49  
    * @return an appropriate file object.
 50  
    */
 51  
   public static File getFile(String path) throws IOException
 52  
   {
 53  56
     URL url = getUrl(path);
 54  42
     if("jar".equals(url.getProtocol()))
 55  6
         throw new IOException(url + " points to a file that lies within a JAR file. Please use getUrl() or getStream() instead.");
 56  
     
 57  36
     File file = new File(URLDecoder.decode(url.getFile(), "UTF-8"));
 58  36
     if(!file.canRead())
 59  0
       throw new IOException("Not readable: '" + file + "'.");
 60  36
     return file;
 61  
   }
 62  
   
 63  
   /**
 64  
    * Returns an {@link InputStream} for the given path.
 65  
    * 
 66  
    * @see #getFile(String)
 67  
    */
 68  
   public static InputStream getStream(String path) throws IOException
 69  
   {
 70  20
     return getUrl(path).openStream();
 71  
   }
 72  
   
 73  
   /**
 74  
    * See {@link #getFile(String)} and {@link #getStream(String)}.
 75  
    */
 76  
   public static URL getUrl(String path) throws IOException
 77  
   {
 78  80
     Set<ClassLoader> classLoaders = ClassLoaders.get();
 79  80
     Set<String> urls = new HashSet<String>(2);
 80  
     
 81  80
     for(ClassLoader cl : classLoaders)
 82  
     {
 83  92
       URL url = getUrl(path, cl);
 84  92
       if(url != null)
 85  72
         urls.add(url.toExternalForm());
 86  92
     }
 87  
     
 88  80
     if(urls.isEmpty())
 89  
     {
 90  16
       StringBuilder sb = new StringBuilder("'");
 91  16
       sb.append(path).append("'");
 92  
       
 93  16
       if(path.startsWith("/"))
 94  6
         sb.append("; try ommiting the leading '/'.");
 95  
       
 96  16
       sb.append("\n").append(classLoaderInfo(classLoaders));
 97  16
       throw new FileNotFoundException(sb.toString());
 98  
     }
 99  
     
 100  64
     if(urls.size() > 1)
 101  0
       throw new IOException("'" + path + "' : Ambiguous path.");
 102  
     
 103  64
     return new URL(urls.iterator().next());
 104  
   }
 105  
   
 106  
   private static String classLoaderInfo(Collection<ClassLoader> classLoaders)
 107  
   {
 108  16
     StringBuilder sb = new StringBuilder("ClassLoaders involved:");
 109  16
     for(ClassLoader cl : classLoaders)
 110  20
       sb.append("\n").append(classLoaderInfo(cl));
 111  16
     return sb.toString();
 112  
   }
 113  
   
 114  
   private static String classLoaderInfo(ClassLoader cl)
 115  
   {
 116  20
     StringBuilder sb = new StringBuilder("** ");
 117  
     
 118  20
     if(cl instanceof URLClassLoader)
 119  
     {
 120  16
       URLClassLoader urlCl = (URLClassLoader) cl;
 121  16
       sb.append("URLClassLoader -- ").append(collectClassLoaderUrls(urlCl));
 122  16
     }
 123  
     else
 124  4
       sb.append(cl);
 125  
     
 126  20
     return sb.toString();
 127  
   }
 128  
   
 129  
   private static URL getUrl(String path, ClassLoader cl) throws IOException
 130  
   {
 131  
     @SuppressWarnings("unchecked")
 132  92
     List<URL> urls = EnumerationUtils.toList(cl.getResources(path));
 133  
     
 134  92
     if(urls.isEmpty())
 135  20
       return null;
 136  72
     if(urls.size() > 1)
 137  0
       throw new IOException("'" + path + "' : Ambiguous path; URLs: " + urls);
 138  72
     return urls.get(0);
 139  
   }
 140  
   
 141  
   private static Set<String> collectClassLoaderUrls(URLClassLoader cl)
 142  
   {
 143  16
     Set<String> urls = new TreeSet<String>();
 144  16
     collectClassLoaderUrls(cl, urls);
 145  16
     return urls;
 146  
   }
 147  
   
 148  
   private static void collectClassLoaderUrls(URLClassLoader cl, Set<String> accumulator)
 149  
   {
 150  144
     for(URL url : cl.getURLs())
 151  112
       accumulator.add(url.toString());
 152  
     
 153  32
     ClassLoader parent = cl.getParent();
 154  32
     if(parent instanceof URLClassLoader)
 155  16
       collectClassLoaderUrls((URLClassLoader) parent, accumulator);
 156  32
   }
 157  
   
 158  
   private LocalResources()
 159  0
   {
 160  
     
 161  0
   }
 162  
 }