/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.virtual.plugins.context;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.List;
import java.util.Map;
import org.jboss.logging.Logger;
import org.jboss.virtual.VFS;
import org.jboss.virtual.VFSUtils;
import org.jboss.virtual.VirtualFile;
import org.jboss.virtual.VirtualFileFilter;
import org.jboss.virtual.VisitorAttributes;
import org.jboss.virtual.spi.VFSContext;
import org.jboss.virtual.spi.VirtualFileHandler;
import org.jboss.virtual.spi.VirtualFileHandlerVisitor;
/**
* AbstractVFSContext.
*
* @author <a href="adrian@jboss.com">Adrian Brock</a>
* @author Scott.Stark@jboss.org
* @version $Revision: 1.1 $
*/
public abstract class AbstractVFSContext implements VFSContext
{
/** The log */
protected final Logger log = Logger.getLogger(getClass());
/** The VFS wrapper */
private VFS vfs;
/** The root url */
private final URI rootURI;
/** Options associated with the root URL */
private Map<String, String> rootOptions;
/** Root's peer within another context */
private VirtualFileHandler rootPeer;
/**
* Create a new AbstractVFSContext.
*
* @param rootURI the root url
* @throws IllegalArgumentException if rootURI is null
*/
protected AbstractVFSContext(URI rootURI)
{
if (rootURI == null)
throw new IllegalArgumentException("Null rootURI");
this.rootURI = rootURI;
String query = rootURI.getQuery();
rootOptions = VFSUtils.parseURLQuery(query);
}
/**
* Create a new AbstractVFSContext.
*
* @param rootURL the root url
* @throws URISyntaxException for illegal URL
* @throws IllegalArgumentException if rootURI is null
*/
protected AbstractVFSContext(URL rootURL) throws URISyntaxException
{
this(rootURL.toURI());
}
public VFS getVFS()
{
if (vfs == null)
vfs = new VFS(this);
return vfs;
}
public URI getRootURI()
{
return rootURI;
}
public void setRootPeer(VirtualFileHandler handler)
{
this.rootPeer = handler;
}
public VirtualFileHandler getRootPeer()
{
return rootPeer;
}
protected void addOption(String key, String value)
{
rootOptions.put(key, value);
}
public Map<String, String> getOptions()
{
return rootOptions;
}
/**
* Helper method to set options on an URL
*
* @param url url to set options on
* @return url with query parameters
* @throws java.net.MalformedURLException if url manipulation fails
*/
protected URL setOptionsToURL(URL url) throws MalformedURLException
{
if (rootOptions.size() == 0)
return url;
StringBuilder sb = new StringBuilder(url.toString());
sb.append("?");
int i = 0;
for (Map.Entry<String, String> ent : rootOptions.entrySet())
{
if (i > 0)
sb.append("&");
sb.append(ent.getKey()).append("=").append(ent.getValue());
i++;
}
return new URL(sb.toString());
}
public List<VirtualFileHandler> getChildren(VirtualFileHandler parent, boolean ignoreErrors) throws IOException
{
if (parent == null)
throw new IllegalArgumentException("Null parent");
return parent.getChildren(ignoreErrors);
}
public VirtualFileHandler getChild(VirtualFileHandler parent, String path) throws IOException
{
if (parent == null)
throw new IllegalArgumentException("Null parent");
if (path == null)
throw new IllegalArgumentException("Null path");
return parent.getChild(path);
}
/**
* Construct a URL from a given parent and a name
*
* @param parent a parent
* @param name a name of the child
* @return URL corresponding to a child
* @throws IOException for any error
*/
public URL getChildURL(VirtualFileHandler parent, String name) throws IOException
{
if(parent != null)
{
VFSContext parentCtx = parent.getVFSContext();
if (parentCtx != this)
{
if (parentCtx instanceof AbstractVFSContext)
{
return ((AbstractVFSContext) parentCtx).getChildURL(parent, name);
}
else
{
StringBuilder urlStr = new StringBuilder(512);
try
{
urlStr.append(parent.toURI());
if (urlStr.charAt( urlStr.length()-1) != '/')
urlStr.append("/");
urlStr.append(name);
return new URL(urlStr.toString());
}
catch (URISyntaxException e)
{
throw new RuntimeException("Failed to create child URL: " + parent + " + " + name, e);
}
}
}
}
StringBuilder urlStr = new StringBuilder(512);
URI rootUri = getRootURI();
urlStr.append(rootUri.getScheme())
.append(":").append(rootUri.getPath());
if(parent != null)
{
if (urlStr.charAt( urlStr.length()-1) != '/')
urlStr.append("/");
String pPathName = parent.getPathName();
if(pPathName.length() != 0)
urlStr.append(pPathName);
if (urlStr.charAt( urlStr.length()-1) != '/')
urlStr.append("/");
urlStr.append(name);
}
return new URL(urlStr.toString());
}
public void visit(VirtualFileHandler handler, VirtualFileHandlerVisitor visitor) throws IOException
{
if (handler == null)
throw new IllegalArgumentException("Null handler");
if (visitor == null)
throw new IllegalArgumentException("Null visitor");
VisitorAttributes attributes = visitor.getAttributes();
boolean includeRoot = attributes.isIncludeRoot();
boolean leavesOnly = attributes.isLeavesOnly();
boolean ignoreErrors = attributes.isIgnoreErrors();
boolean includeHidden = attributes.isIncludeHidden();
VirtualFileFilter recurseFilter = attributes.getRecurseFilter();
visit(handler, visitor, includeRoot, leavesOnly, ignoreErrors, includeHidden, recurseFilter);
}
/**
* Visit. the file system, recursive death checking is left to the visitor
* or otherwise a stack overflow.
*
* @param handler the reference handler
* @param visitor the visitor
* @param includeRoot whether to visit the root
* @param leavesOnly whether to visit leaves only
* @param ignoreErrors whether to ignore errors
* @param includeHidden whether to include hidden files
* @param recurseFilter the recurse filter
* @throws IOException for any problem accessing the virtual file system
*/
protected void visit(VirtualFileHandler handler, VirtualFileHandlerVisitor visitor,
boolean includeRoot, boolean leavesOnly, boolean ignoreErrors,
boolean includeHidden, VirtualFileFilter recurseFilter)
throws IOException
{
// Visit the root when asked
if (includeRoot)
visitor.visit(handler);
// Visit the children
boolean trace = log.isTraceEnabled();
List<VirtualFileHandler> children;
try
{
children = getChildren(handler, ignoreErrors);
}
catch (IOException e)
{
if (ignoreErrors == false)
throw e;
if( trace )
log.trace("Ignored: " + e);
return;
}
// Look through each child
for (VirtualFileHandler child : children)
{
// Ignore hidden if asked
if (includeHidden == false && child.isHidden())
{
if( trace )
log.trace("Ignoring hidden file: "+child);
continue;
}
// Visit the leaf or non-leaves when asked
boolean isLeaf = child.isLeaf();
if (leavesOnly == false || isLeaf)
visitor.visit(child);
else if( trace )
{
log.trace("Skipping non-leaf file: "+child);
}
// Recurse when asked
VirtualFile file = child.getVirtualFile();
if ( isLeaf == false && recurseFilter != null && recurseFilter.accepts(file))
{
try
{
if (handler instanceof DelegatingHandler)
child.getVFSContext().visit(child, visitor);
else
visit(child, visitor, false, leavesOnly, ignoreErrors, includeHidden, recurseFilter);
}
catch (StackOverflowError e)
{
log.debug("Original: " + child, e);
throw new IOException("Stack overflow, the file system is too complicated? " + child);
}
}
}
}
@Override
public String toString()
{
StringBuilder buffer = new StringBuilder();
buffer.append(getClass().getSimpleName());
buffer.append('@');
buffer.append(System.identityHashCode(this));
buffer.append('[');
buffer.append(rootURI);
buffer.append(']');
return buffer.toString();
}
@Override
public int hashCode()
{
return rootURI.hashCode();
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null || obj instanceof VFSContext == false)
return false;
VFSContext other = (VFSContext) obj;
return rootURI.equals(other.getRootURI());
}
}