// -----------------------------------------------------------------------
//
// Copyright 2011 GRNET S.A. All rights reserved.
//
// Redistribution and use in source and binary forms, with or
// without modification, are permitted provided that the following
// conditions are met:
//
// 1. Redistributions of source code must retain the above
// copyright notice, this list of conditions and the following
// disclaimer.
//
// 2. Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and
// documentation are those of the authors and should not be
// interpreted as representing official policies, either expressed
// or implied, of GRNET S.A.
//
// -----------------------------------------------------------------------
using System.Diagnostics.Contracts;
using System.IO;
using System.Threading.Tasks;
using Castle.ActiveRecord;
using Castle.ActiveRecord.Framework;
using Pithos.Core.Agents;
using Pithos.Interfaces;
using log4net;
namespace Pithos.Core
{
using System;
using System.Collections.Generic;
using System.Linq;
///
/// TODO: Update summary.
///
[ActiveRecord]
public class FileState:ActiveRecordLinqBase
{
private static readonly ILog Log = LogManager.GetLogger("FileState");
private string _filePath;
private IList _tags=new List();
[PrimaryKey(PrimaryKeyType.Guid)]
public Guid Id { get; set; }
[Property(Unique=true,UniqueKey="IX_FileState_FilePath")]
public string FilePath
{
get { return _filePath; }
set { _filePath = value.ToLower(); }
}
[Property]
public FileOverlayStatus OverlayStatus { get; set; }
[Property]
public FileStatus FileStatus { get; set; }
[Property]
public string Checksum { get; set; }
[Property]
public long? Version { get; set; }
[Property]
public DateTime? VersionTimeStamp { get; set; }
[Property]
public bool IsShared { get; set; }
[Property]
public string SharedBy { get; set; }
[Property]
public bool ShareWrite { get; set; }
[HasMany(Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Lazy = true,Inverse=true)]
public IList Tags
{
get { return _tags; }
set { _tags=value;}
}
public static FileState FindByFilePath(string absolutePath)
{
if (string.IsNullOrWhiteSpace(absolutePath))
throw new ArgumentNullException("absolutePath");
Contract.EndContractBlock();
try
{
return Queryable.FirstOrDefault(s => s.FilePath == absolutePath.ToLower());
}
catch (Exception ex)
{
Log.Error(ex.ToString());
throw;
}
}
public static void DeleteByFilePath(string absolutePath)
{
if(string.IsNullOrWhiteSpace(absolutePath))
throw new ArgumentNullException("absolutePath");
Contract.EndContractBlock();
Execute((session, instance) =>
{
const string hqlDelete = "delete FileState where FilePath = :path";
var deletedEntities = session.CreateQuery(hqlDelete)
.SetString("path", absolutePath.ToLower())
.ExecuteUpdate();
return deletedEntities;
},null);
}
public static void StoreFileStatus(string absolutePath, FileStatus newStatus)
{
if (string.IsNullOrWhiteSpace(absolutePath))
throw new ArgumentNullException("absolutePath");
Contract.EndContractBlock();
Execute((session, instance) =>
{
const string hqlUpdate = "update FileState set FileStatus= :status where FilePath = :path ";
var updatedEntities = session.CreateQuery(hqlUpdate)
.SetString("path", absolutePath.ToLower())
.SetEnum("status", newStatus)
.ExecuteUpdate();
if (updatedEntities == 0)
{
var newState = new FileState { FilePath = absolutePath.ToLower(), Id = Guid.NewGuid(), FileStatus = newStatus };
newState.CreateAndFlush();
}
return null;
}, null);
}
public static void StoreOverlayStatus(string absolutePath, FileOverlayStatus newStatus)
{
if (string.IsNullOrWhiteSpace(absolutePath))
throw new ArgumentNullException("absolutePath");
Contract.EndContractBlock();
Execute((session, instance) =>
{
const string hqlUpdate = "update FileState set OverlayStatus= :status where FilePath = :path ";
var updatedEntities = session.CreateQuery(hqlUpdate)
.SetString("path", absolutePath.ToLower())
.SetEnum("status", newStatus)
.ExecuteUpdate();
if (updatedEntities == 0)
{
var newState = new FileState { FilePath = absolutePath, Id = Guid.NewGuid(), OverlayStatus = newStatus };
newState.CreateAndFlush();
}
return null;
}, null);
}
public static void UpdateStatus(string absolutePath, FileStatus fileStatus, FileOverlayStatus overlayStatus)
{
if (string.IsNullOrWhiteSpace(absolutePath))
throw new ArgumentNullException("absolutePath");
Contract.EndContractBlock();
Execute((session, instance) =>
{
const string hqlUpdate = "update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus where FilePath = :path ";
var updatedEntities = session.CreateQuery(hqlUpdate)
.SetString("path", absolutePath.ToLower())
.SetEnum("fileStatus", fileStatus)
.SetEnum("overlayStatus", overlayStatus)
.ExecuteUpdate();
return updatedEntities;
}, null);
}
public static void UpdateStatus(string absolutePath, FileStatus fileStatus)
{
if (string.IsNullOrWhiteSpace(absolutePath))
throw new ArgumentNullException("absolutePath");
Contract.EndContractBlock();
Execute((session, instance) =>
{
const string hqlUpdate = "update FileState set FileStatus= :fileStatus where FilePath = :path ";
var updatedEntities = session.CreateQuery(hqlUpdate)
.SetString("path", absolutePath.ToLower())
.SetEnum("fileStatus", fileStatus)
.ExecuteUpdate();
return updatedEntities;
}, null);
}
public static void RenameState(string oldPath, string newPath)
{
if (string.IsNullOrWhiteSpace(oldPath))
throw new ArgumentNullException("oldPath");
Contract.EndContractBlock();
Execute((session, instance) =>
{
const string hqlUpdate = "update FileState set FilePath= :newPath where FilePath = :oldPath ";
var updatedEntities = session.CreateQuery(hqlUpdate)
.SetString("oldPath", oldPath.ToLower())
.SetString("newPath", newPath.ToLower())
.ExecuteUpdate();
return updatedEntities;
}, null);
}
public static void UpdateStatus(Guid id, FileStatus fileStatus)
{
Contract.EndContractBlock();
Execute((session, instance) =>
{
const string hqlUpdate = "update FileState set FileStatus= :fileStatus where Id = :id ";
var updatedEntities = session.CreateQuery(hqlUpdate)
.SetGuid("id", id)
.SetEnum("fileStatus", fileStatus)
.ExecuteUpdate();
return updatedEntities;
}, null);
}
public static void UpdateChecksum(string absolutePath, string checksum)
{
if (string.IsNullOrWhiteSpace(absolutePath))
throw new ArgumentNullException("absolutePath");
Contract.EndContractBlock();
Execute((session, instance) =>
{
const string hqlUpdate = "update FileState set Checksum= :checksum where FilePath = :path ";
var updatedEntities = session.CreateQuery(hqlUpdate)
.SetString("path", absolutePath.ToLower())
.SetString("checksum", checksum)
.ExecuteUpdate();
return updatedEntities;
}, null);
}
public static void ChangeRootPath(string oldPath,string newPath)
{
if (String.IsNullOrWhiteSpace(oldPath))
throw new ArgumentNullException("oldPath");
if (!Path.IsPathRooted(oldPath))
throw new ArgumentException("oldPath must be an absolute path", "oldPath");
if (string.IsNullOrWhiteSpace(newPath))
throw new ArgumentNullException("newPath");
if (!Path.IsPathRooted(newPath))
throw new ArgumentException("newPath must be an absolute path", "newPath");
Contract.EndContractBlock();
//Ensure the paths end with the same character
if (!oldPath.EndsWith("\\"))
oldPath = oldPath + "\\";
if (!newPath.EndsWith("\\"))
newPath = newPath + "\\";
using (new TransactionScope())
{
Execute((session, instance) =>
{
const string hqlUpdate =
"update FileState set FilePath = replace(FilePath,:oldPath,:newPath) where FilePath like :oldPath || '%' ";
var renames=session.CreateQuery(hqlUpdate)
.SetString("oldPath", oldPath.ToLower())
.SetString("newPath", newPath.ToLower())
.ExecuteUpdate();
return renames;
}, null);
}
}
public static Task CreateForAsync(string filePath,int blockSize,string algorithm)
{
if (blockSize <= 0)
throw new ArgumentOutOfRangeException("blockSize");
if (String.IsNullOrWhiteSpace(algorithm))
throw new ArgumentNullException("algorithm");
Contract.EndContractBlock();
var fileState = new FileState
{
FilePath = filePath.ToLower(),
OverlayStatus = FileOverlayStatus.Unversioned,
FileStatus = FileStatus.Created,
Id=Guid.NewGuid()
};
return fileState.UpdateHashesAsync(blockSize,algorithm);
}
public async Task UpdateHashesAsync(int blockSize,string algorithm)
{
if (blockSize<=0)
throw new ArgumentOutOfRangeException("blockSize");
if (String.IsNullOrWhiteSpace(algorithm))
throw new ArgumentNullException("algorithm");
Contract.EndContractBlock();
//Skip updating the hash for folders
if (Directory.Exists(FilePath))
return this;
var hash = await TaskEx.Run(() =>
{
var info = new FileInfo(FilePath);
return info.CalculateHash(blockSize, algorithm);
});
Checksum = hash;
return this;
}
}
[ActiveRecord("Tags")]
public class FileTag : ActiveRecordLinqBase
{
[PrimaryKey]
public int Id { get; set; }
[Property]
public string Name { get; set; }
[Property]
public string Value { get; set; }
[BelongsTo("FileStateId")]
public FileState FileState { get; set; }
}
}