using System;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using Umbraco.Core.IO;
using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Models
{
///
/// Represents an abstract file which provides basic functionality for a File with an Alias and Name
///
[Serializable]
[DataContract(IsReference = true)]
public abstract class File : EntityBase, IFile
{
private string _path;
private string _originalPath;
// initialize to string.Empty so that it is possible to save a new file,
// should use the lazyContent ctor to set it to null when loading existing.
// cannot simply use HasIdentity as some classes (eg Script) override it
// in a weird way.
private string _content;
internal Func GetFileContent { get; set; }
protected File(string path, Func getFileContent = null)
{
_path = SanitizePath(path);
_originalPath = _path;
GetFileContent = getFileContent;
_content = getFileContent != null ? null : string.Empty;
}
private static readonly Lazy Ps = new Lazy();
private class PropertySelectors
{
public readonly PropertyInfo ContentSelector = ExpressionHelper.GetPropertyInfo(x => x.Content);
public readonly PropertyInfo PathSelector = ExpressionHelper.GetPropertyInfo(x => x.Path);
}
private string _alias;
private string _name;
private static string SanitizePath(string path)
{
return path
.Replace('\\', System.IO.Path.DirectorySeparatorChar)
.Replace('/', System.IO.Path.DirectorySeparatorChar);
//Don't strip the start - this was a bug fixed in 7.3, see ScriptRepositoryTests.PathTests
//.TrimStart(System.IO.Path.DirectorySeparatorChar)
//.TrimStart('/');
}
///
/// Gets or sets the Name of the File including extension
///
[DataMember]
public virtual string Name
{
get { return _name ?? (_name = System.IO.Path.GetFileName(Path)); }
}
///
/// Gets or sets the Alias of the File, which is the name without the extension
///
[DataMember]
public virtual string Alias
{
get
{
if (_alias == null)
{
var name = System.IO.Path.GetFileName(Path);
if (name == null) return string.Empty;
var lastIndexOf = name.LastIndexOf(".", StringComparison.InvariantCultureIgnoreCase);
_alias = name.Substring(0, lastIndexOf);
}
return _alias;
}
}
///
/// Gets or sets the Path to the File from the root of the file's associated IFileSystem
///
[DataMember]
public virtual string Path
{
get { return _path; }
set
{
//reset
_alias = null;
_name = null;
SetPropertyValueAndDetectChanges(SanitizePath(value), ref _path, Ps.Value.PathSelector);
}
}
///
/// Gets the original path of the file
///
public string OriginalPath
{
get { return _originalPath; }
}
///
/// Called to re-set the OriginalPath to the Path
///
public void ResetOriginalPath()
{
_originalPath = _path;
}
///
/// Gets or sets the Content of a File
///
/// Marked as DoNotClone, because it should be lazy-reloaded from disk.
[DataMember]
[DoNotClone]
public virtual string Content
{
get
{
if (_content != null)
return _content;
// else, must lazy-load, and ensure it's not null
if (GetFileContent != null)
_content = GetFileContent(this);
return _content ?? (_content = string.Empty);
}
set
{
SetPropertyValueAndDetectChanges(
value ?? string.Empty, // cannot set to null
ref _content, Ps.Value.ContentSelector);
}
}
///
/// Gets or sets the file's virtual path (i.e. the file path relative to the root of the website)
///
public string VirtualPath { get; set; }
// this exists so that class that manage name and alias differently, eg Template,
// can implement their own cloning - (though really, not sure it's even needed)
protected virtual void DeepCloneNameAndAlias(File clone)
{
// set fields that have a lazy value, by forcing evaluation of the lazy
clone._name = Name;
clone._alias = Alias;
}
public override object DeepClone()
{
var clone = (File) base.DeepClone();
// clear fields that were memberwise-cloned and that we don't want to clone
clone._content = null;
// turn off change tracking
clone.DisableChangeTracking();
// ...
DeepCloneNameAndAlias(clone);
// this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
// re-enable tracking
clone.EnableChangeTracking();
return clone;
}
}
}