| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Xml;
- //using DocumentFormat.OpenXml.Packaging;
-
- namespace System.IO.Packaging
- {
- public abstract class Package : IDisposable
- {
- internal const string RelationshipContentType = "application/vnd.openxmlformats-package.relationships+xml";
- internal const string RelationshipNamespace = "http://schemas.openxmlformats.org/package/2006/relationships";
- internal static readonly Uri RelationshipUri = new Uri("/_rels/.rels", UriKind.Relative);
-
- private PackageProperties packageProperties;
- private PackagePartCollection partsCollection;
- private Dictionary<string, PackageRelationship> relationships;
- private PackageRelationshipCollection relationshipsCollection = new PackageRelationshipCollection();
- private Uri Uri = new Uri("/", UriKind.Relative);
-
- private bool Disposed { get; set; }
-
- public FileAccess FileOpenAccess { get; private set; }
-
- public PackageProperties PackageProperties
- {
- get
- {
- // PackageProperties are loaded when the relationships are loaded.
- // Therefore ensure we've already loaded the relationships.
- int count = Relationships.Count;
-
- if (packageProperties == null)
- {
- packageProperties = new PackagePropertiesPart();
- packageProperties.Package = this;
- }
- return packageProperties;
- }
- }
-
- private PackagePartCollection PartsCollection
- {
- get
- {
- if (partsCollection == null)
- {
- partsCollection = new PackagePartCollection();
- partsCollection.Parts.AddRange(GetPartsCore());
- }
- return partsCollection;
- }
- }
-
- private int RelationshipId { get; set; }
-
- private Dictionary<string, PackageRelationship> Relationships
- {
- get
- {
- if (relationships == null)
- {
- LoadRelationships();
- }
- return relationships;
- }
- }
-
- private bool Streaming { get; set; }
-
-
- protected Package(FileAccess openFileAccess)
- : this(openFileAccess, false)
- {
-
- }
-
- protected Package(FileAccess openFileAccess, bool streaming)
- {
- FileOpenAccess = openFileAccess;
- Streaming = streaming;
- }
-
-
- internal void CheckIsReadOnly()
- {
- if (FileOpenAccess == FileAccess.Read)
- throw new IOException("Operation not valid when package is read-only");
- }
-
- public void Close()
- {
- // FIXME: Ensure that Flush is actually called before dispose
- ((IDisposable) this).Dispose();
- }
-
- public PackagePart CreatePart(Uri partUri, string contentType)
- {
- return CreatePart(partUri, contentType, CompressionOption.NotCompressed);
- }
-
- public PackagePart CreatePart(Uri partUri, string contentType, CompressionOption compressionOption)
- {
- CheckIsReadOnly();
- Check.PartUri(partUri);
- Check.ContentTypeIsValid(contentType);
-
- if (PartExists(partUri))
- throw new InvalidOperationException("This partUri is already contained in the package");
-
- PackagePart part = CreatePartCore(partUri, contentType, compressionOption);
- PartsCollection.Parts.Add(part);
- return part;
- }
-
- protected abstract PackagePart CreatePartCore(Uri partUri, string contentType,
- CompressionOption compressionOption);
-
- public PackageRelationship CreateRelationship(Uri targetUri, TargetMode targetMode, string relationshipType)
- {
- return CreateRelationship(targetUri, targetMode, relationshipType, null);
- }
-
- public PackageRelationship CreateRelationship(Uri targetUri, TargetMode targetMode, string relationshipType,
- string id)
- {
- return CreateRelationship(targetUri, targetMode, relationshipType, id, false);
- }
-
- internal PackageRelationship CreateRelationship(Uri targetUri, TargetMode targetMode, string relationshipType,
- string id, bool loading)
- {
- if (!loading)
- CheckIsReadOnly();
-
- Check.TargetUri(targetUri);
- if (targetUri.IsAbsoluteUri && targetMode == TargetMode.Internal)
- throw new ArgumentException("TargetUri cannot be absolute for an internal relationship");
-
- Check.RelationshipTypeIsValid(relationshipType);
- Check.IdIsValid(id);
-
- if (id == null)
- id = NextId();
-
- PackageRelationship r = new PackageRelationship(id, this, relationshipType, Uri, targetMode, targetUri);
-
- if (!PartExists(RelationshipUri))
- CreatePartCore(RelationshipUri, RelationshipContentType, CompressionOption.NotCompressed).IsRelationship
- = true;
-
- Relationships.Add(r.Id, r);
- relationshipsCollection.Relationships.Add(r);
-
- if (!loading)
- {
- using (Stream s = GetPart(RelationshipUri).GetStream())
- WriteRelationships(relationships, s);
- }
-
- return r;
- }
-
-
- public void DeletePart(Uri partUri)
- {
- CheckIsReadOnly();
- Check.PartUri(partUri);
-
- PackagePart part = GetPart(partUri);
- if (part != null)
- {
- if (part.Package == null)
- throw new InvalidOperationException("This part has already been removed");
-
- // FIXME: MS.NET doesn't remove the relationship part
- // Instead it throws an exception if you try to use it
- if (PartExists(part.RelationshipsPartUri))
- GetPart(part.RelationshipsPartUri).Package = null;
-
- part.Package = null;
- DeletePartCore(partUri);
- PartsCollection.Parts.RemoveAll(p => p.Uri == partUri);
- }
- }
-
- protected abstract void DeletePartCore(Uri partUri);
-
- public void DeleteRelationship(string id)
- {
- Check.Id(id);
- CheckIsReadOnly();
-
- Relationships.Remove(id);
-
- relationshipsCollection.Relationships.RemoveAll(r => r.Id == id);
- if (Relationships.Count > 0)
- using (Stream s = GetPart(RelationshipUri).GetStream())
- WriteRelationships(relationships, s);
- else
- DeletePart(RelationshipUri);
- }
-
- void IDisposable.Dispose()
- {
- if (!Disposed)
- {
- Flush();
- Dispose(true);
- Disposed = true;
- }
- }
-
- protected virtual void Dispose(bool disposing)
- {
- // Nothing here needs to be disposed of
- }
-
- private bool flushing = false;
-
- public void Flush()
- {
- if (FileOpenAccess == FileAccess.Read || flushing)
- return;
-
- flushing = true;
-
- // Ensure we've loaded the relationships, parts and properties
- int count = Relationships.Count;
-
- if (packageProperties != null)
- packageProperties.Flush();
-
- FlushCore();
-
- flushing = false;
- }
-
- protected abstract void FlushCore();
-
- public PackagePart GetPart(Uri partUri)
- {
- Check.PartUri(partUri);
- return GetPartCore(partUri);
- }
-
- protected abstract PackagePart GetPartCore(Uri partUri);
-
- public PackagePartCollection GetParts()
- {
- PartsCollection.Parts.Clear();
- PartsCollection.Parts.AddRange(GetPartsCore());
- return PartsCollection;
- }
-
- protected abstract PackagePart[] GetPartsCore();
-
- public PackageRelationship GetRelationship(string id)
- {
- return Relationships[id];
- }
-
- public PackageRelationshipCollection GetRelationships()
- {
- // Ensure the Relationships dict is instantiated first.
- ICollection<PackageRelationship> rels = Relationships.Values;
- relationshipsCollection.Relationships.Clear();
- relationshipsCollection.Relationships.AddRange(rels);
- return relationshipsCollection;
- }
-
- public PackageRelationshipCollection GetRelationshipsByType(string relationshipType)
- {
- PackageRelationshipCollection collection = new PackageRelationshipCollection();
- foreach (PackageRelationship r in Relationships.Values)
- if (r.RelationshipType == relationshipType)
- collection.Relationships.Add(r);
-
- return collection;
- }
-
- private void LoadRelationships()
- {
- relationships = new Dictionary<string, PackageRelationship>();
-
- if (!PartExists(RelationshipUri))
- return;
-
- using (Stream stream = GetPart(RelationshipUri).GetStream())
- {
- XmlDocument doc = new XmlDocument();
- doc.Load(stream);
- XmlNamespaceManager manager = new XmlNamespaceManager(doc.NameTable);
- manager.AddNamespace("rel", RelationshipNamespace);
-
- foreach (XmlNode node in doc.SelectNodes("/rel:Relationships/*", manager))
- {
- TargetMode mode = TargetMode.Internal;
- if (node.Attributes["TargetMode"] != null)
- mode = (TargetMode) Enum.Parse(typeof (TargetMode), node.Attributes["TargetMode"].Value);
-
- Uri uri;
- try
- {
- uri = new Uri(node.Attributes["Target"].Value.ToString(), UriKind.Relative);
- }
- catch
- {
- uri = new Uri(node.Attributes["Target"].Value.ToString(), UriKind.Absolute);
- }
- CreateRelationship(uri,
- mode,
- node.Attributes["Type"].Value.ToString(),
- node.Attributes["Id"].Value.ToString(),
- true);
- }
-
- foreach (PackageRelationship r in relationships.Values)
- {
- if (r.RelationshipType == PackageProperties.NSPackagePropertiesRelation)
- {
- PackagePart part = GetPart(PackUriHelper.ResolvePartUri(Uri, r.TargetUri));
- packageProperties = new PackagePropertiesPart();
- packageProperties.Package = this;
- packageProperties.Part = part;
- packageProperties.LoadFrom(part.GetStream());
- }
- }
- }
- }
-
- private string NextId()
- {
- while (true)
- {
- string s = "Re" + RelationshipId.ToString();
- if (!Relationships.ContainsKey(s))
- return s;
-
- RelationshipId++;
- }
- }
-
- public static Package Open(Stream stream)
- {
- return Open(stream, FileMode.Open);
- }
-
- public static Package Open(string path)
- {
- return Open(path, FileMode.OpenOrCreate);
- }
-
- public static Package Open(Stream stream, FileMode packageMode)
- {
- FileAccess access = packageMode == FileMode.Open ? FileAccess.Read : FileAccess.ReadWrite;
- return Open(stream, packageMode, access);
- }
-
- public static Package Open(string path, FileMode packageMode)
- {
- return Open(path, packageMode, FileAccess.ReadWrite);
- }
-
- public static Package Open(Stream stream, FileMode packageMode, FileAccess packageAccess)
- {
- return Open(stream, packageMode, packageAccess, false);
- }
-
- private static Package Open(Stream stream, FileMode packageMode, FileAccess packageAccess, bool ownsStream)
- {
- return OpenCore(stream, packageMode, packageAccess, ownsStream);
- }
-
- public static Package Open(string path, FileMode packageMode, FileAccess packageAccess)
- {
- return Open(path, packageMode, packageAccess, FileShare.None);
- }
-
- public static Package Open(string path, FileMode packageMode, FileAccess packageAccess, FileShare packageShare)
- {
- if (packageShare != FileShare.Read && packageShare != FileShare.None)
- throw new NotSupportedException("FileShare.Read and FileShare.None are the only supported options");
-
- FileInfo info = new FileInfo(path);
-
- // Bug - MS.NET appears to test for FileAccess.ReadWrite, not FileAccess.Write
- if (packageAccess != FileAccess.ReadWrite && !info.Exists)
- throw new ArgumentException("packageAccess", "Cannot create stream with FileAccess.Read");
-
-
- if (info.Exists && packageMode == FileMode.OpenOrCreate && info.Length == 0)
- throw new FileFormatException("Stream length cannot be zero with FileMode.Open");
-
- Stream s = File.Open(path, packageMode, packageAccess, packageShare);
- return Open(s, packageMode, packageAccess, true);
- }
-
- private static Package OpenCore(Stream stream, FileMode packageMode, FileAccess packageAccess, bool ownsStream)
- {
- if ((packageAccess & FileAccess.Read) == FileAccess.Read && !stream.CanRead)
- throw new IOException("Stream does not support reading");
-
- if ((packageAccess & FileAccess.Write) == FileAccess.Write && !stream.CanWrite)
- throw new IOException("Stream does not support reading");
-
- if (!stream.CanSeek)
- throw new ArgumentException("stream", "Stream must support seeking");
-
- if (packageMode == FileMode.Open && stream.Length == 0)
- throw new FileFormatException("Stream length cannot be zero with FileMode.Open");
-
- if (packageMode == FileMode.CreateNew && stream.Length > 0)
- throw new IOException("Cannot use CreateNew when stream contains data");
-
- if (packageMode == FileMode.Append || packageMode == FileMode.Truncate)
- {
- if (stream.CanWrite)
- throw new NotSupportedException(string.Format("PackageMode.{0} is not supported", packageMode));
- else
- throw new IOException(string.Format("PackageMode.{0} is not supported", packageMode));
- }
-
- return new ZipPackage(packageAccess, ownsStream, stream);
- }
-
- public virtual bool PartExists(Uri partUri)
- {
- return GetPart(partUri) != null;
- }
-
- public bool RelationshipExists(string id)
- {
- return Relationships.ContainsKey(id);
- }
-
- internal static void WriteRelationships(Dictionary<string, PackageRelationship> relationships, Stream stream)
- {
- XmlDocument doc = new XmlDocument();
- XmlNamespaceManager manager = new XmlNamespaceManager(doc.NameTable);
- manager.AddNamespace("rel", RelationshipNamespace);
-
- doc.AppendChild(doc.CreateNode(XmlNodeType.XmlDeclaration, "", ""));
-
- XmlNode root = doc.CreateNode(XmlNodeType.Element, "Relationships", RelationshipNamespace);
- doc.AppendChild(root);
-
- foreach (PackageRelationship relationship in relationships.Values)
- {
- XmlNode node = doc.CreateNode(XmlNodeType.Element, "Relationship", RelationshipNamespace);
-
- XmlAttribute idAtt = doc.CreateAttribute("Id");
- idAtt.Value = relationship.Id;
- node.Attributes.Append(idAtt);
-
- XmlAttribute targetAtt = doc.CreateAttribute("Target");
- targetAtt.Value = relationship.TargetUri.ToString();
- node.Attributes.Append(targetAtt);
-
- if (relationship.TargetMode != TargetMode.Internal)
- {
- XmlAttribute modeAtt = doc.CreateAttribute("TargetMode");
- modeAtt.Value = relationship.TargetMode.ToString();
- node.Attributes.Append(modeAtt);
- }
- XmlAttribute typeAtt = doc.CreateAttribute("Type");
- typeAtt.Value = relationship.RelationshipType;
- node.Attributes.Append(typeAtt);
-
- root.AppendChild(node);
- }
-
- using (XmlTextWriter writer = new XmlTextWriter(stream, System.Text.Encoding.UTF8))
- doc.WriteTo(writer);
- }
- }
-
- internal class FileFormatException : Exception
- {
- public FileFormatException(string message): base(message)
- {
-
- }
- }
- }
|