dew/src/main/java/org/apidesign/bck2brwsr/dew/ClassLoaderFileManager.java
author tzezula
Wed, 02 Oct 2013 21:00:24 +0200
changeset 1324 263482b074e9
child 1325 f7f25ea1bbec
permissions -rw-r--r--
ClassLoaderFileManager
     1 /*
     2  * To change this license header, choose License Headers in Project Properties.
     3  * To change this template file, choose Tools | Templates
     4  * and open the template in the editor.
     5  */
     6 
     7 package org.apidesign.bck2brwsr.dew;
     8 
     9 import java.io.BufferedReader;
    10 import java.io.File;
    11 import java.io.IOException;
    12 import java.io.InputStreamReader;
    13 import java.util.ArrayList;
    14 import java.util.Collections;
    15 import java.util.EnumSet;
    16 import java.util.Enumeration;
    17 import java.util.HashMap;
    18 import java.util.Iterator;
    19 import java.util.List;
    20 import java.util.Map;
    21 import java.util.Set;
    22 import java.util.zip.ZipEntry;
    23 import java.util.zip.ZipFile;
    24 import javax.tools.FileObject;
    25 import javax.tools.JavaFileManager;
    26 import javax.tools.JavaFileObject;
    27 import javax.tools.StandardLocation;
    28 
    29 /**
    30  *
    31  * @author Tomas Zezula
    32  */
    33 public class ClassLoaderFileManager implements JavaFileManager {
    34 
    35     private static final Location[] READ_LOCATIONS = {
    36         StandardLocation.PLATFORM_CLASS_PATH,
    37         StandardLocation.CLASS_PATH,
    38         StandardLocation.SOURCE_PATH
    39     };
    40 
    41     private static final Location[] WRITE_LOCATIONS = {
    42         StandardLocation.CLASS_OUTPUT,
    43         StandardLocation.SOURCE_OUTPUT
    44     };
    45 
    46     private static final Location[] CLASS_LOADER_LOCATIONS = {
    47         StandardLocation.ANNOTATION_PROCESSOR_PATH
    48     };
    49 
    50     private Map<Location, Map<String,List<MemoryFileObject>>> generated;
    51 
    52 
    53     ClassLoaderFileManager() {
    54         generated = new HashMap<>();
    55         for (Location l : WRITE_LOCATIONS) {
    56             generated.put(l, new HashMap<String, List<MemoryFileObject>>());
    57         }
    58     }
    59 
    60 
    61     @Override
    62     public ClassLoader getClassLoader(Location location) {
    63         if (canClassLoad(location)) {
    64             return getClass().getClassLoader();
    65         } else {
    66             return null;
    67         }
    68     }
    69 
    70     @Override
    71     public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
    72         if (canRead(location)) {
    73             final List<JavaFileObject> res = new ArrayList<JavaFileObject>();
    74             for (String resource : getResources(convertFQNToResource(packageName))) {
    75                 final JavaFileObject jfo = new ClassLoaderJavaFileObject(resource);
    76                 if (kinds.contains(jfo.getKind())) {
    77                     res.add(jfo);
    78                 }
    79             }
    80             return res;
    81         } else if (canWrite(location)) {
    82             Map<String,List<MemoryFileObject>> folders = generated.get(location);
    83             List<MemoryFileObject> files = folders.get(convertFQNToResource(packageName));
    84             if (files != null) {
    85                 final List<JavaFileObject> res = new ArrayList<JavaFileObject>();
    86                 for (JavaFileObject file : files) {
    87                     if (kinds.contains(file.getKind()) && file.getLastModified() >= 0) {
    88                         res.add(file);
    89                     }
    90                 }
    91                 return res;
    92             }
    93         }
    94         return Collections.<JavaFileObject>emptyList();
    95     }
    96 
    97     @Override
    98     public String inferBinaryName(Location location, JavaFileObject file) {
    99         return ((InferableJavaFileObject)file).infer();
   100     }
   101 
   102     @Override
   103     public boolean isSameFile(FileObject a, FileObject b) {
   104         return a.toUri().equals(b.toUri());
   105     }
   106 
   107     @Override
   108     public boolean handleOption(String current, Iterator<String> remaining) {
   109         return false;
   110     }
   111 
   112     @Override
   113     public boolean hasLocation(Location location) {
   114         for (Location l : StandardLocation.values()) {
   115             if (l.equals(location)) {
   116                 return true;
   117             }
   118         }
   119         return false;
   120     }
   121 
   122     @Override
   123     public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException {
   124         if (canRead(location)) {
   125             return new ClassLoaderJavaFileObject(convertFQNToResource(className) + kind.extension);
   126         } else {
   127             throw new UnsupportedOperationException("Unsupported location for reading: " + location);   //NOI18N
   128         }
   129     }
   130 
   131     @Override
   132     public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
   133         if (canWrite(location)) {
   134             final String resource = convertFQNToResource(className) + kind.extension;
   135             final MemoryFileObject res = new MemoryFileObject(resource, null);
   136             register(location, resource, res);
   137             return res;
   138         } else {
   139             throw new UnsupportedOperationException("Unsupported location for reading: " + location);   //NOI18N
   140         }
   141     }
   142 
   143     @Override
   144     public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
   145         if (canRead(location)) {
   146             return new ClassLoaderJavaFileObject(convertFQNToResource(packageName) + '/' + relativeName); //NOI18N
   147         } else {
   148             throw new UnsupportedOperationException("Unsupported location for reading: " + location);   //NOI18N
   149         }
   150     }
   151 
   152     @Override
   153     public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
   154         if (canWrite(location)) {
   155             final String resource = convertFQNToResource(packageName) + '/' + relativeName; //NOI18N
   156             final MemoryFileObject res = new MemoryFileObject(resource, null);
   157             register(location, resource, res);
   158             return res;
   159         } else {
   160             throw new UnsupportedOperationException("Unsupported location for reading: " + location);   //NOI18N
   161         }
   162     }
   163 
   164     @Override
   165     public void flush() throws IOException {
   166     }
   167 
   168     @Override
   169     public void close() throws IOException {        
   170     }
   171 
   172     @Override
   173     public int isSupportedOption(String option) {
   174         return -1;
   175     }
   176 
   177 //    private List<String> getResources(String folder) throws IOException {
   178 //        final List<String> result = new ArrayList<String>();
   179 //        final BufferedReader in = new BufferedReader(new InputStreamReader(
   180 //                this.getClass().getClassLoader().getResourceAsStream(String.format("%s/pkg-list", folder.substring(0))),    //NOI18N
   181 //                "UTF-8"));  //NOI18N
   182 //        try {
   183 //            String line;
   184 //            while ((line = in.readLine()) != null) {
   185 //                result.add(line);
   186 //            }
   187 //        } finally {
   188 //            in.close();
   189 //        }
   190 //        return result;
   191 //    }
   192 
   193     //MOCK IMPL
   194     private List<String> getResources(String folder) throws IOException {
   195         if (classPathContent == null) {
   196             classPathContent = new HashMap<>();
   197 //            final String boot = System.getProperty("sun.boot.class.path");  //NOI18N
   198             final String cp = System.getProperty("java.class.path");
   199             for (String entry : cp.split(File.pathSeparator)) {
   200                 File f = new File (entry);
   201                 if (f.canRead()) {
   202                     if (f.isFile()) {
   203                         ZipFile zf = new ZipFile(f);
   204                         try {
   205                             Enumeration<? extends ZipEntry> entries = zf.entries();
   206                             while (entries.hasMoreElements()) {
   207                                 ZipEntry e = entries.nextElement();
   208                                 if (e.isDirectory()) {
   209                                     continue;
   210                                 }
   211                                 final String name = String.format("/%s",e.getName());
   212                                 final String owner = getOwner(name);
   213                                 List<String> content = classPathContent.get(owner);
   214                                 if (content == null) {
   215                                     content = new ArrayList<>();
   216                                     classPathContent.put(owner, content);
   217                                 }
   218                                 content.add(name);
   219                             }
   220                         } finally {
   221                             zf.close();
   222                         }
   223                     } else if (f.isDirectory()) {
   224                         addFiles(f,"/", classPathContent);
   225                     }
   226                 }
   227             }                                    
   228         }
   229         List<String> content = classPathContent.get(folder);
   230         return content == null ? Collections.<String>emptyList() : content;
   231     }
   232 
   233     private void addFiles(File folder, String path, Map<String,List<String>> into) {
   234         for (File f : folder.listFiles()) {
   235             String fname = path + (path.length() == 1 ? "" : "/") +  f.getName();
   236             if (f.isDirectory()) {
   237                 addFiles(f, fname, into);
   238             } else {
   239                 List<String> content = into.get(path);
   240                 if (content == null) {
   241                     content = new ArrayList<>();
   242                     classPathContent.put(path, content);
   243                 }
   244                 content.add(fname);
   245             }
   246         }
   247     }
   248     
   249     private Map<String,List<String>> classPathContent;
   250 
   251     private void register(Location loc, String resource, MemoryFileObject jfo) {
   252         Map<String,List<MemoryFileObject>> folders = generated.get(loc);
   253         final String folder = getOwner(resource);
   254         List<MemoryFileObject> content = folders.get(folder);
   255         if (content == null) {
   256             content = new ArrayList<>();
   257             folders.put(folder, content);
   258         }
   259         content.add(jfo);
   260     }
   261 
   262     private static String getOwner(String resource) {
   263         int lastSlash = resource.lastIndexOf('/');
   264         assert lastSlash >= 0;
   265         return resource.substring(0, lastSlash);
   266     }
   267 
   268     private static boolean canRead(Location loc) {
   269         for (Location rl : READ_LOCATIONS) {
   270             if (rl.equals(loc)) {
   271                 return true;
   272             }
   273         }
   274         return false;
   275     }
   276 
   277     private static boolean canWrite(Location loc) {
   278         for (Location wl : WRITE_LOCATIONS) {
   279             if (wl.equals(loc)) {
   280                 return true;
   281             }
   282         }
   283         return false;
   284     }
   285 
   286     private static boolean canClassLoad(Location loc) {
   287         for (Location cll : CLASS_LOADER_LOCATIONS) {
   288             if (cll.equals(loc)) {
   289                 return true;
   290             }
   291         }
   292         return false;
   293     }
   294 
   295     static String convertFQNToResource(String fqn) {
   296         return '/' + fqn.replace('.', '/');   //NOI18N
   297     }
   298 
   299     static String convertResourceToFQN(String resource) {
   300         assert resource.startsWith("/");    //NOI18N
   301         int lastSlash = resource.lastIndexOf('/');  //NOI18N
   302         int lastDot = resource.lastIndexOf('.');    //NOI18N
   303         int stop = lastSlash < lastDot ?
   304             lastDot :
   305             resource.length();
   306         return resource.substring(1, stop).replace('/', '.');    //NOI18N
   307     }
   308 
   309 
   310     JavaFileObject createMemoryFileObject (String resourceName, JavaFileObject.Kind kind, byte[] content) {
   311         final InferableJavaFileObject jfo  = new MemoryFileObject(resourceName, kind, content);
   312         return jfo;
   313     }
   314 
   315     Iterable<? extends MemoryFileObject> getGeneratedFiles(JavaFileObject.Kind... kinds) {
   316         final Set<JavaFileObject.Kind> ks = EnumSet.noneOf(JavaFileObject.Kind.class);
   317         Collections.addAll(ks, kinds);
   318         final List<MemoryFileObject> res = new ArrayList<>();
   319         for (Map<String,List<MemoryFileObject>> folders : generated.values()) {
   320             for (List<MemoryFileObject> content : folders.values()) {
   321                 for (MemoryFileObject fo : content) {
   322                     if (ks.contains(fo.getKind()) && fo.getLastModified() >= 0) {
   323                         res.add(fo);
   324                     }
   325                 }
   326             }
   327         }
   328         return res;
   329     }
   330 
   331 }