Merge pull request #1936 from esdrubal/DotNetRelativeOrAbsolute
[mono.git] / mcs / class / WindowsBase / System.IO.Packaging / PackagePart.cs
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 (toshok@ximian.com)
24 //  Alan McGovern (amcgovern@novell.com)
25 //
26
27 using System;
28 using System.Collections.Generic;
29 using System.IO;
30 using System.Xml;
31
32 namespace System.IO.Packaging {
33
34         public abstract class PackagePart
35         {
36                 string contentType;
37                 
38                 internal bool IsRelationship { get; set; }
39                 
40                 int relationshipId;
41                 Dictionary<string, PackageRelationship> relationships;
42                 PackageRelationshipCollection relationshipsCollection = new PackageRelationshipCollection ();
43                 
44                 Dictionary<string, PackageRelationship> Relationships {
45                         get {
46                                 if (relationships == null) {
47                                         relationships = new Dictionary<string, PackageRelationship> (StringComparer.OrdinalIgnoreCase);
48                                         if (Package.PartExists (RelationshipsPartUri))
49                                                 using (Stream s = Package.GetPart (RelationshipsPartUri).GetStream ())
50                                                         LoadRelationships (relationships, s);
51                                 }
52
53                                 return relationships;
54                         }
55                 }
56                 Stream PartStream { get; set;  }
57
58                 internal Uri RelationshipsPartUri {
59                         get; set;
60                 }
61                 
62                 protected PackagePart (Package package, Uri partUri)
63                         : this(package, partUri, null)
64                 {
65                         
66                 }
67
68                 protected internal PackagePart (Package package, Uri partUri, string contentType)
69                         : this (package, partUri, contentType, CompressionOption.Normal)
70                 {
71                         
72                 }
73
74                 protected internal PackagePart (Package package, Uri partUri, string contentType, CompressionOption compressionOption)
75                 {
76                         Check.Package (package);
77                         Check.PartUri (partUri);
78                         Check.ContentTypeIsValid (contentType);
79
80                         Package = package;
81                         Uri = partUri;
82                         ContentType = contentType;
83                         CompressionOption = compressionOption;
84                         RelationshipsPartUri = PackUriHelper.GetRelationshipPartUri(Uri);
85                 }
86
87                 public CompressionOption CompressionOption {
88                         get; private set;
89                 }
90
91                 public string ContentType {
92                         get {
93                                 if (contentType == null && (contentType = GetContentTypeCore()) == null)
94                                         throw new NotSupportedException ("If contentType is not supplied in the constructor, GetContentTypeCore must be overridden");
95                                 return contentType;
96                         }
97                         private set {
98                                 contentType = value;
99                         }
100                 }
101
102                 public Package Package {
103                         get; internal set;
104                 }
105
106                 public Uri Uri {
107                         get; private set;
108                 }
109
110                 private void CheckIsRelationship ()
111                 {
112                         if (IsRelationship)
113                                 throw new InvalidOperationException ("A relationship cannot have relationships to other parts"); 
114                 }
115
116                 public PackageRelationship CreateRelationship (Uri targetUri, TargetMode targetMode, string relationshipType)
117                 {
118                         return CreateRelationship (targetUri, targetMode, relationshipType, null);
119                 }
120
121                 public PackageRelationship CreateRelationship (Uri targetUri, TargetMode targetMode, string relationshipType, string id)
122                 {
123                         return CreateRelationship (targetUri, targetMode, relationshipType, id, false);
124                 }
125
126                 private PackageRelationship CreateRelationship (Uri targetUri, TargetMode targetMode, string relationshipType, string id, bool loading)
127                 {
128                         if (!loading)
129                                 Package.CheckIsReadOnly ();
130                         Check.TargetUri (targetUri);
131                         Check.RelationshipTypeIsValid (relationshipType);
132                         Check.IdIsValid (id);
133
134                         if (id == null)
135                                 id = NextId ();
136
137                         if (Relationships.ContainsKey (id))
138                                 throw new XmlException ("A relationship with this ID already exists");
139                         
140                         PackageRelationship r = new PackageRelationship (id, Package, relationshipType, Uri, targetMode, targetUri);
141                         Relationships.Add (r.Id, r);
142
143                         if (!loading)
144                                 WriteRelationships ();
145                         return r;
146                 }
147
148                 public void DeleteRelationship (string id)
149                 {
150                         Package.CheckIsReadOnly ();
151                         CheckIsRelationship ();
152                         Relationships.Remove (id);
153                         WriteRelationships ();
154                 }
155
156                 void LoadRelationships (Dictionary<string, PackageRelationship> relationships, Stream stream)
157                 {
158                         XmlDocument doc = new XmlDocument ();
159                         doc.Load (stream);
160                         XmlNamespaceManager manager = new XmlNamespaceManager (doc.NameTable);
161                         manager.AddNamespace ("rel", Package.RelationshipNamespace);
162
163                         foreach (XmlNode node in doc.SelectNodes ("/rel:Relationships/*", manager))
164                         {
165                                 TargetMode mode = TargetMode.Internal;
166                                 if (node.Attributes["TargetMode"] != null)
167                                         mode = (TargetMode) Enum.Parse (typeof(TargetMode), node.Attributes ["TargetMode"].Value);
168                                 
169                                 CreateRelationship (new Uri (node.Attributes["Target"].Value.ToString(), UriKind.RelativeOrAbsolute),
170                                                     mode,
171                                                     node.Attributes["Type"].Value.ToString (),
172                                                     node.Attributes["Id"].Value.ToString (),
173                                                     true);
174                         }
175                 }
176
177                 public bool RelationshipExists (string id)
178                 {
179                         CheckIsRelationship ();
180                         return Relationships.ContainsKey (id);
181                 }
182
183                 public PackageRelationship GetRelationship (string id)
184                 {
185                         CheckIsRelationship ();
186                         return Relationships [id];
187                 }
188
189                 public PackageRelationshipCollection GetRelationships ()
190                 {
191                         CheckIsRelationship ();
192                         relationshipsCollection.Relationships.Clear ();
193                         relationshipsCollection.Relationships.AddRange (Relationships.Values);
194                         return relationshipsCollection;
195                 }
196
197                 public PackageRelationshipCollection GetRelationshipsByType (string relationshipType)
198                 {
199                         CheckIsRelationship ();
200                         PackageRelationshipCollection collection = new PackageRelationshipCollection ();
201                         foreach (PackageRelationship r in Relationships.Values)
202                                 if (r.RelationshipType == relationshipType)
203                                         collection.Relationships.Add (r);
204                         
205                         return collection;
206                 }
207
208                 public Stream GetStream ()
209                 {
210                         return GetStream (Package.FileOpenAccess == FileAccess.Read && !IsRelationship ? FileMode.Open : FileMode.OpenOrCreate);
211                 }
212
213                 public Stream GetStream (FileMode mode)
214                 {
215                         return GetStream (mode, IsRelationship ? FileAccess.ReadWrite : Package.FileOpenAccess);
216                 }
217
218                 public Stream GetStream (FileMode mode, FileAccess access)
219                 {
220                         bool notAllowed = mode == FileMode.Append || mode == FileMode.CreateNew || mode == FileMode.Truncate;
221                         if (access != FileAccess.Read && notAllowed)
222                                 throw new ArgumentException (string.Format (string.Format ("FileMode '{0}' not supported", mode)));
223
224                         if (access == FileAccess.Read && (notAllowed || mode == FileMode.Create))
225                                 throw new IOException (string.Format ("FileMode '{0}' not allowed on a readonly stream", mode));
226                         
227                         return GetStreamCore (mode, access);
228                 }
229
230                 protected abstract Stream GetStreamCore (FileMode mode, FileAccess access);
231
232                 protected virtual string GetContentTypeCore ()
233                 {
234                         return null;
235                 }
236
237                 private string NextId ()
238                 {
239                         while (true)
240                         {
241                                 string s = "Re" + relationshipId.ToString ();
242                                 if (!RelationshipExists (s))
243                                         return s;
244                                 relationshipId ++;
245                         }
246                 }
247
248                 void WriteRelationships ()
249                 {
250                         bool exists = Package.PartExists (RelationshipsPartUri);
251                         if (exists && Relationships.Count == 0)
252                         {
253                                 Package.DeletePart (RelationshipsPartUri);
254                                 return;
255                         }
256                         
257                         if (!exists)
258                         {
259                                 PackagePart part = Package.CreatePart (RelationshipsPartUri, Package.RelationshipContentType);
260                                 part.IsRelationship = true;
261                         }
262                         using (Stream s = Package.GetPart (RelationshipsPartUri).GetStream ())
263                                 Package.WriteRelationships (Relationships, s);
264                 }
265         }
266 }