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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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.
20 // Copyright (c) 2007 Novell, Inc. (http://www.novell.com)
23 // Chris Toshok (toshok@ximian.com)
24 // Alan McGovern (amcgovern@novell.com)
28 using System
.Collections
.Generic
;
32 namespace System
.IO
.Packaging
{
34 public abstract class PackagePart
38 internal bool IsRelationship { get; set; }
41 Dictionary
<string, PackageRelationship
> relationships
;
42 PackageRelationshipCollection relationshipsCollection
= new PackageRelationshipCollection ();
44 Dictionary
<string, PackageRelationship
> Relationships
{
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
);
56 Stream PartStream { get; set; }
58 internal Uri RelationshipsPartUri
{
62 protected PackagePart (Package package
, Uri partUri
)
63 : this(package
, partUri
, null)
68 protected internal PackagePart (Package package
, Uri partUri
, string contentType
)
69 : this (package
, partUri
, contentType
, CompressionOption
.Normal
)
74 protected internal PackagePart (Package package
, Uri partUri
, string contentType
, CompressionOption compressionOption
)
76 Check
.Package (package
);
77 Check
.PartUri (partUri
);
78 Check
.ContentTypeIsValid (contentType
);
82 ContentType
= contentType
;
83 CompressionOption
= compressionOption
;
84 RelationshipsPartUri
= PackUriHelper
.GetRelationshipPartUri(Uri
);
87 public CompressionOption CompressionOption
{
91 public string ContentType
{
93 if (contentType
== null && (contentType
= GetContentTypeCore()) == null)
94 throw new NotSupportedException ("If contentType is not supplied in the constructor, GetContentTypeCore must be overridden");
102 public Package Package
{
110 private void CheckIsRelationship ()
113 throw new InvalidOperationException ("A relationship cannot have relationships to other parts");
116 public PackageRelationship
CreateRelationship (Uri targetUri
, TargetMode targetMode
, string relationshipType
)
118 return CreateRelationship (targetUri
, targetMode
, relationshipType
, null);
121 public PackageRelationship
CreateRelationship (Uri targetUri
, TargetMode targetMode
, string relationshipType
, string id
)
123 return CreateRelationship (targetUri
, targetMode
, relationshipType
, id
, false);
126 private PackageRelationship
CreateRelationship (Uri targetUri
, TargetMode targetMode
, string relationshipType
, string id
, bool loading
)
128 Package
.CheckIsReadOnly ();
129 Check
.TargetUri (targetUri
);
130 Check
.RelationshipTypeIsValid (relationshipType
);
131 Check
.IdIsValid (id
);
136 if (Relationships
.ContainsKey (id
))
137 throw new XmlException ("A relationship with this ID already exists");
139 PackageRelationship r
= new PackageRelationship (id
, Package
, relationshipType
, Uri
, targetMode
, targetUri
);
140 Relationships
.Add (r
.Id
, r
);
143 WriteRelationships ();
147 public void DeleteRelationship (string id
)
149 Package
.CheckIsReadOnly ();
150 CheckIsRelationship ();
151 Relationships
.Remove (id
);
152 WriteRelationships ();
155 void LoadRelationships (Dictionary
<string, PackageRelationship
> relationships
, Stream stream
)
157 XmlDocument doc
= new XmlDocument ();
159 XmlNamespaceManager manager
= new XmlNamespaceManager (doc
.NameTable
);
160 manager
.AddNamespace ("rel", Package
.RelationshipNamespace
);
162 foreach (XmlNode node
in doc
.SelectNodes ("/rel:Relationships/*", manager
))
164 TargetMode mode
= TargetMode
.Internal
;
165 if (node
.Attributes
["TargetMode"] != null)
166 mode
= (TargetMode
) Enum
.Parse (typeof(TargetMode
), node
.Attributes
["TargetMode"].Value
);
168 CreateRelationship (new Uri ("/" + node
.Attributes
["Target"].Value
.ToString(), UriKind
.Relative
),
170 node
.Attributes
["Type"].Value
.ToString (),
171 node
.Attributes
["Id"].Value
.ToString (),
176 public bool RelationshipExists (string id
)
178 CheckIsRelationship ();
179 return Relationships
.ContainsKey (id
);
182 public PackageRelationship
GetRelationship (string id
)
184 CheckIsRelationship ();
185 return Relationships
[id
];
188 public PackageRelationshipCollection
GetRelationships ()
190 CheckIsRelationship ();
191 relationshipsCollection
.Relationships
.Clear ();
192 relationshipsCollection
.Relationships
.AddRange (Relationships
.Values
);
193 return relationshipsCollection
;
196 public PackageRelationshipCollection
GetRelationshipsByType (string relationshipType
)
198 CheckIsRelationship ();
199 PackageRelationshipCollection collection
= new PackageRelationshipCollection ();
200 foreach (PackageRelationship r
in Relationships
.Values
)
201 if (r
.RelationshipType
== relationshipType
)
202 collection
.Relationships
.Add (r
);
207 public Stream
GetStream ()
209 return GetStream (Package
.FileOpenAccess
== FileAccess
.Read
&& !IsRelationship
? FileMode
.Open
: FileMode
.OpenOrCreate
);
212 public Stream
GetStream (FileMode mode
)
214 return GetStream (mode
, IsRelationship
? FileAccess
.ReadWrite
: Package
.FileOpenAccess
);
217 public Stream
GetStream (FileMode mode
, FileAccess access
)
219 bool notAllowed
= mode
== FileMode
.Append
|| mode
== FileMode
.CreateNew
|| mode
== FileMode
.Truncate
;
220 if (access
!= FileAccess
.Read
&& notAllowed
)
221 throw new ArgumentException (string.Format (string.Format ("FileMode '{0}' not supported", mode
)));
223 if (access
== FileAccess
.Read
&& (notAllowed
|| mode
== FileMode
.Create
))
224 throw new IOException (string.Format ("FileMode '{0}' not allowed on a readonly stream", mode
));
226 return GetStreamCore (mode
, access
);
229 protected abstract Stream
GetStreamCore (FileMode mode
, FileAccess access
);
231 protected virtual string GetContentTypeCore ()
236 private string NextId ()
240 string s
= relationshipId
.ToString ();
241 if (!RelationshipExists (s
))
247 void WriteRelationships ()
249 bool exists
= Package
.PartExists (RelationshipsPartUri
);
250 if (exists
&& Relationships
.Count
== 0)
252 Package
.DeletePart (RelationshipsPartUri
);
258 PackagePart part
= Package
.CreatePart (RelationshipsPartUri
, Package
.RelationshipContentType
);
259 part
.IsRelationship
= true;
261 using (Stream s
= Package
.GetPart (RelationshipsPartUri
).GetStream ())
262 Package
.WriteRelationships (Relationships
, s
);