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}