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