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}