Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Base / MS / Internal / IO / Packaging / InternalRelationshipCollection.cs / 1 / InternalRelationshipCollection.cs
//------------------------------------------------------------------------------ // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: // This is a class for representing a PackageRelationshipCollection. This is an internal // class for manipulating relationships associated with a part // // Details: // This class handles serialization to/from relationship parts, creation of those parts // and offers methods to create, delete and enumerate relationships. This code was // moved from the PackageRelationshipCollection class. // // History: // 04/26/2004: SarjanaS: This code was moved from the PackageRelationshipCollection class. // //----------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Xml; // for XmlReader/Writer using System.IO.Packaging; using System.Windows; // For Exception strings - SRID using System.IO; using System.Diagnostics; using System.Windows.Markup; // For XMLCompatibilityReader using MS.Internal; // For Invariant. namespace MS.Internal.IO.Packaging { ////// Collection of all the relationships corresponding to a given source PackagePart /// internal class InternalRelationshipCollection : IEnumerable{ //----------------------------------------------------- // // Public Methods // //----------------------------------------------------- #region IEnumerable /// /// Returns an enumertor over all the relationships for a Package or a PackagePart /// ///IEnumerator IEnumerable.GetEnumerator() { return _relationships.GetEnumerator(); } /// /// Returns an enumertor over all the relationships for a Package or a PackagePart /// ///IEnumerator IEnumerable .GetEnumerator() { return _relationships.GetEnumerator(); } /// /// Returns an enumertor over all the relationships for a Package or a PackagePart /// ///public List .Enumerator GetEnumerator() { return _relationships.GetEnumerator(); } #endregion //------------------------------------------------------ // // Internal Methods // //----------------------------------------------------- #region Internal Methods /// /// Constructor /// ///For use by PackagePart internal InternalRelationshipCollection(PackagePart part): this(part.Package, part) { } ////// Constructor /// ///For use by Package internal InternalRelationshipCollection(Package package): this(package, null) { } ////// Add new relationship /// /// target /// Enumeration indicating the base uri for the target uri /// relationship type that uniquely defines the role of the relationship /// String that conforms to the xsd:ID datatype. Unique across the source's relationships. /// Null OK (ID will be generated). internal PackageRelationship Add(Uri targetUri, TargetMode targetMode, string relationshipType, string id) { return Add(targetUri, targetMode, relationshipType, id, false /*not parsing*/); } ////// Return the relationship whose id is 'id', and null if not found. /// internal PackageRelationship GetRelationship(string id) { Invariant.Assert(!_package.InStreamingCreation); int index = GetRelationshipIndex(id); if (index == -1) return null; return _relationships[index]; } ////// Delete relationship with ID 'id' /// /// ID of the relationship to remove internal void Delete(String id) { Invariant.Assert(!_package.InStreamingCreation); int index = GetRelationshipIndex(id); if (index == -1) return; _relationships.RemoveAt(index); _dirty = true; } ////// Clear all the relationships in this collection /// Today it is only used when the entire relationship part is being deleted /// internal void Clear() { Invariant.Assert(!_package.InStreamingCreation); _relationships.Clear(); _dirty = true; } ////// Flush to stream (destructive) /// ////// Based on the streaming mode of the package, flush piece or flush part. /// internal void Flush() { if (!_dirty) return; if (_package.InStreamingCreation) { FlushRelationshipsToPiece(false /* not a terminal piece */); } else { if (_relationships.Count == 0) // empty? { // delete the part if (_package.PartExists(_uri)) { _package.DeletePart(_uri); } _relationshipPart = null; } else { EnsureRelationshipPart(); // lazy init // write xml WriteRelationshipPart(_relationshipPart); } } _dirty = false; } ////// Exclusively used for streaming production. Any relationships remaining to flush /// are flushed to a terminal piece. /// If relationships have been created and have all been flushed already, a terminal piece /// is created to complete the Xml document. /// internal void CloseInStreamingCreationMode() { Debug.Assert(_package.InStreamingCreation, "This method should only be called in streaming creation mode"); FlushRelationshipsToPiece(true /* last piece */); } internal static void ThrowIfInvalidRelationshipType(string relationshipType) { // Look for empty string or string with just spaces if (relationshipType.Trim() == String.Empty) throw new ArgumentException(SR.Get(SRID.InvalidRelationshipType)); } // If 'id' is not of the xsd type ID, throw an exception. internal static void ThrowIfInvalidXsdId(string id) { Invariant.Assert(id != null, "id should not be null"); try { // An XSD ID is an NCName that is unique. XmlConvert.VerifyNCName(id); } catch (XmlException exception) { throw new XmlException(SR.Get(SRID.NotAValidXmlIdString, id), exception); } } #endregion Internal Methods //------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods ////// Constructor /// /// package /// part will be null if package is the source of the relationships ///Shared constructor private InternalRelationshipCollection(Package package, PackagePart part) { Debug.Assert(package != null, "package parameter passed should never be null"); _package = package; _sourcePart = part; //_sourcePart may be null representing that the relationships are at the package level _uri = GetRelationshipPartUri(_sourcePart); _relationships = new List(4); // Load if available (not applicable to write-only mode). if (package.FileOpenAccess != FileAccess.Write && package.PartExists(_uri)) { _relationshipPart = package.GetPart(_uri); ThrowIfIncorrectContentType(_relationshipPart.ValidatedContentType); ParseRelationshipPart(_relationshipPart); } //Any initialization in the constructor should not set the dirty flag to true. _dirty = false; } /// /// Returns the associated RelationshipPart for this part /// /// may be null ///name of relationship part for the given part private static Uri GetRelationshipPartUri(PackagePart part) { Uri sourceUri; if (part == null) sourceUri = PackUriHelper.PackageRootUri; else sourceUri = part.Uri; return PackUriHelper.GetRelationshipPartUri(sourceUri); } ////// Parse PackageRelationship Stream /// /// relationship part ///Thrown if XML is malformed private void ParseRelationshipPart(PackagePart part) { //We can safely open the stream as FileAccess.Read, as this code //should only be invoked if the Package has been opened in Read or ReadWrite mode. Debug.Assert(_package.FileOpenAccess == FileAccess.Read || _package.FileOpenAccess == FileAccess.ReadWrite, "This method should only be called when FileAccess is Read or ReadWrite"); using (Stream s = part.GetStream(FileMode.Open, FileAccess.Read)) { // load from the relationship part associated with the given part using (XmlTextReader baseReader = new XmlTextReader(s)) { baseReader.WhitespaceHandling = WhitespaceHandling.None; //Prohibit DTD from the markup as per the OPC spec baseReader.ProhibitDtd = true; using (XmlCompatibilityReader reader = new XmlCompatibilityReader(baseReader, RelationshipKnownNamespaces)) { //This method expects the reader to be in ReadState.Initial. //It will make the first read call. PackagingUtilities.PerformInitailReadAndVerifyEncoding(baseReader); //Note: After the previous method call the reader should be at the first tag in the markup. //MoveToContent - Skips over the following - ProcessingInstruction, DocumentType, Comment, Whitespace, or SignificantWhitespace //If the reader is currently at a content node then this function call is a no-op reader.MoveToContent(); // look for our tag and namespace pair - throw if other elements are encountered // Make sure that the current node read is an Element if (reader.NodeType == XmlNodeType.Element && (reader.Depth == 0) && (String.CompareOrdinal(RelationshipsTagName, reader.LocalName) == 0) && (String.CompareOrdinal(PackagingUtilities.RelationshipNamespaceUri, reader.NamespaceURI) == 0)) { ThrowIfXmlBaseAttributeIsPresent(reader); //There should be a namespace Attribute present at this level. //Also any other attribute on thetag is an error including xml: and xsi: attributes if (PackagingUtilities.GetNonXmlnsAttributeCount(reader) > 0) throw new XmlException(SR.Get(SRID.RelationshipsTagHasExtraAttributes), null, reader.LineNumber, reader.LinePosition); // start tag encountered for Relationships // now parse individual Relationship tags while (reader.Read()) { //Skips over the following - ProcessingInstruction, DocumentType, Comment, Whitespace, or SignificantWhitespace //If the reader is currently at a content node then this function call is a no-op reader.MoveToContent(); //If MoveToContent() takes us to the end of the content if (reader.NodeType == XmlNodeType.None) continue; if (reader.NodeType == XmlNodeType.Element && (reader.Depth == 1) && (String.CompareOrdinal(RelationshipTagName, reader.LocalName) == 0) && (String.CompareOrdinal(PackagingUtilities.RelationshipNamespaceUri, reader.NamespaceURI) == 0)) { ThrowIfXmlBaseAttributeIsPresent(reader); int expectedAttributesCount = 3; string targetModeAttributeValue = reader.GetAttribute(TargetModeAttributeName); if (targetModeAttributeValue != null) expectedAttributesCount++; //check if there are expected number of attributes. //Also any other attribute on the tag is an error including xml: and xsi: attributes if (PackagingUtilities.GetNonXmlnsAttributeCount(reader) == expectedAttributesCount) { ProcessRelationshipAttributes(reader); //Skip the EndElement for Relationship if (!reader.IsEmptyElement) ProcessEndElementForRelationshipTag(reader); } else throw new XmlException(SR.Get(SRID.RelationshipTagDoesntMatchSchema), null, reader.LineNumber, reader.LinePosition); } else if (!(String.CompareOrdinal(RelationshipsTagName,reader.LocalName) == 0 && (reader.NodeType == XmlNodeType.EndElement))) throw new XmlException(SR.Get(SRID.UnknownTagEncountered), null, reader.LineNumber, reader.LinePosition); } } else throw new XmlException(SR.Get(SRID.ExpectedRelationshipsElementTag), null, reader.LineNumber, reader.LinePosition); } } } } //This method processes the attributes that are present on the Relationship element private void ProcessRelationshipAttributes(XmlCompatibilityReader reader) { // Attribute : TargetMode string targetModeAttributeValue = reader.GetAttribute(TargetModeAttributeName); //If the TargetMode attribute is missing in the underlying markup then we assume it to be internal TargetMode relationshipTargetMode = TargetMode.Internal; if (targetModeAttributeValue != null) { try { relationshipTargetMode = (TargetMode)(Enum.Parse(typeof(TargetMode), targetModeAttributeValue, false /* ignore case */)); } catch (ArgumentNullException argNullEx) { ThrowForInvalidAttributeValue(reader, TargetModeAttributeName, argNullEx); } catch (ArgumentException argEx) { //if the targetModeAttributeValue is not Internal|External then Argument Exception will be thrown. ThrowForInvalidAttributeValue(reader, TargetModeAttributeName, argEx); } } // Attribute : Target // create a new PackageRelationship string targetAttributeValue = reader.GetAttribute(TargetAttributeName); if (targetAttributeValue == null || targetAttributeValue == String.Empty) throw new XmlException(SR.Get(SRID.RequiredRelationshipAttributeMissing, TargetAttributeName), null, reader.LineNumber, reader.LinePosition); Uri targetUri = new Uri(targetAttributeValue, UriKind.RelativeOrAbsolute); // Attribute : Type string typeAttributeValue = reader.GetAttribute(TypeAttributeName); if (typeAttributeValue == null || typeAttributeValue == String.Empty) throw new XmlException(SR.Get(SRID.RequiredRelationshipAttributeMissing, TypeAttributeName), null, reader.LineNumber, reader.LinePosition); // Attribute : Id // Get the Id attribute (required attribute). string idAttributeValue = reader.GetAttribute(IdAttributeName); if (idAttributeValue == null || idAttributeValue == String.Empty) throw new XmlException(SR.Get(SRID.RequiredRelationshipAttributeMissing, IdAttributeName), null, reader.LineNumber, reader.LinePosition); // Add the relationship to the collection Add(targetUri, relationshipTargetMode, typeAttributeValue, idAttributeValue, true /*parsing*/); } //If End element is present for Relationship then we process it private void ProcessEndElementForRelationshipTag(XmlCompatibilityReader reader) { Debug.Assert(!reader.IsEmptyElement, "This method should only be called it the Relationship Element is not empty"); reader.Read(); //Skips over the following - ProcessingInstruction, DocumentType, Comment, Whitespace, or SignificantWhitespace reader.MoveToContent(); if (reader.NodeType == XmlNodeType.EndElement && String.CompareOrdinal(RelationshipTagName, reader.LocalName) == 0) return; else throw new XmlException(SR.Get(SRID.ElementIsNotEmptyElement, RelationshipTagName), null, reader.LineNumber, reader.LinePosition); } /// /// Add new relationship to the Collection /// /// target /// Enumeration indicating the base uri for the target uri /// relationship type that uniquely defines the role of the relationship /// String that conforms to the xsd:ID datatype. Unique across the source's relationships. /// Null OK (ID will be generated). /// Indicates whether the add call is made while parsing existing relationships /// from a relationship part, or we are adding a new relationship private PackageRelationship Add(Uri targetUri, TargetMode targetMode, string relationshipType, string id, bool parsing) { if (targetUri == null) throw new ArgumentNullException("targetUri"); if (relationshipType == null) throw new ArgumentNullException("relationshipType"); ThrowIfInvalidRelationshipType(relationshipType); //Verify if the Enum value is valid if (targetMode < TargetMode.Internal || targetMode > TargetMode.External) throw new ArgumentOutOfRangeException("targetMode"); // don't accept absolute Uri's if targetMode is Internal. if (targetMode == TargetMode.Internal && targetUri.IsAbsoluteUri) throw new ArgumentException(SR.Get(SRID.RelationshipTargetMustBeRelative), "targetUri"); // don't allow relationships to relationships // This check should be made for following cases // 1. Uri is absolute and it is pack Uri // 2. Uri is NOT absolute and its target mode is internal (or NOT external) // Note: if the target is absolute uri and its not a pack scheme then we cannot determine if it is a rels part // Note: if the target is relative uri and target mode is external, we cannot determine if it is a rels part if ((!targetUri.IsAbsoluteUri && targetMode != TargetMode.External) || (targetUri.IsAbsoluteUri && targetUri.Scheme == PackUriHelper.UriSchemePack)) { Uri resolvedUri = GetResolvedTargetUri(targetUri, targetMode); //GetResolvedTargetUri returns a null if the target mode is external and the //target Uri is a packUri with no "part" component, so in that case we know that //its not a relationship part. if (resolvedUri != null) { if (PackUriHelper.IsRelationshipPartUri(resolvedUri)) throw new ArgumentException(SR.Get(SRID.RelationshipToRelationshipIllegal), "targetUri"); } } // Generate an ID if id is null. Throw exception if neither null nor a valid unique xsd:ID. if (id == null) id = GenerateUniqueRelationshipId(); else ValidateUniqueRelationshipId(id); //Ensure the relationship part EnsureRelationshipPart(); // create and add PackageRelationship relationship = new PackageRelationship(_package, _sourcePart, targetUri, targetMode, relationshipType, id); _relationships.Add(relationship); //If we are adding relationships as a part of Parsing the underlying relationship part, we should not set //the dirty flag to false. _dirty = !parsing; return relationship; } ////// Write PackageRelationship Stream /// /// part to persist to private void WriteRelationshipPart(PackagePart part) { using (IgnoreFlushAndCloseStream s = new IgnoreFlushAndCloseStream(part.GetStream())) { s.SetLength(0); // truncate to resolve PS 954048 // use UTF-8 encoding by default using (XmlTextWriter writer = new XmlTextWriter(s, System.Text.Encoding.UTF8)) { #if DEBUG writer.Formatting = Formatting.Indented; #endif writer.WriteStartDocument(); // start outer Relationships tag writer.WriteStartElement(RelationshipsTagName, PackagingUtilities.RelationshipNamespaceUri); // Write Relationship elements. WriteRelationshipsAsXml( writer, _relationships, false, /* do not systematically write target mode */ false /* not in streaming production */ ); // end of Relationships tag writer.WriteEndElement(); // close the document writer.WriteEndDocument(); } } } ////// Write one Relationship element for each member of relationships. /// This method is used by XmlDigitalSignatureProcessor code as well /// internal static void WriteRelationshipsAsXml(XmlWriter writer, IEnumerablerelationships, bool alwaysWriteTargetModeAttribute, bool inStreamingProduction) { foreach (PackageRelationship relationship in relationships) { if (inStreamingProduction && relationship.Saved) continue; writer.WriteStartElement(RelationshipTagName); // Write RelationshipType attribute. writer.WriteAttributeString(TypeAttributeName, relationship.RelationshipType); // Write Target attribute. // We would like to persist the uri as passed in by the user and so we use the // OriginalString property. This makes the persisting behavior consistent // for relative and absolute Uris. // Since we accpeted the Uri as a string, we are at the minimum guaranteed that // the string can be converted to a valid Uri. // Also, we are just using it here to persist the information and we are not // resolving or fetching a resource based on this Uri. writer.WriteAttributeString(TargetAttributeName, relationship.TargetUri.OriginalString); // TargetMode is optional attribute in the markup and its default value is TargetMode="Internal" if (alwaysWriteTargetModeAttribute || relationship.TargetMode == TargetMode.External) writer.WriteAttributeString(TargetModeAttributeName, relationship.TargetMode.ToString()); // Write Id attribute. writer.WriteAttributeString(IdAttributeName, relationship.Id); writer.WriteEndElement(); // The following flag is useful only in a write-once context, namely // in streaming production. In other contexts, it is simply ignored. if (inStreamingProduction) relationship.Saved = true; } } /// /// Ensures that the PackageRelationship PackagePart has been created - lazy init /// ////// Streaming production is a special case because the storage layer /// can't and needn't be accessed to retrieve the relationship part /// once it has been created. /// private void EnsureRelationshipPart() { if (_relationshipPart == null || _relationshipPart.IsDeleted) { if (!_package.InStreamingCreation && _package.PartExists(_uri)) { _relationshipPart = _package.GetPart(_uri); ThrowIfIncorrectContentType(_relationshipPart.ValidatedContentType); } else { CompressionOption compressionOption = _sourcePart == null ? CompressionOption.NotCompressed : _sourcePart.CompressionOption; _relationshipPart = _package.CreatePart(_uri, PackagingUtilities.RelationshipPartContentType.ToString(), compressionOption); } } } ////// Resolves the target uri in the relationship against the source part or the /// package root. This resolved Uri is then used by the Add method to figure /// out if a relationship is being created to another relationship part. /// /// PackageRelationship target uri /// Enum value specifying the interpretation of the base uri /// for the relationship target uri ///Resolved Uri private Uri GetResolvedTargetUri(Uri target, TargetMode targetMode) { if (targetMode == TargetMode.Internal) { Debug.Assert(!target.IsAbsoluteUri, "Uri should be relative at this stage"); if (_sourcePart == null) //indicates that the source is the package root return PackUriHelper.ResolvePartUri(PackUriHelper.PackageRootUri, target); else return PackUriHelper.ResolvePartUri(_sourcePart.Uri, target); } else { if (target.IsAbsoluteUri) { if (String.CompareOrdinal(target.Scheme, PackUriHelper.UriSchemePack) == 0) return PackUriHelper.GetPartUri(target); } else Debug.Assert(false, "Uri should not be relative at this stage"); } // relative to the location of the package. return target; } //Throws an exception if the relationship part does not have the correct content type private void ThrowIfIncorrectContentType(ContentType contentType) { if (!contentType.AreTypeAndSubTypeEqual(PackagingUtilities.RelationshipPartContentType)) throw new FileFormatException(SR.Get(SRID.RelationshipPartIncorrectContentType)); } //Throws an exception if the xml:base attribute is present in the Relationships XML private void ThrowIfXmlBaseAttributeIsPresent(XmlCompatibilityReader reader) { string xmlBaseAttributeValue = reader.GetAttribute(XmlBaseAttributeName); if (xmlBaseAttributeValue != null) throw new XmlException(SR.Get(SRID.InvalidXmlBaseAttributePresent, XmlBaseAttributeName), null, reader.LineNumber, reader.LinePosition); } //Throws an XML exception if the attribute value is invalid private void ThrowForInvalidAttributeValue(XmlCompatibilityReader reader, String attributeName, Exception ex) { throw new XmlException(SR.Get(SRID.InvalidValueForTheAttribute, attributeName), ex, reader.LineNumber, reader.LinePosition); } // Generate a unique relation ID. // In streaming production, we rely on the fact that the time stamp is supposedly // unique on a given machine. So no duplication test is carried out. private string GenerateUniqueRelationshipId() { string id; do { id = GenerateRelationshipId(); } while (!_package.InStreamingCreation && GetRelationship(id) != null); return id; } // Build an ID string consisting of the letter 'R' followed by an 8-byte GUID timestamp. // Guid.ToString() outputs the bytes in the big-endian order (higher order byte first) private string GenerateRelationshipId() { // The timestamp consists of the first 8 hex octets of the GUID. return String.Concat("R", Guid.NewGuid().ToString("N").Substring(0, _timestampLength)); } // If 'id' is not of the xsd type ID or is not unique for this collection, throw an exception. private void ValidateUniqueRelationshipId(string id) { // An XSD ID is an NCName that is unique. ThrowIfInvalidXsdId(id); // Check for uniqueness. if (GetRelationshipIndex(id) >= 0) throw new XmlException(SR.Get(SRID.NotAUniqueRelationshipId, id)); } // Retrieve a relationship's index in _relationships given its id. // Return a negative value if not found. private int GetRelationshipIndex(string id) { for (int index = 0; index < _relationships.Count; ++index) if (string.Equals(_relationships[index].Id, id, StringComparison.Ordinal)) return index; return -1; } ////// If any relationships have to be flushed, will lazily create a StreamingZipPartStream /// to flush them to a piece. /// When isLastPiece is true and a StreamingZipPartStream has been created or there are /// more relationships to be flushed, the Xml document is completed and the /// StreamingZipPartStream is closed. /// private void FlushRelationshipsToPiece(bool isLastPiece) { Debug.Assert(_package.InStreamingCreation, "This method should only be called in streaming creation mode"); if (_dirty) { // No deletion in streaming production. Invariant.Assert(_relationships.Count > 0); // Dump the contents of _relationships to the stream and mark as saved. WriteRelationshipsAsXml( StreamingXmlWriter, _relationships, false, /* do not systematically write target mode */ true /* in streaming production */ ); if (!isLastPiece) { // Create a piece with the Xml just written. StreamingXmlWriter.Flush(); } _dirty = false; } if (isLastPiece && StreamingXmlWriter.WriteState != WriteState.Closed) { // Close Relationships tag. StreamingXmlWriter.WriteEndElement(); // Close the document. StreamingXmlWriter.WriteEndDocument(); // Create a terminal piece. This will set StreamingXmlWriter.WriteState to Closed. StreamingXmlWriter.Close(); } } #endregion #region Private Properties ////// Invoked strictly in streaming production to return and, if needed, /// lazily initialize _streamingXmlWriter. /// private XmlWriter StreamingXmlWriter { get { if (_streamingXmlWriter == null) { // Implement the writer on top of a streaming stream, so each Flush // will create a piece. EnsureRelationshipPart(); StreamingZipPartStream s = (StreamingZipPartStream) _relationshipPart.GetStream( FileMode.CreateNew, FileAccess.Write); _streamingXmlWriter = new XmlTextWriter(s, System.Text.Encoding.UTF8); // Write the top of the Xml document. StreamingXmlWriter.WriteStartDocument(); StreamingXmlWriter.WriteStartElement( RelationshipsTagName, PackagingUtilities.RelationshipNamespaceUri); } return _streamingXmlWriter; } } #endregion Private Properties //----------------------------------------------------- // // Private Members // //------------------------------------------------------ #region Private Members private List_relationships; private bool _dirty; // true if we have uncommitted changes to _relationships private Package _package; // our package - in case _sourcePart is null private PackagePart _sourcePart; // owning part - null if package is the owner private PackagePart _relationshipPart; // where our relationships are persisted private Uri _uri; // the URI of our relationship part private XmlWriter _streamingXmlWriter; //----------------------------------------------------- // // Private Fields // //----------------------------------------------------- // segment that indicates a relationship part private static readonly int _timestampLength = 16; private static readonly string RelationshipsTagName = "Relationships"; private static readonly string RelationshipTagName = "Relationship"; private static readonly string TargetAttributeName = "Target"; private static readonly string TypeAttributeName = "Type"; private static readonly string IdAttributeName = "Id"; private static readonly string XmlBaseAttributeName = "xml:base"; private static readonly string TargetModeAttributeName = "TargetMode"; private static readonly string[] RelationshipKnownNamespaces = new string[] { PackagingUtilities.RelationshipNamespaceUri }; #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ // // // Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: // This is a class for representing a PackageRelationshipCollection. This is an internal // class for manipulating relationships associated with a part // // Details: // This class handles serialization to/from relationship parts, creation of those parts // and offers methods to create, delete and enumerate relationships. This code was // moved from the PackageRelationshipCollection class. // // History: // 04/26/2004: SarjanaS: This code was moved from the PackageRelationshipCollection class. // //----------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Xml; // for XmlReader/Writer using System.IO.Packaging; using System.Windows; // For Exception strings - SRID using System.IO; using System.Diagnostics; using System.Windows.Markup; // For XMLCompatibilityReader using MS.Internal; // For Invariant. namespace MS.Internal.IO.Packaging { ////// Collection of all the relationships corresponding to a given source PackagePart /// internal class InternalRelationshipCollection : IEnumerable{ //----------------------------------------------------- // // Public Methods // //----------------------------------------------------- #region IEnumerable /// /// Returns an enumertor over all the relationships for a Package or a PackagePart /// ///IEnumerator IEnumerable.GetEnumerator() { return _relationships.GetEnumerator(); } /// /// Returns an enumertor over all the relationships for a Package or a PackagePart /// ///IEnumerator IEnumerable .GetEnumerator() { return _relationships.GetEnumerator(); } /// /// Returns an enumertor over all the relationships for a Package or a PackagePart /// ///public List .Enumerator GetEnumerator() { return _relationships.GetEnumerator(); } #endregion //------------------------------------------------------ // // Internal Methods // //----------------------------------------------------- #region Internal Methods /// /// Constructor /// ///For use by PackagePart internal InternalRelationshipCollection(PackagePart part): this(part.Package, part) { } ////// Constructor /// ///For use by Package internal InternalRelationshipCollection(Package package): this(package, null) { } ////// Add new relationship /// /// target /// Enumeration indicating the base uri for the target uri /// relationship type that uniquely defines the role of the relationship /// String that conforms to the xsd:ID datatype. Unique across the source's relationships. /// Null OK (ID will be generated). internal PackageRelationship Add(Uri targetUri, TargetMode targetMode, string relationshipType, string id) { return Add(targetUri, targetMode, relationshipType, id, false /*not parsing*/); } ////// Return the relationship whose id is 'id', and null if not found. /// internal PackageRelationship GetRelationship(string id) { Invariant.Assert(!_package.InStreamingCreation); int index = GetRelationshipIndex(id); if (index == -1) return null; return _relationships[index]; } ////// Delete relationship with ID 'id' /// /// ID of the relationship to remove internal void Delete(String id) { Invariant.Assert(!_package.InStreamingCreation); int index = GetRelationshipIndex(id); if (index == -1) return; _relationships.RemoveAt(index); _dirty = true; } ////// Clear all the relationships in this collection /// Today it is only used when the entire relationship part is being deleted /// internal void Clear() { Invariant.Assert(!_package.InStreamingCreation); _relationships.Clear(); _dirty = true; } ////// Flush to stream (destructive) /// ////// Based on the streaming mode of the package, flush piece or flush part. /// internal void Flush() { if (!_dirty) return; if (_package.InStreamingCreation) { FlushRelationshipsToPiece(false /* not a terminal piece */); } else { if (_relationships.Count == 0) // empty? { // delete the part if (_package.PartExists(_uri)) { _package.DeletePart(_uri); } _relationshipPart = null; } else { EnsureRelationshipPart(); // lazy init // write xml WriteRelationshipPart(_relationshipPart); } } _dirty = false; } ////// Exclusively used for streaming production. Any relationships remaining to flush /// are flushed to a terminal piece. /// If relationships have been created and have all been flushed already, a terminal piece /// is created to complete the Xml document. /// internal void CloseInStreamingCreationMode() { Debug.Assert(_package.InStreamingCreation, "This method should only be called in streaming creation mode"); FlushRelationshipsToPiece(true /* last piece */); } internal static void ThrowIfInvalidRelationshipType(string relationshipType) { // Look for empty string or string with just spaces if (relationshipType.Trim() == String.Empty) throw new ArgumentException(SR.Get(SRID.InvalidRelationshipType)); } // If 'id' is not of the xsd type ID, throw an exception. internal static void ThrowIfInvalidXsdId(string id) { Invariant.Assert(id != null, "id should not be null"); try { // An XSD ID is an NCName that is unique. XmlConvert.VerifyNCName(id); } catch (XmlException exception) { throw new XmlException(SR.Get(SRID.NotAValidXmlIdString, id), exception); } } #endregion Internal Methods //------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods ////// Constructor /// /// package /// part will be null if package is the source of the relationships ///Shared constructor private InternalRelationshipCollection(Package package, PackagePart part) { Debug.Assert(package != null, "package parameter passed should never be null"); _package = package; _sourcePart = part; //_sourcePart may be null representing that the relationships are at the package level _uri = GetRelationshipPartUri(_sourcePart); _relationships = new List(4); // Load if available (not applicable to write-only mode). if (package.FileOpenAccess != FileAccess.Write && package.PartExists(_uri)) { _relationshipPart = package.GetPart(_uri); ThrowIfIncorrectContentType(_relationshipPart.ValidatedContentType); ParseRelationshipPart(_relationshipPart); } //Any initialization in the constructor should not set the dirty flag to true. _dirty = false; } /// /// Returns the associated RelationshipPart for this part /// /// may be null ///name of relationship part for the given part private static Uri GetRelationshipPartUri(PackagePart part) { Uri sourceUri; if (part == null) sourceUri = PackUriHelper.PackageRootUri; else sourceUri = part.Uri; return PackUriHelper.GetRelationshipPartUri(sourceUri); } ////// Parse PackageRelationship Stream /// /// relationship part ///Thrown if XML is malformed private void ParseRelationshipPart(PackagePart part) { //We can safely open the stream as FileAccess.Read, as this code //should only be invoked if the Package has been opened in Read or ReadWrite mode. Debug.Assert(_package.FileOpenAccess == FileAccess.Read || _package.FileOpenAccess == FileAccess.ReadWrite, "This method should only be called when FileAccess is Read or ReadWrite"); using (Stream s = part.GetStream(FileMode.Open, FileAccess.Read)) { // load from the relationship part associated with the given part using (XmlTextReader baseReader = new XmlTextReader(s)) { baseReader.WhitespaceHandling = WhitespaceHandling.None; //Prohibit DTD from the markup as per the OPC spec baseReader.ProhibitDtd = true; using (XmlCompatibilityReader reader = new XmlCompatibilityReader(baseReader, RelationshipKnownNamespaces)) { //This method expects the reader to be in ReadState.Initial. //It will make the first read call. PackagingUtilities.PerformInitailReadAndVerifyEncoding(baseReader); //Note: After the previous method call the reader should be at the first tag in the markup. //MoveToContent - Skips over the following - ProcessingInstruction, DocumentType, Comment, Whitespace, or SignificantWhitespace //If the reader is currently at a content node then this function call is a no-op reader.MoveToContent(); // look for our tag and namespace pair - throw if other elements are encountered // Make sure that the current node read is an Element if (reader.NodeType == XmlNodeType.Element && (reader.Depth == 0) && (String.CompareOrdinal(RelationshipsTagName, reader.LocalName) == 0) && (String.CompareOrdinal(PackagingUtilities.RelationshipNamespaceUri, reader.NamespaceURI) == 0)) { ThrowIfXmlBaseAttributeIsPresent(reader); //There should be a namespace Attribute present at this level. //Also any other attribute on thetag is an error including xml: and xsi: attributes if (PackagingUtilities.GetNonXmlnsAttributeCount(reader) > 0) throw new XmlException(SR.Get(SRID.RelationshipsTagHasExtraAttributes), null, reader.LineNumber, reader.LinePosition); // start tag encountered for Relationships // now parse individual Relationship tags while (reader.Read()) { //Skips over the following - ProcessingInstruction, DocumentType, Comment, Whitespace, or SignificantWhitespace //If the reader is currently at a content node then this function call is a no-op reader.MoveToContent(); //If MoveToContent() takes us to the end of the content if (reader.NodeType == XmlNodeType.None) continue; if (reader.NodeType == XmlNodeType.Element && (reader.Depth == 1) && (String.CompareOrdinal(RelationshipTagName, reader.LocalName) == 0) && (String.CompareOrdinal(PackagingUtilities.RelationshipNamespaceUri, reader.NamespaceURI) == 0)) { ThrowIfXmlBaseAttributeIsPresent(reader); int expectedAttributesCount = 3; string targetModeAttributeValue = reader.GetAttribute(TargetModeAttributeName); if (targetModeAttributeValue != null) expectedAttributesCount++; //check if there are expected number of attributes. //Also any other attribute on the tag is an error including xml: and xsi: attributes if (PackagingUtilities.GetNonXmlnsAttributeCount(reader) == expectedAttributesCount) { ProcessRelationshipAttributes(reader); //Skip the EndElement for Relationship if (!reader.IsEmptyElement) ProcessEndElementForRelationshipTag(reader); } else throw new XmlException(SR.Get(SRID.RelationshipTagDoesntMatchSchema), null, reader.LineNumber, reader.LinePosition); } else if (!(String.CompareOrdinal(RelationshipsTagName,reader.LocalName) == 0 && (reader.NodeType == XmlNodeType.EndElement))) throw new XmlException(SR.Get(SRID.UnknownTagEncountered), null, reader.LineNumber, reader.LinePosition); } } else throw new XmlException(SR.Get(SRID.ExpectedRelationshipsElementTag), null, reader.LineNumber, reader.LinePosition); } } } } //This method processes the attributes that are present on the Relationship element private void ProcessRelationshipAttributes(XmlCompatibilityReader reader) { // Attribute : TargetMode string targetModeAttributeValue = reader.GetAttribute(TargetModeAttributeName); //If the TargetMode attribute is missing in the underlying markup then we assume it to be internal TargetMode relationshipTargetMode = TargetMode.Internal; if (targetModeAttributeValue != null) { try { relationshipTargetMode = (TargetMode)(Enum.Parse(typeof(TargetMode), targetModeAttributeValue, false /* ignore case */)); } catch (ArgumentNullException argNullEx) { ThrowForInvalidAttributeValue(reader, TargetModeAttributeName, argNullEx); } catch (ArgumentException argEx) { //if the targetModeAttributeValue is not Internal|External then Argument Exception will be thrown. ThrowForInvalidAttributeValue(reader, TargetModeAttributeName, argEx); } } // Attribute : Target // create a new PackageRelationship string targetAttributeValue = reader.GetAttribute(TargetAttributeName); if (targetAttributeValue == null || targetAttributeValue == String.Empty) throw new XmlException(SR.Get(SRID.RequiredRelationshipAttributeMissing, TargetAttributeName), null, reader.LineNumber, reader.LinePosition); Uri targetUri = new Uri(targetAttributeValue, UriKind.RelativeOrAbsolute); // Attribute : Type string typeAttributeValue = reader.GetAttribute(TypeAttributeName); if (typeAttributeValue == null || typeAttributeValue == String.Empty) throw new XmlException(SR.Get(SRID.RequiredRelationshipAttributeMissing, TypeAttributeName), null, reader.LineNumber, reader.LinePosition); // Attribute : Id // Get the Id attribute (required attribute). string idAttributeValue = reader.GetAttribute(IdAttributeName); if (idAttributeValue == null || idAttributeValue == String.Empty) throw new XmlException(SR.Get(SRID.RequiredRelationshipAttributeMissing, IdAttributeName), null, reader.LineNumber, reader.LinePosition); // Add the relationship to the collection Add(targetUri, relationshipTargetMode, typeAttributeValue, idAttributeValue, true /*parsing*/); } //If End element is present for Relationship then we process it private void ProcessEndElementForRelationshipTag(XmlCompatibilityReader reader) { Debug.Assert(!reader.IsEmptyElement, "This method should only be called it the Relationship Element is not empty"); reader.Read(); //Skips over the following - ProcessingInstruction, DocumentType, Comment, Whitespace, or SignificantWhitespace reader.MoveToContent(); if (reader.NodeType == XmlNodeType.EndElement && String.CompareOrdinal(RelationshipTagName, reader.LocalName) == 0) return; else throw new XmlException(SR.Get(SRID.ElementIsNotEmptyElement, RelationshipTagName), null, reader.LineNumber, reader.LinePosition); } /// /// Add new relationship to the Collection /// /// target /// Enumeration indicating the base uri for the target uri /// relationship type that uniquely defines the role of the relationship /// String that conforms to the xsd:ID datatype. Unique across the source's relationships. /// Null OK (ID will be generated). /// Indicates whether the add call is made while parsing existing relationships /// from a relationship part, or we are adding a new relationship private PackageRelationship Add(Uri targetUri, TargetMode targetMode, string relationshipType, string id, bool parsing) { if (targetUri == null) throw new ArgumentNullException("targetUri"); if (relationshipType == null) throw new ArgumentNullException("relationshipType"); ThrowIfInvalidRelationshipType(relationshipType); //Verify if the Enum value is valid if (targetMode < TargetMode.Internal || targetMode > TargetMode.External) throw new ArgumentOutOfRangeException("targetMode"); // don't accept absolute Uri's if targetMode is Internal. if (targetMode == TargetMode.Internal && targetUri.IsAbsoluteUri) throw new ArgumentException(SR.Get(SRID.RelationshipTargetMustBeRelative), "targetUri"); // don't allow relationships to relationships // This check should be made for following cases // 1. Uri is absolute and it is pack Uri // 2. Uri is NOT absolute and its target mode is internal (or NOT external) // Note: if the target is absolute uri and its not a pack scheme then we cannot determine if it is a rels part // Note: if the target is relative uri and target mode is external, we cannot determine if it is a rels part if ((!targetUri.IsAbsoluteUri && targetMode != TargetMode.External) || (targetUri.IsAbsoluteUri && targetUri.Scheme == PackUriHelper.UriSchemePack)) { Uri resolvedUri = GetResolvedTargetUri(targetUri, targetMode); //GetResolvedTargetUri returns a null if the target mode is external and the //target Uri is a packUri with no "part" component, so in that case we know that //its not a relationship part. if (resolvedUri != null) { if (PackUriHelper.IsRelationshipPartUri(resolvedUri)) throw new ArgumentException(SR.Get(SRID.RelationshipToRelationshipIllegal), "targetUri"); } } // Generate an ID if id is null. Throw exception if neither null nor a valid unique xsd:ID. if (id == null) id = GenerateUniqueRelationshipId(); else ValidateUniqueRelationshipId(id); //Ensure the relationship part EnsureRelationshipPart(); // create and add PackageRelationship relationship = new PackageRelationship(_package, _sourcePart, targetUri, targetMode, relationshipType, id); _relationships.Add(relationship); //If we are adding relationships as a part of Parsing the underlying relationship part, we should not set //the dirty flag to false. _dirty = !parsing; return relationship; } ////// Write PackageRelationship Stream /// /// part to persist to private void WriteRelationshipPart(PackagePart part) { using (IgnoreFlushAndCloseStream s = new IgnoreFlushAndCloseStream(part.GetStream())) { s.SetLength(0); // truncate to resolve PS 954048 // use UTF-8 encoding by default using (XmlTextWriter writer = new XmlTextWriter(s, System.Text.Encoding.UTF8)) { #if DEBUG writer.Formatting = Formatting.Indented; #endif writer.WriteStartDocument(); // start outer Relationships tag writer.WriteStartElement(RelationshipsTagName, PackagingUtilities.RelationshipNamespaceUri); // Write Relationship elements. WriteRelationshipsAsXml( writer, _relationships, false, /* do not systematically write target mode */ false /* not in streaming production */ ); // end of Relationships tag writer.WriteEndElement(); // close the document writer.WriteEndDocument(); } } } ////// Write one Relationship element for each member of relationships. /// This method is used by XmlDigitalSignatureProcessor code as well /// internal static void WriteRelationshipsAsXml(XmlWriter writer, IEnumerablerelationships, bool alwaysWriteTargetModeAttribute, bool inStreamingProduction) { foreach (PackageRelationship relationship in relationships) { if (inStreamingProduction && relationship.Saved) continue; writer.WriteStartElement(RelationshipTagName); // Write RelationshipType attribute. writer.WriteAttributeString(TypeAttributeName, relationship.RelationshipType); // Write Target attribute. // We would like to persist the uri as passed in by the user and so we use the // OriginalString property. This makes the persisting behavior consistent // for relative and absolute Uris. // Since we accpeted the Uri as a string, we are at the minimum guaranteed that // the string can be converted to a valid Uri. // Also, we are just using it here to persist the information and we are not // resolving or fetching a resource based on this Uri. writer.WriteAttributeString(TargetAttributeName, relationship.TargetUri.OriginalString); // TargetMode is optional attribute in the markup and its default value is TargetMode="Internal" if (alwaysWriteTargetModeAttribute || relationship.TargetMode == TargetMode.External) writer.WriteAttributeString(TargetModeAttributeName, relationship.TargetMode.ToString()); // Write Id attribute. writer.WriteAttributeString(IdAttributeName, relationship.Id); writer.WriteEndElement(); // The following flag is useful only in a write-once context, namely // in streaming production. In other contexts, it is simply ignored. if (inStreamingProduction) relationship.Saved = true; } } /// /// Ensures that the PackageRelationship PackagePart has been created - lazy init /// ////// Streaming production is a special case because the storage layer /// can't and needn't be accessed to retrieve the relationship part /// once it has been created. /// private void EnsureRelationshipPart() { if (_relationshipPart == null || _relationshipPart.IsDeleted) { if (!_package.InStreamingCreation && _package.PartExists(_uri)) { _relationshipPart = _package.GetPart(_uri); ThrowIfIncorrectContentType(_relationshipPart.ValidatedContentType); } else { CompressionOption compressionOption = _sourcePart == null ? CompressionOption.NotCompressed : _sourcePart.CompressionOption; _relationshipPart = _package.CreatePart(_uri, PackagingUtilities.RelationshipPartContentType.ToString(), compressionOption); } } } ////// Resolves the target uri in the relationship against the source part or the /// package root. This resolved Uri is then used by the Add method to figure /// out if a relationship is being created to another relationship part. /// /// PackageRelationship target uri /// Enum value specifying the interpretation of the base uri /// for the relationship target uri ///Resolved Uri private Uri GetResolvedTargetUri(Uri target, TargetMode targetMode) { if (targetMode == TargetMode.Internal) { Debug.Assert(!target.IsAbsoluteUri, "Uri should be relative at this stage"); if (_sourcePart == null) //indicates that the source is the package root return PackUriHelper.ResolvePartUri(PackUriHelper.PackageRootUri, target); else return PackUriHelper.ResolvePartUri(_sourcePart.Uri, target); } else { if (target.IsAbsoluteUri) { if (String.CompareOrdinal(target.Scheme, PackUriHelper.UriSchemePack) == 0) return PackUriHelper.GetPartUri(target); } else Debug.Assert(false, "Uri should not be relative at this stage"); } // relative to the location of the package. return target; } //Throws an exception if the relationship part does not have the correct content type private void ThrowIfIncorrectContentType(ContentType contentType) { if (!contentType.AreTypeAndSubTypeEqual(PackagingUtilities.RelationshipPartContentType)) throw new FileFormatException(SR.Get(SRID.RelationshipPartIncorrectContentType)); } //Throws an exception if the xml:base attribute is present in the Relationships XML private void ThrowIfXmlBaseAttributeIsPresent(XmlCompatibilityReader reader) { string xmlBaseAttributeValue = reader.GetAttribute(XmlBaseAttributeName); if (xmlBaseAttributeValue != null) throw new XmlException(SR.Get(SRID.InvalidXmlBaseAttributePresent, XmlBaseAttributeName), null, reader.LineNumber, reader.LinePosition); } //Throws an XML exception if the attribute value is invalid private void ThrowForInvalidAttributeValue(XmlCompatibilityReader reader, String attributeName, Exception ex) { throw new XmlException(SR.Get(SRID.InvalidValueForTheAttribute, attributeName), ex, reader.LineNumber, reader.LinePosition); } // Generate a unique relation ID. // In streaming production, we rely on the fact that the time stamp is supposedly // unique on a given machine. So no duplication test is carried out. private string GenerateUniqueRelationshipId() { string id; do { id = GenerateRelationshipId(); } while (!_package.InStreamingCreation && GetRelationship(id) != null); return id; } // Build an ID string consisting of the letter 'R' followed by an 8-byte GUID timestamp. // Guid.ToString() outputs the bytes in the big-endian order (higher order byte first) private string GenerateRelationshipId() { // The timestamp consists of the first 8 hex octets of the GUID. return String.Concat("R", Guid.NewGuid().ToString("N").Substring(0, _timestampLength)); } // If 'id' is not of the xsd type ID or is not unique for this collection, throw an exception. private void ValidateUniqueRelationshipId(string id) { // An XSD ID is an NCName that is unique. ThrowIfInvalidXsdId(id); // Check for uniqueness. if (GetRelationshipIndex(id) >= 0) throw new XmlException(SR.Get(SRID.NotAUniqueRelationshipId, id)); } // Retrieve a relationship's index in _relationships given its id. // Return a negative value if not found. private int GetRelationshipIndex(string id) { for (int index = 0; index < _relationships.Count; ++index) if (string.Equals(_relationships[index].Id, id, StringComparison.Ordinal)) return index; return -1; } ////// If any relationships have to be flushed, will lazily create a StreamingZipPartStream /// to flush them to a piece. /// When isLastPiece is true and a StreamingZipPartStream has been created or there are /// more relationships to be flushed, the Xml document is completed and the /// StreamingZipPartStream is closed. /// private void FlushRelationshipsToPiece(bool isLastPiece) { Debug.Assert(_package.InStreamingCreation, "This method should only be called in streaming creation mode"); if (_dirty) { // No deletion in streaming production. Invariant.Assert(_relationships.Count > 0); // Dump the contents of _relationships to the stream and mark as saved. WriteRelationshipsAsXml( StreamingXmlWriter, _relationships, false, /* do not systematically write target mode */ true /* in streaming production */ ); if (!isLastPiece) { // Create a piece with the Xml just written. StreamingXmlWriter.Flush(); } _dirty = false; } if (isLastPiece && StreamingXmlWriter.WriteState != WriteState.Closed) { // Close Relationships tag. StreamingXmlWriter.WriteEndElement(); // Close the document. StreamingXmlWriter.WriteEndDocument(); // Create a terminal piece. This will set StreamingXmlWriter.WriteState to Closed. StreamingXmlWriter.Close(); } } #endregion #region Private Properties ////// Invoked strictly in streaming production to return and, if needed, /// lazily initialize _streamingXmlWriter. /// private XmlWriter StreamingXmlWriter { get { if (_streamingXmlWriter == null) { // Implement the writer on top of a streaming stream, so each Flush // will create a piece. EnsureRelationshipPart(); StreamingZipPartStream s = (StreamingZipPartStream) _relationshipPart.GetStream( FileMode.CreateNew, FileAccess.Write); _streamingXmlWriter = new XmlTextWriter(s, System.Text.Encoding.UTF8); // Write the top of the Xml document. StreamingXmlWriter.WriteStartDocument(); StreamingXmlWriter.WriteStartElement( RelationshipsTagName, PackagingUtilities.RelationshipNamespaceUri); } return _streamingXmlWriter; } } #endregion Private Properties //----------------------------------------------------- // // Private Members // //------------------------------------------------------ #region Private Members private List_relationships; private bool _dirty; // true if we have uncommitted changes to _relationships private Package _package; // our package - in case _sourcePart is null private PackagePart _sourcePart; // owning part - null if package is the owner private PackagePart _relationshipPart; // where our relationships are persisted private Uri _uri; // the URI of our relationship part private XmlWriter _streamingXmlWriter; //----------------------------------------------------- // // Private Fields // //----------------------------------------------------- // segment that indicates a relationship part private static readonly int _timestampLength = 16; private static readonly string RelationshipsTagName = "Relationships"; private static readonly string RelationshipTagName = "Relationship"; private static readonly string TargetAttributeName = "Target"; private static readonly string TypeAttributeName = "Type"; private static readonly string IdAttributeName = "Id"; private static readonly string XmlBaseAttributeName = "xml:base"; private static readonly string TargetModeAttributeName = "TargetMode"; private static readonly string[] RelationshipKnownNamespaces = new string[] { PackagingUtilities.RelationshipNamespaceUri }; #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- AuthenticationModuleElement.cs
- MtomMessageEncodingBindingElement.cs
- DataGridBoolColumn.cs
- IteratorDescriptor.cs
- TrackBar.cs
- DictionarySectionHandler.cs
- Queue.cs
- RoleBoolean.cs
- XmlSchemaObject.cs
- AlignmentXValidation.cs
- HttpsChannelListener.cs
- QilBinary.cs
- CompressStream.cs
- RenderContext.cs
- HtmlControl.cs
- IArgumentProvider.cs
- SimpleHandlerFactory.cs
- XsltLibrary.cs
- DataGridViewDataConnection.cs
- Roles.cs
- RecognitionEventArgs.cs
- WebPartManagerInternals.cs
- SqlXmlStorage.cs
- NativeMethodsCLR.cs
- XmlValidatingReaderImpl.cs
- CellParagraph.cs
- SerializationInfoEnumerator.cs
- DataStreams.cs
- ProcessModelInfo.cs
- ConnectionInterfaceCollection.cs
- TextViewBase.cs
- XomlCompilerParameters.cs
- EventWaitHandleSecurity.cs
- AdornerPresentationContext.cs
- DataGridViewCellCancelEventArgs.cs
- XmlSerializerAssemblyAttribute.cs
- DynamicQueryableWrapper.cs
- EventDescriptor.cs
- BuilderPropertyEntry.cs
- ControlTemplate.cs
- Trigger.cs
- AdPostCacheSubstitution.cs
- TypeConverters.cs
- CodeSnippetStatement.cs
- MobileControlPersister.cs
- DataGridViewDataConnection.cs
- ValidatorCollection.cs
- DesignTimeParseData.cs
- GroupBoxRenderer.cs
- OleDbErrorCollection.cs
- DataGridItem.cs
- CmsInterop.cs
- FileDialog.cs
- ConditionValidator.cs
- Expressions.cs
- ColumnHeader.cs
- GenerateHelper.cs
- RTTypeWrapper.cs
- FtpCachePolicyElement.cs
- WindowsFormsHostPropertyMap.cs
- FrameworkElementFactoryMarkupObject.cs
- MenuAdapter.cs
- FigureHelper.cs
- XPathDescendantIterator.cs
- LogicalExpr.cs
- ControlLocalizer.cs
- BufferModesCollection.cs
- SvcFileManager.cs
- DataProtection.cs
- XmlSignatureProperties.cs
- HttpGetProtocolReflector.cs
- xmlfixedPageInfo.cs
- PassportAuthenticationModule.cs
- ActivityExecutorDelegateInfo.cs
- DirectoryObjectSecurity.cs
- NavigationHelper.cs
- CodeMemberProperty.cs
- PassportPrincipal.cs
- NavigationProperty.cs
- Policy.cs
- ScrollItemProviderWrapper.cs
- WebReference.cs
- MethodRental.cs
- ItemsControlAutomationPeer.cs
- BindingExpressionUncommonField.cs
- XmlAttributeCollection.cs
- DynamicResourceExtensionConverter.cs
- PageClientProxyGenerator.cs
- Maps.cs
- DataGridTableCollection.cs
- WindowsIPAddress.cs
- ScaleTransform3D.cs
- RegexGroup.cs
- FontFaceLayoutInfo.cs
- ResXResourceWriter.cs
- EntitySetDataBindingList.cs
- CanonicalFontFamilyReference.cs
- Pens.cs
- RuntimeConfigurationRecord.cs
- PageBuildProvider.cs