001    /*
002     * Copyright (C) 2012 eXo Platform SAS.
003     *
004     * This is free software; you can redistribute it and/or modify it
005     * under the terms of the GNU Lesser General Public License as
006     * published by the Free Software Foundation; either version 2.1 of
007     * the License, or (at your option) any later version.
008     *
009     * This software is distributed in the hope that it will be useful,
010     * but WITHOUT ANY WARRANTY; without even the implied warranty of
011     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012     * Lesser General Public License for more details.
013     *
014     * You should have received a copy of the GNU Lesser General Public
015     * License along with this software; if not, write to the Free
016     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018     */
019    
020    package org.crsh.vfs.spi.url;
021    
022    import org.crsh.util.Safe;
023    
024    import java.io.FileInputStream;
025    import java.io.IOException;
026    import java.io.InputStream;
027    import java.net.URISyntaxException;
028    import java.net.URL;
029    import java.util.ArrayList;
030    import java.util.Collections;
031    import java.util.Enumeration;
032    import java.util.HashMap;
033    import java.util.LinkedList;
034    import java.util.zip.ZipEntry;
035    
036    /** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
037    public class Node {
038    
039      /** . */
040      final String name;
041    
042      /** . */
043      HashMap<String, Node> children = new HashMap<String, Node>();
044    
045      /** . */
046      LinkedList<File> files = new LinkedList<File>();
047    
048      public Node() {
049        this.name = "";
050      }
051    
052      private Node(String name) {
053        this.name = name;
054      }
055    
056      void merge(ClassLoader loader) throws IOException, URISyntaxException {
057    
058        // Get the root class path files
059        for (Enumeration<URL> i = loader.getResources("");i.hasMoreElements();) {
060          URL url = i.nextElement();
061          merge(url);
062        }
063        ArrayList<URL> items = Collections.list(loader.getResources("META-INF/MANIFEST.MF"));
064        for (URL item : items) {
065          if ("jar".equals(item.getProtocol())) {
066            String path = item.getPath();
067            int pos = path.indexOf("!/");
068            URL url = new URL(path.substring(0, pos));
069            merge(url);
070          } else {
071            //
072          }
073        }
074      }
075    
076      void merge(URL url) throws IOException, URISyntaxException {
077        if (url.getProtocol().equals("file")) {
078          try {
079            java.io.File f = new java.io.File(url.toURI());
080            if (f.isDirectory()) {
081              merge(f);
082            }
083            else if (f.getName().endsWith(".jar")) {
084              merge(new URL("jar:" + url + "!/"));
085            } else {
086              // WTF ?
087            }
088          }
089          catch (URISyntaxException e) {
090            throw new IOException(e);
091          }
092        } else if (url.getProtocol().equals("jar")) {
093          int pos = url.getPath().lastIndexOf("!/");
094          URL jarURL = new URL(url.getPath().substring(0, pos));
095          String path = url.getPath().substring(pos + 2);
096          ZipIterator i = ZipIterator.create(jarURL);
097          try {
098            while (i.hasNext()) {
099              ZipEntry entry = i.next();
100              if (entry.getName().startsWith(path)) {
101                add(url, entry.getName().substring(path.length()), i.open());
102              }
103            }
104          }
105          finally {
106            Safe.close(i);
107          }
108        } else {
109          if (url.getPath().endsWith(".jar")) {
110            merge(new URL("jar:" + url + "!/"));
111          } else {
112            // WTF ?
113          }
114        }
115      }
116    
117      private void merge(java.io.File f) throws IOException {
118        java.io.File[] files = f.listFiles();
119        if (files != null) {
120          for (final java.io.File file : files) {
121            String name = file.getName();
122            Node child = children.get(name);
123            if (file.isDirectory()) {
124              if (child == null) {
125                Node dir = new Node(name);
126                dir.merge(file);
127                children.put(name, dir);
128              } else {
129                child.merge(file);
130              }
131            }
132            else {
133              if (child == null) {
134                children.put(name, child = new Node(name));
135              }
136              child.files.add(new File(new InputStreamResolver() {
137                public InputStream open() throws IOException {
138                  return new FileInputStream(file);
139                }
140              }, file.lastModified()));
141            }
142          }
143        }
144      }
145    
146      private void add(URL baseURL, String entryName, InputStreamResolver resolver) throws IOException {
147        if (entryName.length() > 0 && entryName.charAt(entryName.length() - 1) != '/') {
148          add(baseURL, 0, entryName, 1, resolver);
149        }
150      }
151    
152      private void add(URL baseURL, int index, String entryName, long lastModified, InputStreamResolver resolver) throws IOException {
153        int next = entryName.indexOf('/', index);
154        if (next == -1) {
155          String name = entryName.substring(index);
156          Node child = children.get(name);
157          if (child == null) {
158            children.put(name, child = new Node(name));
159          }
160          child.files.add(new File(resolver, lastModified));
161        } else {
162          String name = entryName.substring(index, next);
163          Node child = children.get(name);
164          if (child == null) {
165            children.put(name, child = new Node(name));
166          }
167          child.add(baseURL, next + 1, entryName, lastModified, resolver);
168        }
169      }
170    
171      static class File {
172    
173        /** . */
174        final InputStreamResolver resolver;
175    
176        /** . */
177        final long lastModified;
178    
179        File(InputStreamResolver url, long lastModified) {
180          this.resolver = url;
181          this.lastModified = lastModified;
182        }
183      }
184    }