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}