001/* 002 GRANITE DATA SERVICES 003 Copyright (C) 2011 GRANITE DATA SERVICES S.A.S. 004 005 This file is part of Granite Data Services. 006 007 Granite Data Services is free software; you can redistribute it and/or modify 008 it under the terms of the GNU Library General Public License as published by 009 the Free Software Foundation; either version 2 of the License, or (at your 010 option) any later version. 011 012 Granite Data Services is distributed in the hope that it will be useful, but 013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License 015 for more details. 016 017 You should have received a copy of the GNU Library General Public License 018 along with this library; if not, see <http://www.gnu.org/licenses/>. 019*/ 020 021package org.granite.scan; 022 023import java.io.File; 024import java.io.IOException; 025import java.net.URL; 026import java.net.URLClassLoader; 027import java.net.URLDecoder; 028import java.util.ArrayList; 029import java.util.Collection; 030import java.util.Enumeration; 031import java.util.HashSet; 032import java.util.List; 033import java.util.Set; 034import java.util.zip.ZipEntry; 035import java.util.zip.ZipException; 036import java.util.zip.ZipFile; 037 038/** 039 * @author Franck WOLFF 040 */ 041public class URLScanner implements Scanner { 042 043 /////////////////////////////////////////////////////////////////////////// 044 // Fields. 045 046 private final List<ScannedItemHandler> handlers = new ArrayList<ScannedItemHandler>(); 047 private final String marker; 048 private final ClassLoader loader; 049 050 /////////////////////////////////////////////////////////////////////////// 051 // Constructors. 052 053 public URLScanner(ScannedItemHandler handler) { 054 this(handler, null, Thread.currentThread().getContextClassLoader()); 055 } 056 057 public URLScanner(ScannedItemHandler handler, String marker) { 058 this(handler, marker, Thread.currentThread().getContextClassLoader()); 059 } 060 061 public URLScanner(ScannedItemHandler handler, ClassLoader loader) { 062 this(handler, null, loader); 063 } 064 065 public URLScanner(ScannedItemHandler handler, String marker, ClassLoader loader) { 066 this.marker = marker; 067 this.handlers.add(handler); 068 this.loader = loader; 069 } 070 071 /////////////////////////////////////////////////////////////////////////// 072 // Properties. 073 074 public String getMarker() { 075 return marker; 076 } 077 078 public void addHandler(ScannedItemHandler handler) { 079 if (!handlers.contains(handler)) 080 handlers.add(handler); 081 } 082 083 public void addHandlers(Collection<ScannedItemHandler> handlers) { 084 for (ScannedItemHandler handler : handlers) 085 addHandler(handler); 086 } 087 088 public ClassLoader getLoader() { 089 return loader; 090 } 091 092 /////////////////////////////////////////////////////////////////////////// 093 // Scan methods. 094 095 public void scan() throws IOException { 096 Set<String> paths = new HashSet<String>(); 097 098 if (marker == null) { 099 if (!(loader instanceof URLClassLoader)) 100 throw new RuntimeException("ClassLoader used with no marker should be a URLClassLoader: " + loader); 101 102 for (URL url : ((URLClassLoader)loader).getURLs()) { 103 String urlPath = url.getFile(); 104 if (urlPath.endsWith("/")) 105 urlPath = urlPath.substring(0, urlPath.length() - 1); 106 paths.add(urlPath); 107 } 108 } 109 else { 110 for (Enumeration<URL> urlEnum = loader.getResources(marker); urlEnum.hasMoreElements(); ) { 111 String urlPath = URLDecoder.decode(urlEnum.nextElement().getFile(), "UTF-8"); 112 113 if (urlPath.startsWith("file:")) 114 urlPath = urlPath.substring(5); 115 116 // Jars. 117 if (urlPath.indexOf('!') > 0) 118 urlPath = urlPath.substring(0, urlPath.indexOf('!')); 119 // Regular directories. 120 else { 121 File dirOrArchive = new File(urlPath); 122 123 String[] tokens = marker.split("\\Q/\\E", -1); 124 for (int i = 0; i < tokens.length; i++) 125 dirOrArchive = dirOrArchive.getParentFile(); 126 127 urlPath = dirOrArchive.getPath(); 128 } 129 130 paths.add(urlPath); 131 } 132 } 133 134 for (String urlPath : paths) { 135 File file = new File(urlPath); 136 if (file.isDirectory()) 137 handleDirectory(file, file); 138 else 139 handleArchive(file); 140 } 141 } 142 143 144 public void handleArchive(File file) throws ZipException, IOException { 145 ZipFile zip = new ZipFile(file); 146 147 ZipScannedItem markerItem = null; 148 if (marker != null) { 149 ZipEntry markerEntry = zip.getEntry(marker); 150 markerItem = new ZipScannedItem(this, null, zip, markerEntry); 151 for (ScannedItemHandler handler : handlers) { 152 boolean skip = handler.handleMarkerItem(markerItem); 153 if (skip) 154 return; 155 } 156 } 157 158 for (Enumeration<? extends ZipEntry> entries = zip.entries(); entries.hasMoreElements(); ) { 159 ZipEntry entry = entries.nextElement(); 160 if (!entry.isDirectory() && (markerItem == null || !markerItem.getEntry().getName().equals(entry.getName()))) { 161 for (ScannedItemHandler handler : handlers) 162 handler.handleScannedItem(new ZipScannedItem(this, markerItem, zip, entry)); 163 } 164 } 165 } 166 167 public void handleDirectory(File root, File path) { 168 FileScannedItem markerItem = null; 169 if (marker != null) { 170 File markerFile = new File(root, marker); 171 markerItem = new FileScannedItem(this, null, root, markerFile); 172 for (ScannedItemHandler handler : handlers) { 173 boolean skip = handler.handleMarkerItem(markerItem); 174 if (skip) 175 return; 176 } 177 } 178 handleDirectory(markerItem, root, path); 179 } 180 181 public void handleDirectory(FileScannedItem markerItem, File root, File path) { 182 for (File child : path.listFiles()) { 183 if (child.isDirectory()) 184 handleDirectory(markerItem, root, child); 185 else if (markerItem == null || !markerItem.getFile().equals(child)) { 186 for (ScannedItemHandler handler : handlers) 187 handler.handleScannedItem(new FileScannedItem(this, markerItem, root, child)); 188 } 189 } 190 } 191}