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