View Javadoc

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