001/*-------------------------------------------------------------------------+ 002| | 003| Copyright 2005-2011 The ConQAT Project | 004| | 005| Licensed under the Apache License, Version 2.0 (the "License"); | 006| you may not use this file except in compliance with the License. | 007| You may obtain a copy of the License at | 008| | 009| http://www.apache.org/licenses/LICENSE-2.0 | 010| | 011| Unless required by applicable law or agreed to in writing, software | 012| distributed under the License is distributed on an "AS IS" BASIS, | 013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 014| See the License for the specific language governing permissions and | 015| limitations under the License. | 016+-------------------------------------------------------------------------*/ 017package org.conqat.lib.commons.filesystem; 018 019import java.io.File; 020import java.io.IOException; 021import java.net.URL; 022import java.net.URLDecoder; 023import java.util.ArrayList; 024import java.util.LinkedHashSet; 025import java.util.List; 026 027import org.conqat.lib.commons.string.StringUtils; 028 029/** 030 * Utility class for dealing with class paths. 031 */ 032public class ClassPathUtils { 033 034 /** Suffix for class files. */ 035 public static final String CLASS_FILE_SUFFIX = ".class"; 036 037 /** 038 * This method calls {@link #createClassPath(IURLResolver, Class)} for each 039 * class and concatenates them with {@link File#pathSeparator}. 040 */ 041 public static String createClassPathAsString(IURLResolver resolver, Class<?>... anchorClasses) throws IOException { 042 return StringUtils.concat(createClassPathAsSet(resolver, anchorClasses), File.pathSeparator); 043 } 044 045 /** 046 * This method calls {@link #createClassPath(IURLResolver, Class)} for each 047 * class and returns the classpath as String array. 048 */ 049 public static String[] createClassPathAsArray(IURLResolver resolver, Class<?>... anchorClasses) throws IOException { 050 return createClassPathAsSet(resolver, anchorClasses).toArray(new String[0]); 051 } 052 053 /** 054 * This method calls {@link #createClassPath(IURLResolver, Class)} for each 055 * class and returns the classpath as ordered set. 056 * 057 * @param resolver 058 * an optional resolver (may be null) that can be used to map 059 * special URLs (for example in the Eclipse context). 060 */ 061 public static LinkedHashSet<String> createClassPathAsSet(IURLResolver resolver, Class<?>... anchorClasses) 062 throws IOException { 063 LinkedHashSet<String> classPath = new LinkedHashSet<String>(); 064 for (Class<?> clazz : anchorClasses) { 065 classPath.add(createClassPath(resolver, clazz)); 066 } 067 return classPath; 068 } 069 070 /** 071 * Create class path for an anchor class. This method checks were the anchor 072 * class resides, i.e. in a jar file or directory and returns it. 073 * 074 * @param resolver 075 * an optional resolver (may be null) that can be used to map 076 * special URLs (for example in the Eclipse context). 077 */ 078 public static String createClassPath(IURLResolver resolver, Class<?> anchorClass) throws IOException { 079 // this should be a save way to obtain the location 080 URL url = obtainClassFileURL(anchorClass); 081 082 if (url == null) { 083 throw new AssertionError("Internal assumption violated."); 084 } 085 086 // resolve URL if resolver was provided 087 if (resolver != null) { 088 url = resolver.resolve(url); 089 } 090 091 String protocol = url.getProtocol(); 092 093 if ("file".equals(protocol)) { 094 return createFileClasspath(url, anchorClass); 095 } 096 097 if ("jar".equals(protocol)) { 098 return FileSystemUtils.extractJarFileFromJarURL(url).getCanonicalPath(); 099 } 100 101 // If this resides somewhere on the net, we have a problem. 102 throw new IOException("Unsupported protocol: " + protocol); 103 } 104 105 /** 106 * Returns the URL for the .class-file of a given class. For inner classes, 107 * the URL of the surrounding class file is returned. 108 */ 109 public static URL obtainClassFileURL(Class<?> clazz) { 110 String name = clazz.getSimpleName(); 111 112 // deal with inner classes 113 while (clazz.getEnclosingClass() != null) { 114 clazz = clazz.getEnclosingClass(); 115 name = clazz.getSimpleName() + "$" + name; 116 } 117 118 return clazz.getResource(name + ".class"); 119 } 120 121 /** Converts file URL to normal file location. */ 122 private static String createFileClasspath(URL url, Class<?> clazz) throws IOException { 123 String path = URLDecoder.decode(url.getPath(), FileSystemUtils.UTF8_ENCODING); 124 125 // strip class name and '.class' 126 path = path.substring(0, path.length() - 6 - clazz.getName().length()); 127 return new File(path).getCanonicalPath(); 128 } 129 130 /** Interface for performing URL resolving. */ 131 public static interface IURLResolver { 132 133 /** Resolves the URL and returns the result. */ 134 URL resolve(URL url) throws IOException; 135 } 136 137 /** 138 * Returns the fully qualified name of all java classes within the given 139 * directory and subdirectories. 140 */ 141 public static List<String> getClassNames(File directory) { 142 List<String> classNames = new ArrayList<>(); 143 144 List<File> classFiles = FileSystemUtils.listFilesRecursively(directory, new PlainClassFileFilter()); 145 146 String prefix = directory.getAbsolutePath() + File.separator; 147 for (File classFile : classFiles) { 148 String path = classFile.getAbsolutePath(); 149 path = StringUtils.stripPrefix(path, prefix); 150 path = StringUtils.stripSuffix(path, ClassPathUtils.CLASS_FILE_SUFFIX); 151 classNames.add(path.replace(File.separator, ".")); 152 } 153 return classNames; 154 } 155}