#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; } } }