#region License
// Copyright (c) 2006-2008, ClearCanvas Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of ClearCanvas Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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.
#endregion
using System;
using System.Collections.Generic;
using System.Reflection;
using ClearCanvas.Common.Utilities;
namespace Nullstack.ClearCanvasEx.DevTools.Common
{
///
/// Represents a path.
///
///
/// Instances of this class are immutable.
/// This is actually a copy of the class due to minor changes between this version and the one in the 1.2 SDK.
///
internal class CCPath
{
///
/// Gets the empty object.
///
public static CCPath Empty = new CCPath(new PathSegment[] {});
private const string SEPARATOR = "/";
private const string ESCAPED_SEPARATOR = "//";
private const string TEMP = "__$:$__";
private readonly List _segments;
///
/// Creates a new from the specified path string, resolving
/// resource keys in the path string using the specified .
///
///
/// The path string may contain any combination of literals and resource keys. Localization
/// will be attempted on each path segment by treating the segment as a resource key,
/// and path segments that fail as resource keys will be treated as literals.
///
/// The path string to parse.
/// The to use for localization.
public CCPath(string pathString, IResourceResolver resolver)
: this(ParsePathString(pathString, resolver)) {}
///
/// Creates a new from the specified path string, with no resource resolver.
///
///
/// The path string must only contain literals, because there is no resource resolver to perform
/// localization.
///
///
public CCPath(string pathString)
: this(ParsePathString(pathString, new ResourceResolver(new Assembly[] {}))) {}
///
/// Internal constructor.
///
///
private CCPath(IEnumerable segments)
{
_segments = new List(segments);
}
///
/// Gets the individual segments contained in this path.
///
public IList Segments
{
get { return _segments.AsReadOnly(); }
}
///
/// The final segment in this path, or null if this path is empty.
///
public PathSegment LastSegment
{
get { return CollectionUtils.LastElement(_segments); }
}
///
/// Gets a new object representing the specified sub-path.
///
public CCPath SubPath(int start, int count)
{
return new CCPath(_segments.GetRange(start, count));
}
///
/// Gets the full path string, localized.
///
public string LocalizedPath
{
get
{
return StringUtilities.Combine(_segments, SEPARATOR,
delegate(PathSegment s) { return s.LocalizedText.Replace(SEPARATOR, ESCAPED_SEPARATOR); }, false);
}
}
///
/// Returns a new object obtained by appending path
/// to this path.
///
///
///
public CCPath Append(CCPath other)
{
List combined = new List(_segments);
combined.AddRange(other.Segments);
return new CCPath(combined);
}
///
/// Converts this path back to a string.
///
public override string ToString()
{
return StringUtilities.Combine(_segments, SEPARATOR,
delegate(PathSegment s) { return s.ResourceKey.Replace(SEPARATOR, ESCAPED_SEPARATOR); }, false);
}
///
/// Returns a new object representing the longest common path
/// between this object and .
///
///
///
public CCPath GetCommonPath(CCPath other)
{
List commonPath = new List();
for (int i = 0; i < Math.Min(_segments.Count, other.Segments.Count); i++)
{
if (_segments[i] == other.Segments[i])
commonPath.Add(_segments[i]);
else
break; // must break as soon as paths differ
}
return new CCPath(commonPath);
}
///
/// Returns true if this path starts with .
///
///
///
public bool StartsWith(CCPath other)
{
// if other path is longer, then this path can't possibly "start with" it
if (other.Segments.Count > _segments.Count)
return false;
// check that segments are equal up to length of other path
for (int i = 0; i < other.Segments.Count; i++)
{
if (!Equals(_segments[i], other.Segments[i]))
return false;
}
return true;
}
private static PathSegment[] ParsePathString(string pathString, IResourceResolver resolver)
{
// replace any escaped separators with some weird temporary string
pathString = StringUtilities.EmptyIfNull(pathString).Replace(ESCAPED_SEPARATOR, TEMP);
// split string by separator
string[] parts = pathString.Split(new string[] {SEPARATOR}, StringSplitOptions.None);
int n = parts.Length;
PathSegment[] segments = new PathSegment[n];
for (int i = 0; i < n; i++)
{
// replace the temp string with the unescaped separator
parts[i] = parts[i].Replace(TEMP, SEPARATOR);
segments[i] = new PathSegment(parts[i], resolver != null ? resolver.LocalizeString(parts[i]) : parts[i]);
}
return segments;
}
}
///
/// Represents a single segment of a .
///
///
/// This is actually a copy of the class due to minor changes between this version and the one in the 1.2 SDK.
///
internal class PathSegment : IEquatable
{
private readonly string _key;
private readonly string _localized;
///
/// Internal constructor.
///
/// The resource key or unlocalized path segment string.
/// The localized path segment string.
internal PathSegment(string key, string localized)
{
_key = key;
_localized = localized;
}
///
/// Gets the resource key or unlocalized text.
///
public string ResourceKey
{
get { return _key; }
}
///
/// Gets the localized text.
///
public string LocalizedText
{
get { return _localized; }
}
///
///
///
///
///
public static bool operator !=(PathSegment pathSegment1, PathSegment pathSegment2)
{
return !Equals(pathSegment1, pathSegment2);
}
///
///
///
///
///
public static bool operator ==(PathSegment pathSegment1, PathSegment pathSegment2)
{
return Equals(pathSegment1, pathSegment2);
}
///
/// Gets whether or not is equal to this object.
///
public bool Equals(PathSegment pathSegment)
{
if (pathSegment == null) return false;
return Equals(_localized, pathSegment._localized);
}
///
/// Gets whether or not is equal to this object.
///
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj)) return true;
return Equals(obj as PathSegment);
}
///
/// Gets a hash code.
///
public override int GetHashCode()
{
return _localized != null ? _localized.GetHashCode() : 0;
}
}
}