001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. 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 */
017 package org.apache.xbean.finder.archive;
018
019 import java.io.ByteArrayOutputStream;
020 import java.io.File;
021 import java.io.IOException;
022 import java.io.InputStream;
023 import java.net.URL;
024 import java.util.ArrayList;
025 import java.util.Iterator;
026 import java.util.List;
027
028 /**
029 * @version $Rev$ $Date$
030 */
031 public class FileArchive implements Archive {
032
033 private final ClassLoader loader;
034 private final File dir;
035
036 public FileArchive(ClassLoader loader, URL url) {
037 this.loader = loader;
038 this.dir = toFile(url);
039 }
040
041 public FileArchive(ClassLoader loader, File dir) {
042 this.loader = loader;
043 this.dir = dir;
044 }
045
046 @Override
047 public InputStream getBytecode(String className) throws IOException, ClassNotFoundException {
048 int pos = className.indexOf("<");
049 if (pos > -1) {
050 className = className.substring(0, pos);
051 }
052 pos = className.indexOf(">");
053 if (pos > -1) {
054 className = className.substring(0, pos);
055 }
056 if (!className.endsWith(".class")) {
057 className = className.replace('.', '/') + ".class";
058 }
059
060 URL resource = loader.getResource(className);
061 if (resource != null) return resource.openStream();
062
063 throw new ClassNotFoundException(className);
064 }
065
066
067 @Override
068 public Class<?> loadClass(String className) throws ClassNotFoundException {
069 return loader.loadClass(className);
070 }
071
072 @Override
073 public Iterator<String> iterator() {
074 return file(dir).iterator();
075 }
076
077 private List<String> file(File dir) {
078 List<String> classNames = new ArrayList<String>();
079 if (dir.isDirectory()) {
080 scanDir(dir, classNames, "");
081 }
082 return classNames;
083 }
084
085 private void scanDir(File dir, List<String> classNames, String packageName) {
086 File[] files = dir.listFiles();
087 for (File file : files) {
088 if (file.isDirectory()) {
089 scanDir(file, classNames, packageName + file.getName() + ".");
090 } else if (file.getName().endsWith(".class")) {
091 String name = file.getName();
092 name = name.replaceFirst(".class$", "");
093 if (name.contains(".")) continue;
094 classNames.add(packageName + name);
095 }
096 }
097 }
098
099 private static File toFile(URL url) {
100 if (!"file".equals(url.getProtocol())) throw new IllegalArgumentException("not a file url: " + url);
101 String path = url.getFile();
102 File dir = new File(decode(path));
103 if (dir.getName().equals("META-INF")) {
104 dir = dir.getParentFile(); // Scrape "META-INF" off
105 }
106 return dir;
107 }
108
109 public static String decode(String fileName) {
110 if (fileName.indexOf('%') == -1) return fileName;
111
112 StringBuilder result = new StringBuilder(fileName.length());
113 ByteArrayOutputStream out = new ByteArrayOutputStream();
114
115 for (int i = 0; i < fileName.length();) {
116 char c = fileName.charAt(i);
117
118 if (c == '%') {
119 out.reset();
120 do {
121 if (i + 2 >= fileName.length()) {
122 throw new IllegalArgumentException("Incomplete % sequence at: " + i);
123 }
124
125 int d1 = Character.digit(fileName.charAt(i + 1), 16);
126 int d2 = Character.digit(fileName.charAt(i + 2), 16);
127
128 if (d1 == -1 || d2 == -1) {
129 throw new IllegalArgumentException("Invalid % sequence (" + fileName.substring(i, i + 3) + ") at: " + String.valueOf(i));
130 }
131
132 out.write((byte) ((d1 << 4) + d2));
133
134 i += 3;
135
136 } while (i < fileName.length() && fileName.charAt(i) == '%');
137
138
139 result.append(out.toString());
140
141 continue;
142 } else {
143 result.append(c);
144 }
145
146 i++;
147 }
148 return result.toString();
149 }
150 }