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 }