513 lines
18 KiB
C#
513 lines
18 KiB
C#
using System;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using System.Xml.Linq;
|
|
using UAF;
|
|
using UAF.WPF.Models;
|
|
|
|
namespace DataFileAnalyzer
|
|
{
|
|
/// <summary>
|
|
/// All we need to know about a UAF data file:
|
|
/// path, key, dependencies, tags, etc...
|
|
/// </summary>
|
|
public class DataFile : ListViewItemData
|
|
{
|
|
protected DataFileKey m_key = DataFileKey.Invalid;
|
|
protected string m_filePath = null;
|
|
protected string m_normalizedPath = null;
|
|
protected DateTime m_lastModificationTime = DateTimeExtensions.Invalid;
|
|
protected long m_length = 0;
|
|
protected DateTime m_lastCheckTime = DateTimeExtensions.Invalid;
|
|
protected DateTime m_lastDependenciesCheckTime = DateTimeExtensions.Invalid;
|
|
protected List<DataFileDependency> m_children = new List<DataFileDependency>();
|
|
protected List<DataFileDependency> m_parents = new List<DataFileDependency>();
|
|
protected List<DataFileError> m_errors = new List<DataFileError>();
|
|
protected List<DataFileError> m_warnings = new List<DataFileError>();
|
|
|
|
#region properties
|
|
|
|
/// <summary>
|
|
/// The key of the file
|
|
/// </summary>
|
|
public DataFileKey Key { get { return m_key; } set { m_key = value; } }
|
|
|
|
/// <summary>
|
|
/// The path of the file, relative to the data folder, keeping its original case.
|
|
/// </summary>
|
|
public string FilePath { get { return m_filePath; } set { m_filePath = value; } }
|
|
|
|
/// <summary>
|
|
/// The normalized path of the file, relative to the data folder.
|
|
/// </summary>
|
|
public string NormalizedPath { get { return m_normalizedPath; } set { m_normalizedPath = value; } }
|
|
|
|
/// <summary>
|
|
/// Last time the file was written (UTC)
|
|
/// </summary>
|
|
public DateTime LastModificationTime { get { return m_lastModificationTime; } set { m_lastModificationTime = value; } }
|
|
|
|
/// <summary>
|
|
/// Size of the file
|
|
/// </summary>
|
|
public long Length { get { return m_length; } set { m_length = value; } }
|
|
|
|
/// <summary>
|
|
/// Last time we checked on the file (UTC)
|
|
/// </summary>
|
|
public DateTime LastCheckTime { get { return m_lastCheckTime; } set { m_lastCheckTime = value; } }
|
|
|
|
/// <summary>
|
|
/// Last time we computed the dependencies of the file (UTC)
|
|
/// </summary>
|
|
public DateTime LastDependenciesCheckTime { get { return m_lastDependenciesCheckTime; } set { m_lastDependenciesCheckTime = value; } }
|
|
|
|
/// <summary>
|
|
/// The files referenced by this one
|
|
/// </summary>
|
|
public List<DataFileDependency> Children { get { return m_children; } }
|
|
|
|
/// <summary>
|
|
/// The files referencing by this one
|
|
/// </summary>
|
|
public List<DataFileDependency> Parents { get { return m_parents; } }
|
|
|
|
/// <summary>
|
|
/// Are the dependencies up-to-date?
|
|
/// </summary>
|
|
public bool DependenciesUpToDate
|
|
{
|
|
get
|
|
{
|
|
return !m_lastDependenciesCheckTime.IsInvalid() && m_lastDependenciesCheckTime > m_lastModificationTime;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is the file present on drive?
|
|
/// </summary>
|
|
public bool IsMissing { get { return Errors.Any(error => error.Code == DataFileError.Codes.MissingFile); } }
|
|
|
|
/// <summary>
|
|
/// Does the file have missing dependencies?
|
|
/// </summary>
|
|
public bool HasMissingDependencies { get { return Errors.Any(error => error.Code == DataFileError.Codes.UnresolvedDependencies); } }
|
|
|
|
/// <summary>
|
|
/// Is the file absent from Perforce depot?
|
|
/// </summary>
|
|
public bool AbsentFromPerforce { get { return ((DataFileAnalyzerModel)UAF.Application.Workspace.This.ActiveDocument).IsPerforceEnabled && Warnings.Any(error => error.Code == DataFileError.Codes.NotInPerforce); } }
|
|
|
|
/// <summary>
|
|
/// Is the file absent from Perforce depot?
|
|
/// </summary>
|
|
public bool IsOrphan { get { return Warnings.Any(error => error.Code == DataFileError.Codes.Orphan); } }
|
|
|
|
/// <summary>
|
|
/// Does the file must compute its dependencies?
|
|
/// </summary>
|
|
public bool MustComputeDependencies { get { return Warnings.Any(warning => warning.Code == DataFileError.Codes.NeedDependencies); } }
|
|
|
|
/// <summary>
|
|
/// Did the Data File Analyzer fail to compute dependencies?
|
|
/// </summary>
|
|
public bool ComputeDependenciesError { get { return Errors.Any(error => error.Code == DataFileError.Codes.ErrorComputingDependencies); } }
|
|
|
|
/// <summary>
|
|
/// The list of errors that occurred during file update
|
|
/// </summary>
|
|
public List<DataFileError> Errors { get { return m_errors; } }
|
|
|
|
/// <summary>
|
|
/// The list of warnings that occurred during file update
|
|
/// </summary>
|
|
public List<DataFileError> Warnings { get { return m_warnings; } }
|
|
|
|
/// <summary>
|
|
/// Does the file has errors?
|
|
/// </summary>
|
|
public bool HasErrors { get { return m_errors.Count > 0; } }
|
|
|
|
/// <summary>
|
|
/// Does the file has warnings?
|
|
/// </summary>
|
|
public bool HasWarnings { get { return m_warnings.Count > 0; } }
|
|
|
|
#endregion
|
|
|
|
#region Errors/Warnings management
|
|
|
|
/// <summary>
|
|
/// Add a warning
|
|
/// </summary>
|
|
public void AddWarning(DataFileError.Codes _code, string _message)
|
|
{
|
|
RemoveWarning(_code);
|
|
m_warnings.Add(new DataFileError(_code, _message));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a warning message with a generic code
|
|
/// </summary>
|
|
public void AddWarning(string _message)
|
|
{
|
|
RemoveWarning(DataFileError.Codes.Generic);
|
|
m_warnings.Add(new DataFileError(_message));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a warning code with the generic message
|
|
/// </summary>
|
|
public void AddWarning(DataFileError.Codes _code)
|
|
{
|
|
RemoveWarning(_code);
|
|
m_warnings.Add(new DataFileError(_code));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove the warning with this code
|
|
/// </summary>
|
|
public void RemoveWarning(DataFileError.Codes _code)
|
|
{
|
|
foreach (DataFileError warning in m_warnings)
|
|
{
|
|
if (warning.Code == _code)
|
|
{
|
|
m_warnings.Remove(warning);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add an error
|
|
/// </summary>
|
|
public void AddError(DataFileError.Codes _code, string _message)
|
|
{
|
|
RemoveError(_code);
|
|
m_errors.Add(new DataFileError(_code, _message));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add an error message with a generic code
|
|
/// </summary>
|
|
public void AddError(string _message)
|
|
{
|
|
RemoveError(DataFileError.Codes.Generic);
|
|
m_errors.Add(new DataFileError(_message));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add an error code with the generic message
|
|
/// </summary>
|
|
public void AddError(DataFileError.Codes _code)
|
|
{
|
|
RemoveError(_code);
|
|
m_errors.Add(new DataFileError(_code));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove the error with this code
|
|
/// </summary>
|
|
public void RemoveError(DataFileError.Codes _code)
|
|
{
|
|
foreach (DataFileError error in m_errors)
|
|
{
|
|
if (error.Code == _code)
|
|
{
|
|
m_errors.Remove(error);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove all errors and all warnings
|
|
/// </summary>
|
|
public void ClearErrorsAndWarnings()
|
|
{
|
|
m_errors.Clear();
|
|
m_warnings.Clear();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Load/Save
|
|
|
|
/// <summary>
|
|
/// Save into an XML node
|
|
/// <returns>true if success</returns>
|
|
/// </summary>
|
|
public bool Save(XElement _node)
|
|
{
|
|
bool result = DataFileKey.Save(m_key, _node);
|
|
_node.SetAttributeValue("Path", m_filePath);
|
|
_node.SetAttributeValue("LastModificationTime", m_lastModificationTime.ToString("o"));
|
|
_node.SetAttributeValue("Length", m_length);
|
|
|
|
_node.SetAttributeValue("LastCheckTime", m_lastCheckTime.ToString("o"));
|
|
_node.SetAttributeValue("LastDependenciesCheckTime", m_lastDependenciesCheckTime.ToString("o"));
|
|
|
|
foreach (DataFileDependency dep in m_children)
|
|
{
|
|
XElement depElem = new XElement("Child");
|
|
result &= dep.Save(depElem);
|
|
_node.Add(depElem);
|
|
}
|
|
|
|
foreach (DataFileError warning in m_warnings)
|
|
{
|
|
XElement warningElement = new XElement("Warning");
|
|
result &= warning.Save(warningElement);
|
|
_node.Add(warningElement);
|
|
}
|
|
|
|
foreach (DataFileError error in m_errors)
|
|
{
|
|
XElement errorElement = new XElement("Error");
|
|
result &= error.Save(errorElement);
|
|
_node.Add(errorElement);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load from an XML node
|
|
/// <returns>true if success</returns>
|
|
/// </summary>
|
|
public bool Load(XElement _node)
|
|
{
|
|
m_key = DataFileKey.Load(_node);
|
|
bool result = !m_key.IsInvalid();
|
|
|
|
m_filePath = _node.Attribute("Path").Value;
|
|
m_normalizedPath = PathUtils.Normalize(m_filePath);
|
|
|
|
XAttribute attr = _node.Attribute("LastModificationTime");
|
|
if (attr == null || !DateTime.TryParse(attr.Value, out m_lastModificationTime))
|
|
m_lastModificationTime = DateTimeExtensions.Invalid;
|
|
|
|
attr = _node.Attribute("Length");
|
|
if (attr == null || !long.TryParse(attr.Value, out m_length))
|
|
m_length = 0;
|
|
|
|
attr = _node.Attribute("LastCheckTime");
|
|
if (attr == null || !DateTime.TryParse(attr.Value, out m_lastCheckTime))
|
|
m_lastCheckTime = DateTimeExtensions.Invalid;
|
|
|
|
attr = _node.Attribute("LastDependenciesCheckTime");
|
|
if (attr == null || !DateTime.TryParse(attr.Value, out m_lastDependenciesCheckTime))
|
|
m_lastDependenciesCheckTime = DateTimeExtensions.Invalid;
|
|
|
|
foreach (XElement depElement in _node.Elements("Child"))
|
|
{
|
|
DataFileDependency dep = new DataFileDependency();
|
|
if (dep.Load(depElement))
|
|
m_children.Add(dep);
|
|
else
|
|
result = false;
|
|
}
|
|
|
|
foreach (XElement warningElement in _node.Elements("Warning"))
|
|
{
|
|
DataFileError warning = new DataFileError("");
|
|
if (warning.Load(warningElement))
|
|
m_warnings.Add(warning);
|
|
else
|
|
result = false;
|
|
}
|
|
|
|
foreach (XElement errorElement in _node.Elements("Error"))
|
|
{
|
|
DataFileError error = new DataFileError("");
|
|
if (error.Load(errorElement))
|
|
m_errors.Add(error);
|
|
else
|
|
result = false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Child/Parent management
|
|
|
|
/// <summary>
|
|
/// Get a child dependency by key
|
|
/// <returns>null if not found</returns>
|
|
/// </summary>
|
|
public DataFileDependency GetChild(DataFileKey _key)
|
|
{
|
|
foreach (DataFileDependency dep in m_children)
|
|
{
|
|
if (dep.Key.Equals(_key))
|
|
return dep;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a child dependency by path
|
|
/// <returns>null if not found</returns>
|
|
/// </summary>
|
|
public DataFileDependency GetChild(string _normalizedPath)
|
|
{
|
|
foreach (DataFileDependency dep in m_children)
|
|
{
|
|
if (dep.Path == _normalizedPath)
|
|
return dep;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a parent dependency by key
|
|
/// <returns>null if not found</returns>
|
|
/// </summary>
|
|
public DataFileDependency GetParent(DataFileKey _key)
|
|
{
|
|
foreach (DataFileDependency dep in m_parents)
|
|
{
|
|
if (dep.Key == _key)
|
|
return dep;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a parent dependency by path
|
|
/// <returns>null if not found</returns>
|
|
/// </summary>
|
|
public DataFileDependency GetParent(string _normalizedPath)
|
|
{
|
|
foreach (DataFileDependency dep in m_parents)
|
|
{
|
|
if (dep.Path == _normalizedPath)
|
|
return dep;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the children by platform
|
|
/// <returns>The list of dependencies on this platform</returns>
|
|
/// </summary>
|
|
public IEnumerable<DataFileDependency> GetChildrenByPlatform(Platform _platform)
|
|
{
|
|
List<DataFileDependency> result = new List<DataFileDependency>();
|
|
foreach (DataFileDependency dep in m_children)
|
|
{
|
|
if (dep.Platforms.Contains(_platform))
|
|
result.Add(dep);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a child dependency on a platform
|
|
/// </summary>
|
|
public void AddChild(string _normalizedPath, DataFileKey _key, HashSet<Platform> _platforms, bool _missing)
|
|
{
|
|
DataFileDependency dependency = _key.IsInvalid() ? GetChild(_normalizedPath) : GetChild(_key);
|
|
if (dependency != null)
|
|
{
|
|
dependency.Platforms.UnionWith(_platforms);
|
|
dependency.Missing = _missing;
|
|
}
|
|
else
|
|
{
|
|
DataFileDependency newDependency = new DataFileDependency() { Path = _normalizedPath, Key = _key, Missing = _missing };
|
|
newDependency.Platforms.UnionWith(_platforms);
|
|
m_children.Add(newDependency);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a parent dependency on the given platforms
|
|
/// </summary>
|
|
public void AddParent(string _normalizedPath, DataFileKey _key, HashSet<Platform> _platforms)
|
|
{
|
|
DataFileDependency dependency = _key.IsInvalid() ? GetParent(_normalizedPath) : GetParent(_key);
|
|
if (dependency != null)
|
|
{
|
|
dependency.Platforms.UnionWith(_platforms);
|
|
}
|
|
else
|
|
{
|
|
RemoveWarning(DataFileError.Codes.Orphan);
|
|
|
|
DataFileDependency newDependency = new DataFileDependency() { Path = _normalizedPath, Key = _key };
|
|
newDependency.Platforms.UnionWith(_platforms);
|
|
m_parents.Add(newDependency);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove a child dependency on the given platforms
|
|
/// </summary>
|
|
public void RemoveChild(DataFileKey _key, HashSet<Platform> _platforms)
|
|
{
|
|
DataFileDependency childDependency = GetChild(_key);
|
|
if (childDependency != null)
|
|
RemoveChild(childDependency, _platforms);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove a child dependency on the given platforms
|
|
/// </summary>
|
|
public void RemoveChild(DataFileDependency _dependency, HashSet<Platform> _platforms)
|
|
{
|
|
_dependency.Platforms.ExceptWith(_platforms);
|
|
if (_dependency.Platforms.Count == 0)
|
|
m_children.Remove(_dependency);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove a parent dependency on the given platforms
|
|
/// </summary>
|
|
public void RemoveParent(DataFileKey _key, HashSet<Platform> _platforms)
|
|
{
|
|
DataFileDependency parentDependency = GetParent(_key);
|
|
if (parentDependency != null)
|
|
RemoveParent(parentDependency, _platforms);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove a parent dependency on the given platforms
|
|
/// </summary>
|
|
public void RemoveParent(DataFileDependency _dependency, HashSet<Platform> _platforms)
|
|
{
|
|
_dependency.Platforms.ExceptWith(_platforms);
|
|
if (_dependency.Platforms.Count == 0)
|
|
{
|
|
m_parents.Remove(_dependency);
|
|
if (m_parents.Count == 0)
|
|
{
|
|
string extension = UAF.PathUtils.GetExtension(NormalizedPath);
|
|
if (!OrphanDetectionOperation.SkippedExtensions.Contains(extension))
|
|
{
|
|
AddWarning(DataFileError.Codes.Orphan);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A child file has been deleted
|
|
/// </summary>
|
|
public void OnChildDeleted(DataFileKey _childKey)
|
|
{
|
|
DataFileDependency dependency = GetChild(_childKey);
|
|
if (dependency != null)
|
|
{
|
|
dependency.Missing = true;
|
|
if (!HasMissingDependencies)
|
|
AddError(DataFileError.Codes.UnresolvedDependencies);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|