View Javadoc

1   /*
2    * Copyright 2007-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.maven.exceptions.util;
17  
18  import java.io.File;
19  
20  import org.apache.commons.io.FilenameUtils;
21  import org.apache.commons.lang.StringUtils;
22  import org.apache.commons.logging.Log;
23  import org.apache.tools.ant.DirectoryScanner;
24  
25  import de.smartics.exceptions.report.scan.ScannerFactory;
26  
27  /**
28   * Utilities to work on file names and files.
29   */
30  public final class FileNameUtils
31  {
32    // ********************************* Fields *********************************
33  
34    // --- constants ------------------------------------------------------------
35  
36    // --- members --------------------------------------------------------------
37  
38    // ****************************** Initializer *******************************
39  
40    // ****************************** Constructors ******************************
41  
42    /**
43     * Utility class pattern.
44     */
45    private FileNameUtils()
46    {
47    }
48  
49    // ****************************** Inner Classes *****************************
50  
51    // ********************************* Methods ********************************
52  
53    // --- init -----------------------------------------------------------------
54  
55    // --- get&set --------------------------------------------------------------
56  
57    // --- business -------------------------------------------------------------
58  
59    /**
60     * Checks if the given root refers to an archive or not. Simply the file
61     * extension is checked. Accessibility et al. is not considered.
62     *
63     * @param root the root to check.
64     * @return <code>true</code> if root refers to an archive file,
65     *         <code>false</code> otherwise.
66     * @throws NullPointerException if <code>root</code> is <code>null</code>.
67     */
68    public static boolean isArchive(final String root)
69      throws NullPointerException
70    {
71      return root.endsWith(".jar") || root.endsWith(".zip");
72    }
73  
74    /**
75     * Creates package names from directory files found by the given scanner.
76     *
77     * @param log the logger to log at debug level. No logging if <code>log</code>
78     *          is <code>null</code>.
79     * @param rootDir the root directory to create a scanner on.
80     * @param scannerFactory the factory to create the scanner with the given
81     *          <code>rootDir</code>.
82     * @return the package list. Package names are separated by a colon (
83     *         <code>:</code>) or the empty string if the root directory is not
84     *         accessible (will be logged at debug level if <code>log</code> is
85     *         not <code>null</code>.
86     * @throws NullPointerException if the <code>rootDir</code> or
87     *           <code>scannerFactory</code> is <code>null</code>.
88     */
89    public static String createPackageNames(final Log log, final String rootDir,
90        final ScannerFactory scannerFactory) throws NullPointerException
91    {
92      if (!isRootAccessible(rootDir))
93      {
94        if (log != null && log.isDebugEnabled())
95        {
96          log.debug("Root '" + rootDir + "' does not exist. Skipping...");
97        }
98        return "";
99      }
100 
101     final DirectoryScanner scanner = scannerFactory.createScanner(rootDir);
102     final String[] dirs = scanner.getIncludedDirectories();
103     final StringBuilder buffer = new StringBuilder();
104     for (String dir : dirs)
105     {
106       if (!containsJavaFiles(rootDir, dir))
107       {
108         continue;
109       }
110 
111       final String packageName = internalCreatePackageName(dir);
112       if (StringUtils.isNotBlank(packageName))
113       {
114         buffer.append(packageName).append(':');
115       }
116     }
117     final String packageNames = StringUtils.chop(buffer.toString());
118     return packageNames;
119   }
120 
121   /**
122    * Checks if the given directory contains files with the extension
123    * <code>java</code>.
124    *
125    * @param rootDir the path to the root directory the <code>dir</code> is
126    *          relative to.
127    * @param dir the directory within <code>rootDir</code> to check if it
128    *          contains Java files.
129    * @return <code>true</code> if the directory contains Java files (or is an
130    *         entry in an archive), <code>false</code> otherwise.
131    * @todo Currently, if <code>rootDir</code> points to an
132    *       {@link #isArchive(String) archive}, this method always returns
133    *       <code>true</code>.
134    */
135   private static boolean containsJavaFiles(final String rootDir,
136       final String dir)
137   {
138     if (isArchive(rootDir))
139     {
140       // TODO: We make life simple for the moment, assuming, that there are no
141       // empty packages in an archive file. The answer of this method may be
142       // plain wrong, but currently does no harm.
143       return true;
144     }
145     else
146     {
147       final File[] files = new File(rootDir, dir).listFiles();
148       if (files != null)
149       {
150         for (File file : files)
151         {
152           if (file.isFile()
153               && FilenameUtils.getExtension(file.getName()).equals("java"))
154           {
155             return true;
156           }
157         }
158       }
159       else
160       {
161         return true;
162       }
163     }
164     return false;
165   }
166 
167   /**
168    * Calls {@link #createPackageName(String)} and maps exceptions to
169    * <code>null</code>.
170    *
171    * @param dirName the name of the directory that contains classes of a
172    *          package. It is expected that the directory is relative to the
173    *          source root directory.
174    * @return the package name for the directory or <code>null</code> if the name
175    *         is not a valid package name.
176    */
177   private static String internalCreatePackageName(final String dirName)
178   {
179     try
180     {
181       return createPackageName(dirName);
182     }
183     catch (final Exception e)
184     {
185       return null;
186     }
187   }
188 
189   /**
190    * Creates the package name for the given directory.
191    *
192    * @param dirName the name of the directory that contains classes of a
193    *          package. It is expected that the directory is relative to the
194    *          source root directory.
195    * @return the package name for the directory.
196    * @throws NullPointerException if <code>dirName</code> is <code>null</code>.
197    * @throws IllegalArgumentException if the directory is not valid as a package
198    *           directory or if <code>dirName</code> is blank.
199    */
200   public static String createPackageName(final String dirName)
201     throws IllegalArgumentException, NullPointerException
202   {
203     if (dirName != null && StringUtils.isBlank(dirName))
204     {
205       // If dirName is null, the NPE will be thrown later.
206       throw new IllegalArgumentException("Directory name must not be blank.");
207     }
208     if (dirName.indexOf('.') != -1)
209     {
210       throw new IllegalArgumentException(
211           "Directory name must not contain dots.");
212     }
213 
214     final String normalizedPath = normalizePath(dirName);
215     if (!FilterUtils.isValidJavaPackageDirPath(normalizedPath))
216     {
217       throw new IllegalArgumentException("Invalid path for packages: "
218                                          + dirName);
219     }
220     final String packageName =
221         StringUtils.replaceChars(normalizedPath, "/\\", "..");
222     return packageName;
223   }
224 
225   /**
226    * Returns the normalized path where duplicate slashes are removed. If there
227    * is a slash (or backslash) in front, it is removed, too.
228    *
229    * @param path the path to normalize.
230    * @return the normalized path.
231    */
232   public static String normalizePath(final String path)
233   {
234     final File file = new File(path);
235     final String normalized = file.getPath();
236     if (normalized.length() > 0)
237     {
238       final char first = normalized.charAt(0);
239       if (first == '\\' || first == '/')
240       {
241         return normalized.substring(1);
242       }
243     }
244     return normalized;
245   }
246 
247   /**
248    * Checks if <code>root</code> points to a directory of file that is existing
249    * and readable.
250    *
251    * @param root the directory or file name to check.
252    * @return <code>true</code> if the directory of file exists,
253    *         <code>false</code> otherwise.
254    * @throws NullPointerException if the <code>root</code> argument is
255    *           <code>null</code>.
256    */
257   public static boolean isRootAccessible(final String root)
258     throws NullPointerException
259   {
260     final File file = new File(root);
261     return file.canRead();
262   }
263 
264   // --- object basics --------------------------------------------------------
265 
266 }