#region License
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
#region Imports
using System;
using System.Globalization;
using System.IO;
using Spring.Util;
#endregion
namespace Spring.Core.IO{
/// <summary>
/// A <see cref="System.IO.File"/> backed resource.
/// </summary>
/// <remarks>
/// <p>
/// Supports resolution as both a <see cref="System.IO.FileInfo"/> and a
/// <see cref="System.Uri"/>.
/// </p>
/// <p>
/// Also supports the use of the <c>~</c> character. If the <c>~</c> character
/// is the first character in a resource path (sans protocol), the <c>~</c>
/// character will be replaced with the value of the
/// <c>System.AppDomain.CurrentDomain.BaseDirectory</c> property (an example of
/// this can be seen in the examples below).
/// </p>
/// </remarks>
/// <example>
/// <p>
/// Consider the example of an application that is running (has been launched
/// from) the <c>C:\App\</c> directory. The following resource paths will map
/// to the following resources on the filesystem...
/// </p>
/// <code escaped="true">
/// strings.txt C:\App\strings.txt
/// ~/strings.txt C:\App\strings.txt
/// file://~/strings.txt C:\App\strings.txt
/// file://~/../strings.txt C:\strings.txt
/// ../strings.txt C:\strings.txt
/// ~/../strings.txt C:\strings.txt
///
/// // note that only a leading ~ character is resolved to the executing directory...
/// stri~ngs.txt C:\App\stri~ngs.txt
/// </code>
/// </example>
/// <author>Juergen Hoeller</author>
/// <author>Leonardo Susatyo (.NET)</author>
/// <author>Aleksandar Seovic (.NET)</author>
public class FileSystemResource : AbstractResource
{
private FileInfo fileHandle;
private string rootLocation;
private string resourcePath;
#region Constructors
/// <summary>
/// Creates a new instance of the
/// <see cref="Spring.Core.IO.FileSystemResource"/> class.
/// </summary>
protected FileSystemResource()
{
}
/// <summary>
/// Creates a new instance of the
/// <see cref="Spring.Core.IO.FileSystemResource"/> class.
/// </summary>
/// <param name="resourceName">
/// The name of the file system resource.
/// </param>
/// <exception cref="System.ArgumentNullException">
/// If the supplied <paramref name="resourceName"/> is
/// <see langword="null"/> or contains only whitespace character(s).
/// </exception>
public FileSystemResource(string resourceName)
: this(resourceName, false)
{
}
/// <summary>
/// Creates a new instance of the
/// <see cref="Spring.Core.IO.FileSystemResource"/> class.
/// </summary>
/// <param name="resourceName">
/// The name of the file system resource.
/// </param>
/// <param name="suppressInitialize">
/// Supresses initialization of this instance. Used from derived classes.
/// </param>
/// <exception cref="System.ArgumentNullException">
/// If the supplied <paramref name="resourceName"/> is
/// <see langword="null"/> or contains only whitespace character(s).
/// </exception>
protected FileSystemResource(string resourceName, bool suppressInitialize)
: base(resourceName)
{
if (!suppressInitialize)
{
Initialize( resourceName );
}
}
#endregion
#region Properties
/// <summary>
/// Returns the underlying <see cref="System.IO.FileInfo"/> handle for
/// this resource.
/// </summary>
/// <value>
/// The <see cref="System.IO.FileInfo"/> handle for this resource.
/// </value>
/// <seealso cref="Spring.Core.IO.IResource.File"/>
public override FileInfo File
{
get { return fileHandle; }
}
/// <summary>
/// Does this <see cref="Spring.Core.IO.IResource"/> support relative
/// resource retrieval?
/// </summary>
/// <remarks>
/// <p>
/// This implementation does support relative resource retrieval, and
/// so will always return <see langword="true"/>.
/// </p>
/// </remarks>
/// <value>
/// <see langword="true"/> if this
/// <see cref="Spring.Core.IO.IResource"/> supports relative resource
/// retrieval.
/// </value>
/// <seealso cref="Spring.Core.IO.AbstractResource.SupportsRelativeResources"/>
protected override bool SupportsRelativeResources
{
get { return true; }
}
/// <summary>
/// Gets the root location of the resource (a drive or UNC file share
/// name in this case).
/// </summary>
/// <value>
/// The root location of the resource.
/// </value>
/// <seealso cref="Spring.Core.IO.AbstractResource.RootLocation"/>
protected override string RootLocation
{
get { return this.rootLocation; }
}
/// <summary>
/// Gets the current path of the resource.
/// </summary>
/// <value>
/// The current path of the resource.
/// </value>
/// <seealso cref="Spring.Core.IO.AbstractResource.ResourcePath"/>
protected override string ResourcePath
{
get { return this.resourcePath; }
}
/// <summary>
/// Gets those characters that are valid path separators for the
/// resource type.
/// </summary>
/// <value>
/// Those characters that are valid path separators for the resource
/// type.
/// </value>
/// <seealso cref="Spring.Core.IO.AbstractResource.PathSeparatorChars"/>
/// <seealso cref="System.IO.Path"/>
protected override char[] PathSeparatorChars
{
get { return new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; }
}
/// <summary>
/// Return an <see cref="System.IO.Stream"/> for this resource.
/// </summary>
/// <value>
/// An <see cref="System.IO.Stream"/>.
/// </value>
/// <exception cref="System.IO.IOException">
/// If the stream could not be opened.
/// </exception>
/// <exception cref="System.IO.FileNotFoundException">
/// If the underlying file could not be found.
/// </exception>
/// <seealso cref="Spring.Core.IO.IInputStreamSource"/>
public override Stream InputStream
{
get
{
if (Uri.IsFile)
{
try
{
return new FileStream(Uri.LocalPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
}
catch(DirectoryNotFoundException)
{
// ignore difference between File & Directory exception in this case
}
}
throw new FileNotFoundException(Description
+ " cannot be resolved to local file path"
+ " - resource does not use 'file:' protocol.");
}
}
/// <summary>
/// Returns a description for this resource.
/// </summary>
/// <value>
/// A description for this resource.
/// </value>
/// <seealso cref="Spring.Core.IO.IResource.Description"/>
public override string Description
{
get
{
return string.Format(
CultureInfo.InvariantCulture,
"file [{0}]", fileHandle.FullName);
}
}
/// <summary>
/// Returns the <see cref="System.Uri"/> handle for this resource.
/// </summary>
/// <value>
/// The <see cref="System.Uri"/> handle for this resource.
/// </value>
/// <exception cref="System.IO.IOException">
/// If the resource is not available or cannot be exposed as a
/// <see cref="System.Uri"/>.
/// </exception>
/// <seealso cref="Spring.Core.IO.IResource.Uri"/>
public override Uri Uri
{
get { return new UriBuilder("file", null, 0, fileHandle.FullName).Uri; }
}
#endregion
/// <summary>
/// Initializes this instance.
/// </summary>
/// <param name="resourceName"></param>
protected void Initialize( string resourceName )
{
this.fileHandle = ResolveFileHandle(resourceName);
this.rootLocation = ResolveRootLocation(resourceName);
this.resourcePath = ResolveResourcePath(resourceName);
}
/// <summary>
/// Resolves the <see cref="System.IO.FileInfo"/> handle
/// for the supplied <paramref name="resourceName"/>.
/// </summary>
/// <param name="resourceName">
/// The name of the file system resource.
/// </param>
/// <returns>
/// The <see cref="System.IO.FileInfo"/> handle for this resource.
/// </returns>
protected virtual FileInfo ResolveFileHandle(string resourceName)
{
return new FileInfo(ResolveResourceNameWithoutProtocol(resourceName));
}
/// <summary>
/// Resolves the root location for the supplied <paramref name="resourceName"/>.
/// </summary>
/// <param name="resourceName">
/// The name of the file system resource.
/// </param>
/// <returns>
/// The root location of the resource.
/// </returns>
protected virtual string ResolveRootLocation(string resourceName)
{
string root = this.fileHandle.Directory.Root.ToString();
if (root.Length > 0 &&
(root.EndsWith(Path.DirectorySeparatorChar.ToString()) ||
root.EndsWith(Path.AltDirectorySeparatorChar.ToString())))
{
root = root.Substring(0, root.Length - 1);
}
return root;
}
/// <summary>
/// Resolves the path for the supplied <paramref name="resourceName"/>.
/// </summary>
/// <param name="resourceName">
/// The name of the file system resource.
/// </param>
/// <returns>
/// The current path of the resource.
/// </returns>
protected virtual string ResolveResourcePath(string resourceName)
{
string path = this.fileHandle.DirectoryName;
if (path.Equals(this.fileHandle.Directory.Root.ToString()))
{
path = null;
}
else
{
path = path.Substring(this.rootLocation.Length + 1);
}
return path;
}
/// <summary>
/// Resolves the presence of the
/// <paramref name="basePathPlaceHolder"/> value
/// in the supplied <paramref name="resourceName"/> into a path.
/// </summary>
/// <param name="resourceName">
/// The name of the resource.
/// </param>
/// <param name="basePathPlaceHolder">
/// The string that is a placeholder for a base path.
/// </param>
/// <returns>
/// The name of the resource with any <paramref name="basePathPlaceHolder"/>
/// value having been resolved into an actual path.
/// </returns>
protected override string ResolveBasePathPlaceHolder(
string resourceName, string basePathPlaceHolder)
{
// Remove extra slashes used to indicate that resource is local (handle the case "/C:/path1/...")
if (resourceName[0] == '/' && resourceName[2] == ':')
{
resourceName = resourceName.Substring(1);
}
if (StringUtils.HasText(resourceName)
&& resourceName.TrimStart().StartsWith(basePathPlaceHolder))
{
return resourceName.Replace(basePathPlaceHolder, AppDomain.CurrentDomain.BaseDirectory).TrimStart();
}
return resourceName;
}
/// <summary>
/// Does the supplied <paramref name="resourceName"/> relative ?
/// </summary>
/// <param name="resourceName">
/// The name of the resource to test.
/// </param>
/// <returns>
/// <see langword="true"/> if resource name is relative;
/// otherwise <see langword="false"/>.
/// </returns>
protected override bool IsRelativeResource(string resourceName)
{
return (!
(resourceName.StartsWith(@"\\") || // UNC file share
resourceName.IndexOf(':') >= 0 || // drive
resourceName.StartsWith(BasePathPlaceHolder)));
}
}
}
|