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    }