001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2014 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                        @SuppressWarnings("deprecation")
122                                VirtualFile markerFile = VFS.getChild(markerUrl);
123                        markerItem = new VFS3FileScannedItem(this, null, markerFile, markerFile);
124                    for (ScannedItemHandler handler : handlers) {
125                        boolean skip = handler.handleMarkerItem(markerItem);
126                        if (skip)
127                                return;
128                    }
129                }
130                catch (URISyntaxException e) {
131                        IOException ex = new IOException("Invalid URI " + markerUrl);
132                        ex.initCause(e);
133                        throw ex;
134                }
135        }
136        
137        if (root.isFile()) {
138            for (ScannedItemHandler handler : handlers)
139                handler.handleScannedItem(new VFS3FileScannedItem(this, markerItem, root, root));
140        }
141        else {
142                String rootPathName = root.getPathName();
143                int rootPathNameLength = rootPathName.length();
144                List<VirtualFile> children = root.getChildrenRecursively();
145                for (VirtualFile child : children) {
146                        if (child.isFile()) {
147                                String name = child.getPathName();
148                                // move past '/'
149                                int length = rootPathNameLength;
150                                if (name.charAt(length) == '/')
151                                        length++;
152                    for (ScannedItemHandler handler : handlers)
153                        handler.handleScannedItem(new VFS3FileScannedItem(this, markerItem, root, child));
154                        }
155                }
156        }
157    }
158    
159
160    protected static VirtualFile getRoot(URL url, int parentDepth) throws IOException {
161        String urlString = url.toString();
162        // TODO - this should go away once we figure out why -exp.war is part of CL resources
163        if (urlString.startsWith("vfs") == false)
164                return null;
165
166        int p = urlString.indexOf(":");
167        String file = urlString.substring(p + 1);
168        URL vfsurl = null;
169        String relative;
170        File fp = new File(file);
171
172        if (fp.exists()) {
173                vfsurl = fp.getParentFile().toURI().toURL();
174                relative = fp.getName();
175        }
176        else {
177                File curr = fp;
178                relative = fp.getName();
179                while ((curr = curr.getParentFile()) != null) {
180                        if (curr.exists()) {
181                                vfsurl = curr.toURI().toURL();
182                                break;
183                        }
184                        
185                        relative = curr.getName() + "/" + relative;
186                }
187        }
188
189        try {
190                @SuppressWarnings("deprecation")
191                        VirtualFile top = VFS.getChild(vfsurl);
192                top = top.getChild(relative);
193                while (parentDepth > 0) {
194                        if (top == null)
195                                throw new IllegalArgumentException("Null parent: " + vfsurl + ", relative: " + relative);
196                        top = top.getParent();
197                        parentDepth--;
198                }
199        
200                return top;
201        }
202        catch (URISyntaxException e) {
203                IOException ex = new IOException("Invalid URI " + url);
204                ex.initCause(e);
205                throw ex;
206        }
207    }
208}