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
020package org.crsh.vfs.spi.url;
021
022import org.crsh.util.InputStreamFactory;
023import org.crsh.util.Safe;
024import org.crsh.util.ZipIterator;
025
026import java.io.FileInputStream;
027import java.io.IOException;
028import java.io.InputStream;
029import java.net.URISyntaxException;
030import java.net.URL;
031import java.util.ArrayList;
032import java.util.Collections;
033import java.util.Enumeration;
034import java.util.HashMap;
035import java.util.LinkedList;
036import java.util.zip.ZipEntry;
037
038/** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
039public class Node {
040
041  /** . */
042  final String name;
043
044  /** . */
045  HashMap<String, Node> children = new HashMap<String, Node>();
046
047  /** . */
048  LinkedList<File> files = new LinkedList<File>();
049
050  public Node() {
051    this.name = "";
052  }
053
054  private Node(String name) {
055    this.name = 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    } else 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    java.io.File[] files = f.listFiles();
121    if (files != null) {
122      for (final java.io.File file : files) {
123        String name = file.getName();
124        Node child = children.get(name);
125        if (file.isDirectory()) {
126          if (child == null) {
127            Node dir = new Node(name);
128            dir.merge(file);
129            children.put(name, dir);
130          } else {
131            child.merge(file);
132          }
133        }
134        else {
135          if (child == null) {
136            children.put(name, child = new Node(name));
137          }
138          child.files.add(new File(new InputStreamFactory() {
139            public InputStream open() throws IOException {
140              return new FileInputStream(file);
141            }
142          }, file.lastModified()));
143        }
144      }
145    }
146  }
147
148  private void add(URL baseURL, String entryName, InputStreamFactory resolver) throws IOException {
149    if (entryName.length() > 0 && entryName.charAt(entryName.length() - 1) != '/') {
150      add(baseURL, 0, entryName, 1, resolver);
151    }
152  }
153
154  private void add(URL baseURL, int index, String entryName, long lastModified, InputStreamFactory resolver) throws IOException {
155    int next = entryName.indexOf('/', index);
156    if (next == -1) {
157      String name = entryName.substring(index);
158      Node child = children.get(name);
159      if (child == null) {
160        children.put(name, child = new Node(name));
161      }
162      child.files.add(new File(resolver, lastModified));
163    } else {
164      String name = entryName.substring(index, next);
165      Node child = children.get(name);
166      if (child == null) {
167        children.put(name, child = new Node(name));
168      }
169      child.add(baseURL, next + 1, entryName, lastModified, resolver);
170    }
171  }
172
173  static class File {
174
175    /** . */
176    final InputStreamFactory resolver;
177
178    /** . */
179    final long lastModified;
180
181    File(InputStreamFactory url, long lastModified) {
182      this.resolver = url;
183      this.lastModified = lastModified;
184    }
185  }
186}