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.util.ArrayList;
029import java.util.Collection;
030import java.util.Enumeration;
031import java.util.List;
032
033import org.jboss.virtual.VFS;
034import org.jboss.virtual.VirtualFile;
035
036/**
037 * @author Franck WOLFF
038 */
039public class VFSScanner implements Scanner {
040
041    ///////////////////////////////////////////////////////////////////////////
042    // Fields.
043
044    private final List<ScannedItemHandler> handlers = new ArrayList<ScannedItemHandler>();
045    private final String marker;
046    private final ClassLoader loader;
047
048    ///////////////////////////////////////////////////////////////////////////
049    // Constructors.
050
051    public VFSScanner(ScannedItemHandler handler) {
052        this(handler, null, Thread.currentThread().getContextClassLoader());
053    }
054
055    public VFSScanner(ScannedItemHandler handler, String marker) {
056        this(handler, marker, Thread.currentThread().getContextClassLoader());
057    }
058
059    public VFSScanner(ScannedItemHandler handler, ClassLoader loader) {
060        this(handler, null, loader);
061    }
062
063    public VFSScanner(ScannedItemHandler handler, String marker, ClassLoader loader) {
064        this.marker = marker;
065        this.handlers.add(handler);
066        this.loader = loader;
067    }
068
069    ///////////////////////////////////////////////////////////////////////////
070    // Properties.
071
072    public String getMarker() {
073        return marker;
074    }
075
076    public void addHandler(ScannedItemHandler handler) {
077        if (!handlers.contains(handler))
078                handlers.add(handler);
079    }
080
081    public void addHandlers(Collection<ScannedItemHandler> handlers) {
082        for (ScannedItemHandler handler : handlers)
083                addHandler(handler);
084    }
085    
086    public ClassLoader getLoader() {
087        return loader;
088    }
089
090    ///////////////////////////////////////////////////////////////////////////
091    // Scan methods.
092
093    public void scan() throws IOException {
094        if (marker == null) {
095                if (!(loader instanceof URLClassLoader))
096                        throw new RuntimeException("ClassLoader used with no marker should be a URLClassLoader: " + loader);
097                
098            for (URL url : ((URLClassLoader)loader).getURLs()) {
099                VirtualFile root = getRoot(url, 1);
100                if (root != null)
101                        handleRoot(null, root);
102            }
103        }
104        else {
105            for (Enumeration<URL> urlEnum = loader.getResources(marker); urlEnum.hasMoreElements(); ) {
106                URL url = urlEnum.nextElement();
107                VirtualFile root = getRoot(url, marker.lastIndexOf('/') > 0 ? 2 : 1);
108                if (root != null)
109                        handleRoot(url, root);
110            }
111        }
112    }
113
114    
115    protected void handleRoot(URL markerUrl, VirtualFile root) throws IOException {
116        VFSFileScannedItem markerItem = null;
117        
118        if (markerUrl != null) {
119                VirtualFile markerFile = VFS.getRoot(markerUrl);
120                markerItem = new VFSFileScannedItem(this, null, markerFile, markerFile);
121            for (ScannedItemHandler handler : handlers) {
122                boolean skip = handler.handleMarkerItem(markerItem);
123                if (skip)
124                        return;
125            }
126        }
127        
128        if (root.isLeaf()) {
129            for (ScannedItemHandler handler : handlers)
130                handler.handleScannedItem(new VFSFileScannedItem(this, markerItem, root, root));
131        }
132        else {
133                String rootPathName = root.getPathName();
134                int rootPathNameLength = rootPathName.length();
135                List<VirtualFile> children = root.getChildrenRecursively();
136                for (VirtualFile child : children) {
137                        if (child.isLeaf()) {
138                                String name = child.getPathName();
139                                // move past '/'
140                                int length = rootPathNameLength;
141                                if (name.charAt(length) == '/')
142                                        length++;
143                    for (ScannedItemHandler handler : handlers)
144                        handler.handleScannedItem(new VFSFileScannedItem(this, markerItem, root, child));
145                        }
146                }
147        }
148    }
149    
150
151        protected static VirtualFile getRoot(URL url, int parentDepth) throws IOException {
152        String urlString = url.toString();
153        // TODO - this should go away once we figure out why -exp.war is part of CL resources
154        if (urlString.startsWith("vfs") == false)
155                return null;
156
157        int p = urlString.indexOf(":");
158        String file = urlString.substring(p + 1);
159        URL vfsurl = null;
160        String relative;
161        File fp = new File(file);
162
163        if (fp.exists()) {
164                vfsurl = fp.getParentFile().toURI().toURL();
165                relative = fp.getName();
166        }
167        else {
168                File curr = fp;
169                relative = fp.getName();
170                while ((curr = curr.getParentFile()) != null) {
171                        if (curr.exists()) {
172                                vfsurl = curr.toURI().toURL();
173                                break;
174                        }
175                        
176                        relative = curr.getName() + "/" + relative;
177                }
178        }
179
180        VirtualFile top = VFS.getRoot(vfsurl);
181        top = top.getChild(relative);
182        while (parentDepth > 0) {
183                if (top == null)
184                        throw new IllegalArgumentException("Null parent: " + vfsurl + ", relative: " + relative);
185                top = top.getParent();
186                parentDepth--;
187        }
188
189        return top;
190    }
191}