1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/dew/src/main/java/org/apidesign/bck2brwsr/dew/ClassLoaderFileManager.java Wed Oct 02 21:00:24 2013 +0200
1.3 @@ -0,0 +1,331 @@
1.4 +/*
1.5 + * To change this license header, choose License Headers in Project Properties.
1.6 + * To change this template file, choose Tools | Templates
1.7 + * and open the template in the editor.
1.8 + */
1.9 +
1.10 +package org.apidesign.bck2brwsr.dew;
1.11 +
1.12 +import java.io.BufferedReader;
1.13 +import java.io.File;
1.14 +import java.io.IOException;
1.15 +import java.io.InputStreamReader;
1.16 +import java.util.ArrayList;
1.17 +import java.util.Collections;
1.18 +import java.util.EnumSet;
1.19 +import java.util.Enumeration;
1.20 +import java.util.HashMap;
1.21 +import java.util.Iterator;
1.22 +import java.util.List;
1.23 +import java.util.Map;
1.24 +import java.util.Set;
1.25 +import java.util.zip.ZipEntry;
1.26 +import java.util.zip.ZipFile;
1.27 +import javax.tools.FileObject;
1.28 +import javax.tools.JavaFileManager;
1.29 +import javax.tools.JavaFileObject;
1.30 +import javax.tools.StandardLocation;
1.31 +
1.32 +/**
1.33 + *
1.34 + * @author Tomas Zezula
1.35 + */
1.36 +public class ClassLoaderFileManager implements JavaFileManager {
1.37 +
1.38 + private static final Location[] READ_LOCATIONS = {
1.39 + StandardLocation.PLATFORM_CLASS_PATH,
1.40 + StandardLocation.CLASS_PATH,
1.41 + StandardLocation.SOURCE_PATH
1.42 + };
1.43 +
1.44 + private static final Location[] WRITE_LOCATIONS = {
1.45 + StandardLocation.CLASS_OUTPUT,
1.46 + StandardLocation.SOURCE_OUTPUT
1.47 + };
1.48 +
1.49 + private static final Location[] CLASS_LOADER_LOCATIONS = {
1.50 + StandardLocation.ANNOTATION_PROCESSOR_PATH
1.51 + };
1.52 +
1.53 + private Map<Location, Map<String,List<MemoryFileObject>>> generated;
1.54 +
1.55 +
1.56 + ClassLoaderFileManager() {
1.57 + generated = new HashMap<>();
1.58 + for (Location l : WRITE_LOCATIONS) {
1.59 + generated.put(l, new HashMap<String, List<MemoryFileObject>>());
1.60 + }
1.61 + }
1.62 +
1.63 +
1.64 + @Override
1.65 + public ClassLoader getClassLoader(Location location) {
1.66 + if (canClassLoad(location)) {
1.67 + return getClass().getClassLoader();
1.68 + } else {
1.69 + return null;
1.70 + }
1.71 + }
1.72 +
1.73 + @Override
1.74 + public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
1.75 + if (canRead(location)) {
1.76 + final List<JavaFileObject> res = new ArrayList<JavaFileObject>();
1.77 + for (String resource : getResources(convertFQNToResource(packageName))) {
1.78 + final JavaFileObject jfo = new ClassLoaderJavaFileObject(resource);
1.79 + if (kinds.contains(jfo.getKind())) {
1.80 + res.add(jfo);
1.81 + }
1.82 + }
1.83 + return res;
1.84 + } else if (canWrite(location)) {
1.85 + Map<String,List<MemoryFileObject>> folders = generated.get(location);
1.86 + List<MemoryFileObject> files = folders.get(convertFQNToResource(packageName));
1.87 + if (files != null) {
1.88 + final List<JavaFileObject> res = new ArrayList<JavaFileObject>();
1.89 + for (JavaFileObject file : files) {
1.90 + if (kinds.contains(file.getKind()) && file.getLastModified() >= 0) {
1.91 + res.add(file);
1.92 + }
1.93 + }
1.94 + return res;
1.95 + }
1.96 + }
1.97 + return Collections.<JavaFileObject>emptyList();
1.98 + }
1.99 +
1.100 + @Override
1.101 + public String inferBinaryName(Location location, JavaFileObject file) {
1.102 + return ((InferableJavaFileObject)file).infer();
1.103 + }
1.104 +
1.105 + @Override
1.106 + public boolean isSameFile(FileObject a, FileObject b) {
1.107 + return a.toUri().equals(b.toUri());
1.108 + }
1.109 +
1.110 + @Override
1.111 + public boolean handleOption(String current, Iterator<String> remaining) {
1.112 + return false;
1.113 + }
1.114 +
1.115 + @Override
1.116 + public boolean hasLocation(Location location) {
1.117 + for (Location l : StandardLocation.values()) {
1.118 + if (l.equals(location)) {
1.119 + return true;
1.120 + }
1.121 + }
1.122 + return false;
1.123 + }
1.124 +
1.125 + @Override
1.126 + public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException {
1.127 + if (canRead(location)) {
1.128 + return new ClassLoaderJavaFileObject(convertFQNToResource(className) + kind.extension);
1.129 + } else {
1.130 + throw new UnsupportedOperationException("Unsupported location for reading: " + location); //NOI18N
1.131 + }
1.132 + }
1.133 +
1.134 + @Override
1.135 + public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
1.136 + if (canWrite(location)) {
1.137 + final String resource = convertFQNToResource(className) + kind.extension;
1.138 + final MemoryFileObject res = new MemoryFileObject(resource, null);
1.139 + register(location, resource, res);
1.140 + return res;
1.141 + } else {
1.142 + throw new UnsupportedOperationException("Unsupported location for reading: " + location); //NOI18N
1.143 + }
1.144 + }
1.145 +
1.146 + @Override
1.147 + public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
1.148 + if (canRead(location)) {
1.149 + return new ClassLoaderJavaFileObject(convertFQNToResource(packageName) + '/' + relativeName); //NOI18N
1.150 + } else {
1.151 + throw new UnsupportedOperationException("Unsupported location for reading: " + location); //NOI18N
1.152 + }
1.153 + }
1.154 +
1.155 + @Override
1.156 + public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
1.157 + if (canWrite(location)) {
1.158 + final String resource = convertFQNToResource(packageName) + '/' + relativeName; //NOI18N
1.159 + final MemoryFileObject res = new MemoryFileObject(resource, null);
1.160 + register(location, resource, res);
1.161 + return res;
1.162 + } else {
1.163 + throw new UnsupportedOperationException("Unsupported location for reading: " + location); //NOI18N
1.164 + }
1.165 + }
1.166 +
1.167 + @Override
1.168 + public void flush() throws IOException {
1.169 + }
1.170 +
1.171 + @Override
1.172 + public void close() throws IOException {
1.173 + }
1.174 +
1.175 + @Override
1.176 + public int isSupportedOption(String option) {
1.177 + return -1;
1.178 + }
1.179 +
1.180 +// private List<String> getResources(String folder) throws IOException {
1.181 +// final List<String> result = new ArrayList<String>();
1.182 +// final BufferedReader in = new BufferedReader(new InputStreamReader(
1.183 +// this.getClass().getClassLoader().getResourceAsStream(String.format("%s/pkg-list", folder.substring(0))), //NOI18N
1.184 +// "UTF-8")); //NOI18N
1.185 +// try {
1.186 +// String line;
1.187 +// while ((line = in.readLine()) != null) {
1.188 +// result.add(line);
1.189 +// }
1.190 +// } finally {
1.191 +// in.close();
1.192 +// }
1.193 +// return result;
1.194 +// }
1.195 +
1.196 + //MOCK IMPL
1.197 + private List<String> getResources(String folder) throws IOException {
1.198 + if (classPathContent == null) {
1.199 + classPathContent = new HashMap<>();
1.200 +// final String boot = System.getProperty("sun.boot.class.path"); //NOI18N
1.201 + final String cp = System.getProperty("java.class.path");
1.202 + for (String entry : cp.split(File.pathSeparator)) {
1.203 + File f = new File (entry);
1.204 + if (f.canRead()) {
1.205 + if (f.isFile()) {
1.206 + ZipFile zf = new ZipFile(f);
1.207 + try {
1.208 + Enumeration<? extends ZipEntry> entries = zf.entries();
1.209 + while (entries.hasMoreElements()) {
1.210 + ZipEntry e = entries.nextElement();
1.211 + if (e.isDirectory()) {
1.212 + continue;
1.213 + }
1.214 + final String name = String.format("/%s",e.getName());
1.215 + final String owner = getOwner(name);
1.216 + List<String> content = classPathContent.get(owner);
1.217 + if (content == null) {
1.218 + content = new ArrayList<>();
1.219 + classPathContent.put(owner, content);
1.220 + }
1.221 + content.add(name);
1.222 + }
1.223 + } finally {
1.224 + zf.close();
1.225 + }
1.226 + } else if (f.isDirectory()) {
1.227 + addFiles(f,"/", classPathContent);
1.228 + }
1.229 + }
1.230 + }
1.231 + }
1.232 + List<String> content = classPathContent.get(folder);
1.233 + return content == null ? Collections.<String>emptyList() : content;
1.234 + }
1.235 +
1.236 + private void addFiles(File folder, String path, Map<String,List<String>> into) {
1.237 + for (File f : folder.listFiles()) {
1.238 + String fname = path + (path.length() == 1 ? "" : "/") + f.getName();
1.239 + if (f.isDirectory()) {
1.240 + addFiles(f, fname, into);
1.241 + } else {
1.242 + List<String> content = into.get(path);
1.243 + if (content == null) {
1.244 + content = new ArrayList<>();
1.245 + classPathContent.put(path, content);
1.246 + }
1.247 + content.add(fname);
1.248 + }
1.249 + }
1.250 + }
1.251 +
1.252 + private Map<String,List<String>> classPathContent;
1.253 +
1.254 + private void register(Location loc, String resource, MemoryFileObject jfo) {
1.255 + Map<String,List<MemoryFileObject>> folders = generated.get(loc);
1.256 + final String folder = getOwner(resource);
1.257 + List<MemoryFileObject> content = folders.get(folder);
1.258 + if (content == null) {
1.259 + content = new ArrayList<>();
1.260 + folders.put(folder, content);
1.261 + }
1.262 + content.add(jfo);
1.263 + }
1.264 +
1.265 + private static String getOwner(String resource) {
1.266 + int lastSlash = resource.lastIndexOf('/');
1.267 + assert lastSlash >= 0;
1.268 + return resource.substring(0, lastSlash);
1.269 + }
1.270 +
1.271 + private static boolean canRead(Location loc) {
1.272 + for (Location rl : READ_LOCATIONS) {
1.273 + if (rl.equals(loc)) {
1.274 + return true;
1.275 + }
1.276 + }
1.277 + return false;
1.278 + }
1.279 +
1.280 + private static boolean canWrite(Location loc) {
1.281 + for (Location wl : WRITE_LOCATIONS) {
1.282 + if (wl.equals(loc)) {
1.283 + return true;
1.284 + }
1.285 + }
1.286 + return false;
1.287 + }
1.288 +
1.289 + private static boolean canClassLoad(Location loc) {
1.290 + for (Location cll : CLASS_LOADER_LOCATIONS) {
1.291 + if (cll.equals(loc)) {
1.292 + return true;
1.293 + }
1.294 + }
1.295 + return false;
1.296 + }
1.297 +
1.298 + static String convertFQNToResource(String fqn) {
1.299 + return '/' + fqn.replace('.', '/'); //NOI18N
1.300 + }
1.301 +
1.302 + static String convertResourceToFQN(String resource) {
1.303 + assert resource.startsWith("/"); //NOI18N
1.304 + int lastSlash = resource.lastIndexOf('/'); //NOI18N
1.305 + int lastDot = resource.lastIndexOf('.'); //NOI18N
1.306 + int stop = lastSlash < lastDot ?
1.307 + lastDot :
1.308 + resource.length();
1.309 + return resource.substring(1, stop).replace('/', '.'); //NOI18N
1.310 + }
1.311 +
1.312 +
1.313 + JavaFileObject createMemoryFileObject (String resourceName, JavaFileObject.Kind kind, byte[] content) {
1.314 + final InferableJavaFileObject jfo = new MemoryFileObject(resourceName, kind, content);
1.315 + return jfo;
1.316 + }
1.317 +
1.318 + Iterable<? extends MemoryFileObject> getGeneratedFiles(JavaFileObject.Kind... kinds) {
1.319 + final Set<JavaFileObject.Kind> ks = EnumSet.noneOf(JavaFileObject.Kind.class);
1.320 + Collections.addAll(ks, kinds);
1.321 + final List<MemoryFileObject> res = new ArrayList<>();
1.322 + for (Map<String,List<MemoryFileObject>> folders : generated.values()) {
1.323 + for (List<MemoryFileObject> content : folders.values()) {
1.324 + for (MemoryFileObject fo : content) {
1.325 + if (ks.contains(fo.getKind()) && fo.getLastModified() >= 0) {
1.326 + res.add(fo);
1.327 + }
1.328 + }
1.329 + }
1.330 + }
1.331 + return res;
1.332 + }
1.333 +
1.334 +}