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