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 */ 017package org.apache.xbean.finder.archive; 018 019import java.io.BufferedInputStream; 020import java.io.ByteArrayOutputStream; 021import java.io.File; 022import java.io.IOException; 023import java.io.InputStream; 024import java.net.URL; 025import java.util.ArrayList; 026import java.util.Iterator; 027import java.util.List; 028 029/** 030 * @version $Rev$ $Date$ 031 */ 032public class FileArchive implements Archive { 033 034 private final ClassLoader loader; 035 private final String basePackage; 036 private final File dir; 037 private List<String> list; 038 039 public FileArchive(ClassLoader loader, URL url) { 040 this.loader = loader; 041 this.basePackage = ""; 042 this.dir = toFile(url); 043 } 044 045 public FileArchive(ClassLoader loader, File dir) { 046 this.loader = loader; 047 this.basePackage = ""; 048 this.dir = dir; 049 } 050 051 public FileArchive(ClassLoader loader, URL url, String basePackage) { 052 this.loader = loader; 053 this.basePackage = basePackage; 054 this.dir = toFile(url); 055 } 056 057 public FileArchive(ClassLoader loader, File dir, String basePackage) { 058 this.loader = loader; 059 this.basePackage = basePackage; 060 this.dir = dir; 061 } 062 063 public File getDir() { 064 return dir; 065 } 066 067 public InputStream getBytecode(String className) throws IOException, ClassNotFoundException { 068 int pos = className.indexOf("<"); 069 if (pos > -1) { 070 className = className.substring(0, pos); 071 } 072 pos = className.indexOf(">"); 073 if (pos > -1) { 074 className = className.substring(0, pos); 075 } 076 if (!className.endsWith(".class")) { 077 className = className.replace('.', '/') + ".class"; 078 } 079 080 URL resource = loader.getResource(className); 081 if (resource != null) return new BufferedInputStream(resource.openStream()); 082 083 throw new ClassNotFoundException(className); 084 } 085 086 087 public Class<?> loadClass(String className) throws ClassNotFoundException { 088 return loader.loadClass(className); 089 } 090 091 public Iterator<Entry> iterator() { 092 return new ArchiveIterator(this, _iterator()); 093 } 094 095 public Iterator<String> _iterator() { 096 if (list != null) return list.iterator(); 097 098 list = file(dir); 099 return list.iterator(); 100 } 101 102 private List<String> file(File dir) { 103 List<String> classNames = new ArrayList<String>(); 104 if (dir.isDirectory()) { 105 scanDir(dir, classNames, (basePackage.length() > 0) ? (basePackage + ".") : basePackage); 106 } 107 return classNames; 108 } 109 110 private void scanDir(File dir, List<String> classNames, String packageName) { 111 File[] files = dir.listFiles(); 112 for (File file : files) { 113 if (file.isDirectory()) { 114 scanDir(file, classNames, packageName + file.getName() + "."); 115 } else if (file.getName().endsWith(".class")) { 116 String name = file.getName(); 117 name = name.replaceFirst(".class$", ""); 118 if (name.contains(".")) continue; 119 classNames.add(packageName + name); 120 } 121 } 122 } 123 124 private static File toFile(URL url) { 125 if (!"file".equals(url.getProtocol())) throw new IllegalArgumentException("not a file url: " + url); 126 String path = url.getFile(); 127 File dir = new File(decode(path)); 128 if (dir.getName().equals("META-INF")) { 129 dir = dir.getParentFile(); // Scrape "META-INF" off 130 } 131 return dir; 132 } 133 134 public static String decode(String fileName) { 135 if (fileName.indexOf('%') == -1) return fileName; 136 137 StringBuilder result = new StringBuilder(fileName.length()); 138 ByteArrayOutputStream out = new ByteArrayOutputStream(); 139 140 for (int i = 0; i < fileName.length();) { 141 char c = fileName.charAt(i); 142 143 if (c == '%') { 144 out.reset(); 145 do { 146 if (i + 2 >= fileName.length()) { 147 throw new IllegalArgumentException("Incomplete % sequence at: " + i); 148 } 149 150 int d1 = Character.digit(fileName.charAt(i + 1), 16); 151 int d2 = Character.digit(fileName.charAt(i + 2), 16); 152 153 if (d1 == -1 || d2 == -1) { 154 throw new IllegalArgumentException("Invalid % sequence (" + fileName.substring(i, i + 3) + ") at: " + String.valueOf(i)); 155 } 156 157 out.write((byte) ((d1 << 4) + d2)); 158 159 i += 3; 160 161 } while (i < fileName.length() && fileName.charAt(i) == '%'); 162 163 164 result.append(out.toString()); 165 166 continue; 167 } else { 168 result.append(c); 169 } 170 171 i++; 172 } 173 return result.toString(); 174 } 175}