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.zip.ZipEntry;
034    
035    /** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
036    public abstract class Node {
037    
038      /** . */
039      final String name;
040    
041      protected Node(String name) {
042        this.name = name;
043      }
044    
045      static class Dir extends Node {
046    
047        /** . */
048        HashMap<String, Node> children = new HashMap<String, Node>();
049    
050        Dir() {
051          this("");
052        }
053    
054        private Dir(String name) {
055          super(name);
056        }
057    
058        void merge(ClassLoader loader) throws IOException, URISyntaxException {
059    
060          // Get the root class path files
061          for (Enumeration<URL> i = loader.getResources("");i.hasMoreElements();) {
062            URL url = i.nextElement();
063            merge(url);
064          }
065          ArrayList<URL> items = Collections.list(loader.getResources("META-INF/MANIFEST.MF"));
066          for (URL item : items) {
067            if ("jar".equals(item.getProtocol())) {
068              String path = item.getPath();
069              int pos = path.indexOf("!/");
070              URL url = new URL(path.substring(0, pos));
071              merge(url);
072            } else {
073              //
074            }
075          }
076        }
077    
078        void merge(URL url) throws IOException, URISyntaxException {
079          if (url.getProtocol().equals("file")) {
080            try {
081              java.io.File f = new java.io.File(url.toURI());
082              if (f.isDirectory()) {
083                merge(f);
084              }
085              else if (f.getName().endsWith(".jar")) {
086                merge(new URL("jar:" + url + "!/"));
087              } else {
088                // WTF ?
089              }
090            }
091            catch (URISyntaxException e) {
092              throw new IOException(e);
093            }
094          } if (url.getProtocol().equals("jar")) {
095            int pos = url.getPath().lastIndexOf("!/");
096            URL jarURL = new URL(url.getPath().substring(0, pos));
097            String path = url.getPath().substring(pos + 2);
098            ZipIterator i = ZipIterator.create(jarURL);
099            try {
100              while (i.hasNext()) {
101                ZipEntry entry = i.next();
102                if (entry.getName().startsWith(path)) {
103                  add(url, entry.getName().substring(path.length()), i.open());
104                }
105              }
106            }
107            finally {
108              Safe.close(i);
109            }
110          } else {
111            if (url.getPath().endsWith(".jar")) {
112              merge(new URL("jar:" + url + "!/"));
113            } else {
114              // WTF ?
115            }
116          }
117        }
118    
119        private void merge(java.io.File f) throws IOException {
120          for (final java.io.File file : f.listFiles()) {
121            String name = file.getName();
122            Node child = children.get(name);
123            if (file.isDirectory()) {
124              if (child == null) {
125                Dir dir = new Dir(name);
126                dir.merge(file);
127                children.put(name, dir);
128              } else if (child instanceof Dir) {
129                ((Dir)child).merge(file);
130              }
131            }
132            else {
133              if (child == null) {
134                children.put(name, new File(name, new InputStreamResolver() {
135                  public InputStream open() throws IOException {
136                    return new FileInputStream(file);
137                  }
138                }, file.lastModified()));
139              }
140            }
141          }
142        }
143    
144        private void add(URL baseURL, String entryName, InputStreamResolver resolver) throws IOException {
145          if (entryName.length() > 0 && entryName.charAt(entryName.length() - 1) != '/') {
146            add(baseURL, 0, entryName, 1, resolver);
147          }
148        }
149    
150        private void add(URL baseURL, int index, String entryName, long lastModified, InputStreamResolver resolver) throws IOException {
151          int next = entryName.indexOf('/', index);
152          if (next == -1) {
153            String name = entryName.substring(index);
154            Node child = children.get(name);
155            if (child == null) {
156              children.put(name, new File(name, resolver, lastModified));
157            }
158          } else {
159            String name = entryName.substring(index, next);
160            Node child = children.get(name);
161            if (child == null) {
162              Dir dir = new Dir(name);
163              children.put(name, dir);
164              dir.add(baseURL, next + 1, entryName, lastModified, resolver);
165            } else if (child instanceof Dir) {
166              ((Dir)child).add(baseURL, next + 1, entryName, lastModified, resolver);
167            } else {
168              //
169            }
170          }
171        }
172      }
173    
174      static class File extends Node {
175    
176        /** . */
177        final InputStreamResolver resolver;
178    
179        /** . */
180        final long lastModified;
181    
182        File(String name, InputStreamResolver url, long lastModified) {
183          super(name);
184    
185          //
186          this.resolver = url;
187          this.lastModified = lastModified;
188        }
189      }
190    }