PackagePart.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. // Permission is hereby granted, free of charge, to any person obtaining
  2. // a copy of this software and associated documentation files (the
  3. // "Software"), to deal in the Software without restriction, including
  4. // without limitation the rights to use, copy, modify, merge, publish,
  5. // distribute, sublicense, and/or sell copies of the Software, and to
  6. // permit persons to whom the Software is furnished to do so, subject to
  7. // the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be
  10. // included in all copies or substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  13. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  14. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  15. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  16. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  17. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  18. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  19. //
  20. // Copyright (c) 2007 Novell, Inc. (http://www.novell.com)
  21. //
  22. // Authors:
  23. // Chris Toshok ([email protected])
  24. // Alan McGovern ([email protected])
  25. //
  26. using System;
  27. using System.Collections.Generic;
  28. using System.IO;
  29. using System.Xml;
  30. namespace System.IO.Packaging {
  31. public abstract class PackagePart
  32. {
  33. string contentType;
  34. internal bool IsRelationship { get; set; }
  35. int relationshipId;
  36. Dictionary<string, PackageRelationship> relationships;
  37. PackageRelationshipCollection relationshipsCollection = new PackageRelationshipCollection ();
  38. Dictionary<string, PackageRelationship> Relationships {
  39. get {
  40. if (relationships == null) {
  41. relationships = new Dictionary<string, PackageRelationship> (StringComparer.OrdinalIgnoreCase);
  42. if (Package.PartExists (RelationshipsPartUri))
  43. using (Stream s = Package.GetPart (RelationshipsPartUri).GetStream ())
  44. LoadRelationships (relationships, s);
  45. }
  46. return relationships;
  47. }
  48. }
  49. Stream PartStream { get; set; }
  50. internal Uri RelationshipsPartUri {
  51. get; set;
  52. }
  53. protected PackagePart (Package package, Uri partUri)
  54. : this(package, partUri, null)
  55. {
  56. }
  57. protected internal PackagePart (Package package, Uri partUri, string contentType)
  58. : this (package, partUri, contentType, CompressionOption.Normal)
  59. {
  60. }
  61. protected internal PackagePart (Package package, Uri partUri, string contentType, CompressionOption compressionOption)
  62. {
  63. Check.Package (package);
  64. Check.PartUri (partUri);
  65. Check.ContentTypeIsValid (contentType);
  66. Package = package;
  67. Uri = partUri;
  68. ContentType = contentType;
  69. CompressionOption = compressionOption;
  70. RelationshipsPartUri = PackUriHelper.GetRelationshipPartUri(Uri);
  71. }
  72. public CompressionOption CompressionOption {
  73. get; private set;
  74. }
  75. public string ContentType {
  76. get {
  77. if (contentType == null && (contentType = GetContentTypeCore()) == null)
  78. throw new NotSupportedException ("If contentType is not supplied in the constructor, GetContentTypeCore must be overridden");
  79. return contentType;
  80. }
  81. private set {
  82. contentType = value;
  83. }
  84. }
  85. public Package Package {
  86. get; internal set;
  87. }
  88. public Uri Uri {
  89. get; private set;
  90. }
  91. private void CheckIsRelationship ()
  92. {
  93. if (IsRelationship)
  94. throw new InvalidOperationException ("A relationship cannot have relationships to other parts");
  95. }
  96. public PackageRelationship CreateRelationship (Uri targetUri, TargetMode targetMode, string relationshipType)
  97. {
  98. return CreateRelationship (targetUri, targetMode, relationshipType, null);
  99. }
  100. public PackageRelationship CreateRelationship (Uri targetUri, TargetMode targetMode, string relationshipType, string id)
  101. {
  102. return CreateRelationship (targetUri, targetMode, relationshipType, id, false);
  103. }
  104. private PackageRelationship CreateRelationship (Uri targetUri, TargetMode targetMode, string relationshipType, string id, bool loading)
  105. {
  106. Package.CheckIsReadOnly ();
  107. Check.TargetUri (targetUri);
  108. Check.RelationshipTypeIsValid (relationshipType);
  109. Check.IdIsValid (id);
  110. if (id == null)
  111. id = NextId ();
  112. if (Relationships.ContainsKey (id))
  113. throw new XmlException ("A relationship with this ID already exists");
  114. PackageRelationship r = new PackageRelationship (id, Package, relationshipType, Uri, targetMode, targetUri);
  115. Relationships.Add (r.Id, r);
  116. if (!loading)
  117. WriteRelationships ();
  118. return r;
  119. }
  120. public void DeleteRelationship (string id)
  121. {
  122. Package.CheckIsReadOnly ();
  123. CheckIsRelationship ();
  124. Relationships.Remove (id);
  125. WriteRelationships ();
  126. }
  127. void LoadRelationships (Dictionary<string, PackageRelationship> relationships, Stream stream)
  128. {
  129. XmlDocument doc = new XmlDocument ();
  130. doc.Load (stream);
  131. XmlNamespaceManager manager = new XmlNamespaceManager (doc.NameTable);
  132. manager.AddNamespace ("rel", Package.RelationshipNamespace);
  133. foreach (XmlNode node in doc.SelectNodes ("/rel:Relationships/*", manager))
  134. {
  135. TargetMode mode = TargetMode.Internal;
  136. if (node.Attributes["TargetMode"] != null)
  137. mode = (TargetMode) Enum.Parse (typeof(TargetMode), node.Attributes ["TargetMode"].Value);
  138. CreateRelationship (new Uri ("/" + node.Attributes["Target"].Value.ToString(), UriKind.Relative),
  139. mode,
  140. node.Attributes["Type"].Value.ToString (),
  141. node.Attributes["Id"].Value.ToString (),
  142. true);
  143. }
  144. }
  145. public bool RelationshipExists (string id)
  146. {
  147. CheckIsRelationship ();
  148. return Relationships.ContainsKey (id);
  149. }
  150. public PackageRelationship GetRelationship (string id)
  151. {
  152. CheckIsRelationship ();
  153. return Relationships [id];
  154. }
  155. public PackageRelationshipCollection GetRelationships ()
  156. {
  157. CheckIsRelationship ();
  158. relationshipsCollection.Relationships.Clear ();
  159. relationshipsCollection.Relationships.AddRange (Relationships.Values);
  160. return relationshipsCollection;
  161. }
  162. public PackageRelationshipCollection GetRelationshipsByType (string relationshipType)
  163. {
  164. CheckIsRelationship ();
  165. PackageRelationshipCollection collection = new PackageRelationshipCollection ();
  166. foreach (PackageRelationship r in Relationships.Values)
  167. if (r.RelationshipType == relationshipType)
  168. collection.Relationships.Add (r);
  169. return collection;
  170. }
  171. public Stream GetStream ()
  172. {
  173. // FIXME: Need to find out what kind of access the streams are usually opened with
  174. // Appears to be read/write/seek == true.
  175. return GetStream (Package.FileOpenAccess == FileAccess.Read && !IsRelationship ? FileMode.Open : FileMode.OpenOrCreate);
  176. }
  177. public Stream GetStream (FileMode mode)
  178. {
  179. return GetStream (mode, IsRelationship ? FileAccess.ReadWrite : Package.FileOpenAccess);
  180. }
  181. public Stream GetStream (FileMode mode, FileAccess access)
  182. {
  183. return GetStreamCore (mode, access);
  184. }
  185. protected abstract Stream GetStreamCore (FileMode mode, FileAccess access);
  186. protected virtual string GetContentTypeCore ()
  187. {
  188. return null;
  189. }
  190. private string NextId ()
  191. {
  192. while (true)
  193. {
  194. string s = relationshipId.ToString ();
  195. if (!RelationshipExists (s))
  196. return s;
  197. relationshipId ++;
  198. }
  199. }
  200. void WriteRelationships ()
  201. {
  202. bool exists = Package.PartExists (RelationshipsPartUri);
  203. if (exists && Relationships.Count == 0)
  204. {
  205. Package.DeletePart (RelationshipsPartUri);
  206. return;
  207. }
  208. if (!exists)
  209. {
  210. PackagePart part = Package.CreatePart (RelationshipsPartUri, Package.RelationshipContentType);
  211. part.IsRelationship = true;
  212. }
  213. using (Stream s = Package.GetPart (RelationshipsPartUri).GetStream ())
  214. Package.WriteRelationships (Relationships, s);
  215. }
  216. }
  217. }