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