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 Lesser General Public License as published by
009  the Free Software Foundation; either version 3 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 Lesser General Public License
015  for more details.
016
017  You should have received a copy of the GNU Lesser 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.URISyntaxException;
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.vfs.VFS;
034import org.jboss.vfs.VirtualFile;
035
036/**
037 * @author Franck WOLFF
038 */
039public class VFS3Scanner 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 VFS3Scanner(ScannedItemHandler handler) {
052        this(handler, null, Thread.currentThread().getContextClassLoader());
053    }
054
055    public VFS3Scanner(ScannedItemHandler handler, String marker) {
056        this(handler, marker, Thread.currentThread().getContextClassLoader());
057    }
058
059    public VFS3Scanner(ScannedItemHandler handler, ClassLoader loader) {
060        this(handler, null, loader);
061    }
062
063    public VFS3Scanner(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        VFS3FileScannedItem markerItem = null;
117        
118        if (markerUrl != null) {
119                try {
120                        VirtualFile markerFile = VFS.getChild(markerUrl);
121                        markerItem = new VFS3FileScannedItem(this, null, markerFile, markerFile);
122                    for (ScannedItemHandler handler : handlers) {
123                        boolean skip = handler.handleMarkerItem(markerItem);
124                        if (skip)
125                                return;
126                    }
127                }
128                catch (URISyntaxException e) {
129                        IOException ex = new IOException("Invalid URI " + markerUrl);
130                        ex.initCause(e);
131                        throw ex;
132                }
133        }
134        
135        if (root.isFile()) {
136            for (ScannedItemHandler handler : handlers)
137                handler.handleScannedItem(new VFS3FileScannedItem(this, markerItem, root, root));
138        }
139        else {
140                String rootPathName = root.getPathName();
141                int rootPathNameLength = rootPathName.length();
142                List<VirtualFile> children = root.getChildrenRecursively();
143                for (VirtualFile child : children) {
144                        if (child.isFile()) {
145                                String name = child.getPathName();
146                                // move past '/'
147                                int length = rootPathNameLength;
148                                if (name.charAt(length) == '/')
149                                        length++;
150                    for (ScannedItemHandler handler : handlers)
151                        handler.handleScannedItem(new VFS3FileScannedItem(this, markerItem, root, child));
152                        }
153                }
154        }
155    }
156    
157
158    protected static VirtualFile getRoot(URL url, int parentDepth) throws IOException {
159        String urlString = url.toString();
160        // TODO - this should go away once we figure out why -exp.war is part of CL resources
161        if (urlString.startsWith("vfs") == false)
162                return null;
163
164        int p = urlString.indexOf(":");
165        String file = urlString.substring(p + 1);
166        URL vfsurl = null;
167        String relative;
168        File fp = new File(file);
169
170        if (fp.exists()) {
171                vfsurl = fp.getParentFile().toURI().toURL();
172                relative = fp.getName();
173        }
174        else {
175                File curr = fp;
176                relative = fp.getName();
177                while ((curr = curr.getParentFile()) != null) {
178                        if (curr.exists()) {
179                                vfsurl = curr.toURI().toURL();
180                                break;
181                        }
182                        
183                        relative = curr.getName() + "/" + relative;
184                }
185        }
186
187        try {
188                VirtualFile top = VFS.getChild(vfsurl);
189                top = top.getChild(relative);
190                while (parentDepth > 0) {
191                        if (top == null)
192                                throw new IllegalArgumentException("Null parent: " + vfsurl + ", relative: " + relative);
193                        top = top.getParent();
194                        parentDepth--;
195                }
196        
197                return top;
198        }
199        catch (URISyntaxException e) {
200                IOException ex = new IOException("Invalid URI " + url);
201                ex.initCause(e);
202                throw ex;
203        }
204    }
205}