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
021 package org.granite.scan;
022
023 import java.io.File;
024 import java.io.IOException;
025 import java.net.URL;
026 import java.net.URLClassLoader;
027 import java.net.URLDecoder;
028 import java.util.ArrayList;
029 import java.util.Collection;
030 import java.util.Enumeration;
031 import java.util.HashSet;
032 import java.util.List;
033 import java.util.Set;
034 import java.util.zip.ZipEntry;
035 import java.util.zip.ZipException;
036 import java.util.zip.ZipFile;
037
038 /**
039 * @author Franck WOLFF
040 */
041 public 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 }