Convert all url usages to use the Uri class instead of raw strings.
[pithos-ms-client] / trunk / Pithos.Interfaces / ObjectInfo.cs
index 1675be1..03aecc1 100644 (file)
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-
-namespace Pithos.Interfaces
-{
-    public class ObjectInfo
-    {
-        public string Name { get; set; }
-        public string Hash { get; set; }
-        public long Bytes { get; set; }
-        public string Content_Type { get; set; }
-        public DateTime Last_Modified { get; set; }
-
-        private Dictionary<string, string> _tags=new Dictionary<string, string>();
-        public Dictionary<string, string> Tags
-        {
-            get { return _tags; }
-            set { _tags = value; }
-        }
-
-        private Dictionary<string, string> _extensions=new Dictionary<string, string>();
-        public Dictionary<string, string> Extensions
-        {
-            get { return _extensions; }
-            set
-            {
-                _extensions = value;
-                ExtractKnownExtensions();
-            }
-        }
-
-        public long? Version { get; set; }
-        public DateTime? VersionTimeStamp { get; set; }
-
-        public Stream Stream { get; set; }
-
-
-        private void ExtractKnownExtensions()
-        {
-            Version=GetLong("X-Object-Version");
-            VersionTimeStamp = GetTimestamp("X-Object-Version-TimeStamp");
-        }
-
-        private long? GetLong(string name)
-        {
-            string version;
-            long value;
-            if (_extensions.TryGetValue(name, out version) && long.TryParse(version, out value))
-            {
-                return value;
-            }
-            return null;
-        }
-
-        private DateTime? GetTimestamp(string name)
-        {
-            string version;
-            DateTime value;
-            if (_extensions.TryGetValue(name, out version) && 
-                DateTime.TryParse(version,CultureInfo.InvariantCulture,DateTimeStyles.AdjustToUniversal, out value))
-            {
-                return value;
-            }
-            return null;
-        }
-
-
-        public static ObjectInfo Empty = new ObjectInfo
-        {
-            Name = String.Empty,
-            Hash = String.Empty,
-            Bytes = 0,
-            Content_Type = String.Empty,
-            Last_Modified = DateTime.MinValue
-        };
-    }
+#region\r
+/* -----------------------------------------------------------------------\r
+ * <copyright file="ObjectInfo.cs" company="GRNet">\r
+ * \r
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or\r
+ * without modification, are permitted provided that the following\r
+ * conditions are met:\r
+ *\r
+ *   1. Redistributions of source code must retain the above\r
+ *      copyright notice, this list of conditions and the following\r
+ *      disclaimer.\r
+ *\r
+ *   2. Redistributions in binary form must reproduce the above\r
+ *      copyright notice, this list of conditions and the following\r
+ *      disclaimer in the documentation and/or other materials\r
+ *      provided with the distribution.\r
+ *\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS\r
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR\r
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\r
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * The views and conclusions contained in the software and\r
+ * documentation are those of the authors and should not be\r
+ * interpreted as representing official policies, either expressed\r
+ * or implied, of GRNET S.A.\r
+ * </copyright>\r
+ * -----------------------------------------------------------------------\r
+ */\r
+#endregion\r
+using System;\r
+using System.Collections.Generic;\r
+using System.Diagnostics;\r
+using System.Diagnostics.Contracts;\r
+using System.Dynamic;\r
+using System.Globalization;\r
+using System.IO;\r
+using System.Linq;\r
+using System.Text;\r
+using Newtonsoft.Json;\r
+\r
+namespace Pithos.Interfaces\r
+{\r
+    [DebuggerDisplay("Name {Name}")]\r
+    public class ObjectInfo//:DynamicObject \r
+    {\r
+        public const string CONTENT_TYPE_DIRECTORY = @"application/directory";\r
+        public const string CONTENT_TYPE_FOLDER = @"application/folder";\r
+        private readonly List<string> _knownContainers= new List<string>{"trash"};\r
+\r
+        [JsonConverter(typeof(RelativeUriConverter))]\r
+        public Uri Name\r
+        {\r
+            get { return _name; }\r
+            set\r
+            {\r
+                if (value != null && value.IsAbsoluteUri)\r
+                    throw new ArgumentException("Must be relative Uri", "Name");\r
+                _name = value;\r
+            }\r
+        }\r
+\r
+        [JsonProperty("hash")]\r
+        public string ETag { get; set; }\r
+\r
+        //public string Hash { get; set; }\r
+\r
+/*\r
+        public string X_Object_Hash { get { return Hash; } set { Hash = value; } }\r
+*/\r
+\r
+        public string X_Object_Hash { get; set; }\r
+\r
+        [JsonProperty("x_object_uuid")]\r
+        public string UUID { get; set; }\r
+\r
+        public long Bytes { get; set; }\r
+        public string Content_Type { get; set; }\r
+        public DateTime Last_Modified { get; set; }\r
+\r
+        private Dictionary<string, string> _tags=new Dictionary<string, string>();\r
+        public Dictionary<string, string> Tags\r
+        {\r
+            get { return _tags; }\r
+            set { _tags = value; }\r
+        }\r
+\r
+        private Dictionary<string, string> _extensions=new Dictionary<string, string>();\r
+        public Dictionary<string, string> Extensions\r
+        {\r
+            get { return _extensions; }\r
+            set\r
+            {\r
+                _extensions = value;\r
+                ExtractKnownExtensions();\r
+            }\r
+        }\r
+        \r
+        \r
+        private Dictionary<string, string> _permissions=new Dictionary<string, string>();\r
+        [JsonProperty("x_object_sharing")]\r
+        [JsonConverter(typeof(PermissionConverter))]\r
+        public Dictionary<string, string> Permissions\r
+        {\r
+            get { return _permissions; }\r
+            set\r
+            {\r
+                _permissions = value;                \r
+            }\r
+        }\r
+\r
+        /// <summary>\r
+        /// Version number\r
+        /// </summary>\r
+        [JsonProperty("x_object_version")]\r
+        public long? Version { get; set; }\r
+\r
+\r
+        /// <summary>\r
+        /// Shared object permissions can be Read or Write\r
+        /// </summary>\r
+        [JsonProperty("x_object_allowed_to")]\r
+        public string AllowedTo { get; set; }\r
+\r
+\r
+        /// <summary>\r
+        /// Version timestamp\r
+        /// </summary>\r
+        [JsonProperty("X_Object_Version_Timestamp"), JsonConverter(typeof(PithosDateTimeConverter))]\r
+        public DateTime? VersionTimestamp { get; set; }\r
+\r
+        [JsonProperty("X_Object_Modified_By")]\r
+        public string ModifiedBy { get; set; }\r
+\r
+\r
+        public Stream Stream { get; set; }\r
+\r
+\r
+        public Uri StorageUri { get; set; }\r
+\r
+        public string Account { get; set; }\r
+\r
+        [JsonConverter(typeof(RelativeUriConverter))]\r
+        public Uri Container\r
+        {\r
+            get { return _container; }\r
+            set\r
+            {\r
+                if (value != null && value.IsAbsoluteUri)\r
+                    throw new ArgumentException("Must be relative Uri", "Container");\r
+                _container = value;\r
+            }\r
+        }\r
+\r
+        public Uri Uri\r
+        {\r
+            get\r
+            {\r
+                var relativeUrl=String.Format("{0}/{1}/{2}",Account, Container,Name);\r
+                return StorageUri==null \r
+                    ? new Uri(relativeUrl,UriKind.Relative) \r
+                    : StorageUri.Combine(relativeUrl);\r
+            }\r
+        }\r
+\r
+        public string ContendDisposition { get; set; }\r
+\r
+        public string ContentEncoding { get; set; }\r
+\r
+        public string Manifest { get; set; }\r
+        \r
+        public bool IsPublic\r
+        {\r
+            get { return !String.IsNullOrWhiteSpace(PublicUrl); }\r
+            set\r
+            {\r
+                if (!value)\r
+                    PublicUrl = null;\r
+                else if (String.IsNullOrWhiteSpace(PublicUrl))\r
+                    PublicUrl="true";                \r
+            }\r
+        }\r
+\r
+        [JsonProperty("X_Object_Public")]\r
+        public string PublicUrl { get; set; }\r
+\r
+        public string PreviousHash { get; set; }\r
+\r
+        public ObjectInfo()\r
+        {}\r
+\r
+        public ObjectInfo(string accountPath,string accountName,FileSystemInfo fileInfo)\r
+        {\r
+            if (String.IsNullOrWhiteSpace(accountPath))\r
+                throw new ArgumentNullException("accountPath");\r
+            if (string.IsNullOrWhiteSpace(accountName))\r
+                throw new ArgumentNullException("accountName");\r
+            if (fileInfo == null)\r
+                throw new ArgumentNullException("fileInfo");\r
+            Contract.EndContractBlock();\r
+\r
+            var relativeUrl = fileInfo.WithProperCapitalization().AsRelativeUrlTo(accountPath);\r
+            //The first part of the URL is the container\r
+            var parts = relativeUrl.ToString().Split(new[]{'/'}, 2);\r
+            var container = parts[0];\r
+            //The second is the file's url relative to the container\r
+            var fileUrl = parts[1];\r
+\r
+            Account = accountName;\r
+            Container = new Uri(container,UriKind.Relative);\r
+            Name = new Uri(fileUrl,UriKind.Relative); \r
+        }\r
+\r
+\r
+        private void ExtractKnownExtensions()\r
+        {\r
+            Version=GetLong(KnownExtensions.X_Object_Version);\r
+            VersionTimestamp = GetTimestamp(KnownExtensions.X_Object_Version_Timestamp);\r
+            ModifiedBy = GetString(KnownExtensions.X_Object_Modified_By);\r
+        }\r
+\r
+        private string GetString(string name)\r
+        {            \r
+            string value;\r
+            _extensions.TryGetValue(name, out value);\r
+            return value ;                        \r
+        }\r
+        \r
+        private long? GetLong(string name)\r
+        {\r
+            string version;\r
+            long value;\r
+            return _extensions.TryGetValue(name, out version) && long.TryParse(version, out value)\r
+                       ? (long?) value\r
+                       : null;\r
+        }\r
+\r
+        private DateTime? GetTimestamp(string name)\r
+        {\r
+            string version;\r
+            DateTime value;\r
+            if (_extensions.TryGetValue(name, out version) && \r
+                DateTime.TryParse(version,CultureInfo.InvariantCulture,DateTimeStyles.AdjustToUniversal, out value))\r
+            {\r
+                return value;\r
+            }\r
+            return null;\r
+        }\r
+\r
+\r
+        public static ObjectInfo Empty = new ObjectInfo\r
+        {\r
+            Name = _emptyUri,\r
+            Container = _emptyUri,\r
+            ETag= String.Empty,\r
+            X_Object_Hash= String.Empty,\r
+            Bytes = 0,\r
+            Content_Type = String.Empty,\r
+            Last_Modified = DateTime.MinValue,\r
+            Exists=false\r
+        };\r
+\r
+        private bool _exists=true;\r
+        private Uri _container;\r
+        private Uri _name;\r
+        private static Uri _emptyUri = new Uri(String.Empty, UriKind.Relative);\r
+\r
+        public bool Exists\r
+        {\r
+            get {\r
+                return _exists;\r
+            }\r
+            set {\r
+                _exists = value;\r
+            }\r
+        }\r
+\r
+\r
+        public string RelativeUrlToFilePath(string currentAccount)\r
+        {\r
+            if (Name==null)\r
+                throw new InvalidOperationException("Name can't be null");\r
+            if (String.IsNullOrWhiteSpace(currentAccount))\r
+                throw new ArgumentNullException("currentAccount");\r
+            Contract.EndContractBlock();\r
+\r
+            if (this == Empty)\r
+                return String.Empty;\r
+\r
+            var unescaped = Uri.UnescapeDataString(Name.ToString());\r
+            var path = unescaped.Replace("/", "\\");\r
+            var pathParts=new Stack<string>();\r
+            pathParts.Push(path);\r
+\r
+            var container = Container.NullSafe(c=>c.ToString());\r
+            if (!String.IsNullOrWhiteSpace(container) && !_knownContainers.Contains(container))\r
+                pathParts.Push(Uri.UnescapeDataString(container));\r
+            if (!currentAccount.Equals(Account, StringComparison.InvariantCultureIgnoreCase))\r
+            {\r
+                if (Account != null)\r
+                {\r
+                    pathParts.Push(Account);\r
+                    pathParts.Push(FolderConstants.OthersFolder);\r
+                }\r
+            }\r
+            var finalPath=Path.Combine(pathParts.ToArray());\r
+            return finalPath;\r
+        }\r
+\r
+/*\r
+        public override bool TrySetMember(SetMemberBinder binder, object value)\r
+        {\r
+            if (binder.Name.StartsWith("x_object_meta"))\r
+            {\r
+                Tags[binder.Name] = value.ToString();\r
+            }\r
+            return false;\r
+        }\r
+*/\r
+\r
+        public string GetPermissionString()\r
+        {\r
+            if (Permissions==null)\r
+                throw new InvalidOperationException();\r
+            Contract.EndContractBlock();\r
+\r
+            if (Permissions.Count == 0)\r
+                return "~";\r
+            var permissionBuilder = new StringBuilder();\r
+            var groupings = Permissions.GroupBy(pair => pair.Value.Trim(), pair => pair.Key.Trim());\r
+            foreach (var grouping in groupings)\r
+            {\r
+                permissionBuilder.AppendFormat("{0}={1};", grouping.Key, String.Join(",", grouping));\r
+            }\r
+            var permissions = Uri.EscapeDataString(permissionBuilder.ToString().Trim(';'));\r
+            return permissions;\r
+        }\r
+\r
+        public void SetPermissions(string permissions)\r
+        {\r
+            if (String.IsNullOrWhiteSpace(permissions))\r
+                return;\r
+            \r
+            Permissions = PermissionConverter.ParsePermissions(permissions);\r
+        }\r
+\r
+        //The previous values that correspond to a NoModification object\r
+        //have the same account, container and possibly the same folder\r
+        public bool CorrespondsTo(ObjectInfo other)\r
+        {\r
+            return other.Account == this.Account\r
+                   && other.Container == this.Container\r
+                   && (this.Name == null || other.Name.ToString().StartsWith(this.Name.ToString()));\r
+        }\r
+\r
+        public bool IsWritable(string account)\r
+        {\r
+            //If the Allowed To header has no value, try to determine the Share permissions\r
+            if (AllowedTo == null)\r
+            {\r
+                //If this file has no permissions defined, we can probably write it\r
+                //This case should occur only when the info comes from a listing of the user's own files\r
+                if (Permissions == null || Permissions.Count == 0)\r
+                    return true;\r
+                string perms;\r
+\r
+                //Do we have an explicit write share to this account?\r
+                return Permissions.TryGetValue(account, out perms) \r
+                    && perms.Equals("write",StringComparison.InvariantCultureIgnoreCase);\r
+                \r
+            }\r
+            //Otherwise return the permissions specified by AllowedTo\r
+            return AllowedTo.Equals("write",StringComparison.InvariantCultureIgnoreCase) ;\r
+        }\r
+\r
+        public ObjectInfo Previous { get; private set; }\r
+\r
+        public bool IsDirectory\r
+        {\r
+            get\r
+            {\r
+                if (Content_Type == null)\r
+                    return false;\r
+                if (Content_Type.StartsWith(CONTENT_TYPE_DIRECTORY,StringComparison.InvariantCultureIgnoreCase))\r
+                    return true;\r
+                if (Content_Type.StartsWith(CONTENT_TYPE_FOLDER,StringComparison.InvariantCultureIgnoreCase))\r
+                    return true;\r
+                return false;\r
+            }\r
+        }\r
+\r
+        public Uri AccountKey\r
+        {\r
+            get { return StorageUri.Combine("../" + Account); }\r
+        }\r
+\r
+        public ObjectInfo SetPrevious(ObjectInfo previous)\r
+        {            \r
+            Previous = previous;\r
+            PreviousHash = previous.X_Object_Hash;\r
+            return this;\r
+        }\r
+\r
+        public bool? IsShared\r
+        {\r
+            get\r
+            {\r
+                if (Uri == null || StorageUri == null)\r
+                    return null;\r
+                var isShared = !Uri.ToString().StartsWith(StorageUri.ToString());\r
+                return isShared;\r
+            }\r
+        }\r
+    }\r
 }
\ No newline at end of file