Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / TrustUi / MS / Internal / documents / Application / TransactionalPackage.cs / 1 / TransactionalPackage.cs
//------------------------------------------------------------------------------ //// Copyright (C) Microsoft Corporation. All rights reserved. // //// This class represents a package which behaves similar to a Word document. // // - original package is treated as read-only // - edits in the meantime are done lazily to a temporary package // - when the user Commits their changes the temporary package will be filled // and then the temporary file will be copied to the comparee location // - if the user discards the package, original will be untouched and the // temporary in theory, given the original could be recovered // // - normalizedUri is used only for look ups, the original Uri is what we // should always store // // History: // 07/04/2005: [....]: Initial implementation. // 08/28/2005: [....]: Refactored, extracting responsiblities that were not // package related. // Adjusted spacing and style to match team better //----------------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.IO.Packaging; using System.Text; using System.Security; using System.Security.Permissions; using System.Windows.TrustUI; using MS.Internal; namespace MS.Internal.Documents.Application { ////// This class represents a Package which does not alter the original /// and writes the changes to a temporary package (when provided) as a /// type of change log; leaving the original untouched. /// ////// In the descriptions below the following terms are used: /// /// Proxy: This is the reference being given to callers that /// contains underlying objects. /// Active: The underlying object that the proxy should pass calls to. /// Temp: This is the writeable object that contains changes. /// Original: This is the read only object that has the source data. /// internal class TransactionalPackage : Package, IDisposable { #region Constructors //------------------------------------------------------------------------- // Constructors //------------------------------------------------------------------------- ////// Requires an existing open Package; and returns a package which will /// capture changes with out applying them to the original. /// See the class description for details. /// ////// /// Package package = new TransactionalPackage( /// Package.Open(source, FileMode.Open, FileAccess.Read)); /// /// An open package. ////// Critical: /// - sets the original package /// - sets the temp package to an empty value (avoid build warnings) /// NotSafe: /// - only our caller knows where the stream came from, it must be from /// the user specified location /// [SecurityCritical] internal TransactionalPackage(Stream original) : base(FileAccess.ReadWrite) { if (original == null) { throw new ArgumentNullException("original"); } Package originalPackage = Package.Open(original); _originalPackage = new SecurityCriticalDataForSet(originalPackage); _tempPackage = new SecurityCriticalDataForSet (null); } #endregion Constructors #region Internal Methods //-------------------------------------------------------------------------- // Internal Methods //------------------------------------------------------------------------- /// /// /// Critical: /// - we are writing data out to a stream we were simply given /// although this does not require priveledge, if the data is protected /// (RM) we want to be sure we write it to the correct location aslo /// operations like (DigSig) 'Sign' can be sure what we see is what /// we will Sign & write /// NotSafe: /// - audit caller must ensure stream is from PackageDocument /// [SecurityCritical] internal void EnableEditMode(Stream workspace) { if (workspace == null) { throw new ArgumentNullException("workspace"); } if (!workspace.CanWrite) { throw new ArgumentException( SR.Get(SRID.PackagingWriteNotSupported), "workspace"); } Package temporaryPackage = Package.Open( workspace, FileMode.Create, FileAccess.ReadWrite); _tempPackage = new SecurityCriticalDataForSet(temporaryPackage); } /// /// /// /// Critical: /// - we create a temporary package using a stream provided to us /// NotSafe: /// - only the caller participating in constructing the stream can validate /// that this is an XpsDocument stream /// [SecurityCritical] internal virtual void MergeChanges(Stream target) { if (target == null) { throw new ArgumentNullException("target"); } if (!target.CanWrite) { throw new InvalidOperationException(); } if (_tempPackage.Value != null) { Package destination = Package.Open( target, FileMode.Open, FileAccess.ReadWrite); foreach (PackagePart part in _tempPackage.Value.GetParts()) { if (destination.PartExists(part.Uri)) { Trace.SafeWrite( Trace.Packaging, "Over writing existing part {0}({1}).", part.Uri, part.ContentType); CopyPackagePartStream( part, destination.GetPart(part.Uri)); } else { Trace.SafeWrite( Trace.Packaging, "Creating new part from edited part {0}({1}).", part.Uri, part.ContentType); CopyPackagePartStream( part, destination.CreatePart( part.Uri, part.ContentType)); } } destination.Flush(); destination.Close(); Trace.SafeWrite(Trace.Packaging, "Merge package closed."); } } ////// Critical: /// - sets the original package /// NotSafe: /// - only our caller knows where the stream came from, it must be from /// the user specified location /// [SecurityCritical] internal void Rebind(Stream newOriginal) { if (newOriginal == null) { throw new ArgumentNullException("newOriginal"); } // close this as we will open a new one _originalPackage.Value.Close(); _trashCan.Add(_originalPackage.Value); _isDirty = false; Package newPackage = Package.Open(newOriginal, FileMode.Open, FileAccess.Read); // remap parts for people who keep references around after we rebind foreach (PackagePart part in newPackage.GetParts()) { Uri normalizedPartUri = PackUriHelper.GetNormalizedPartUri(part.Uri); if (_activeParts.ContainsKey(normalizedPartUri)) { _activeParts[normalizedPartUri].Target = newPackage.GetPart(part.Uri); } } _originalPackage.Value = newPackage; } #endregion Internal Methods //-------------------------------------------------------------------------- // Internal Properties //-------------------------------------------------------------------------- #region Internal Properties ////// Indicates whether the TransactionalPackage has been dirtied. /// internal bool IsDirty { get { return _isDirty; } } #endregion Internal Properties #region Protected Methods - Package Overrides //------------------------------------------------------------------------- // Protected Methods - Package Overrides //-------------------------------------------------------------------------- ////// Creates a new PackagePart. /// ////// When creating a new PackagePart we must: /// a) ensure the part does not exist in package /// b) ensure there is a writable package /// c) create a temp part /// d) update active part reference to the temp part /// /// What if a PackagePart with the same Uri already exists? /// Package.CreatePart checks for this. /// /// Do we need to worry about updating relationships and other parts? /// Relationships are a part and are thus intrinsically handled. /// /// Uri for the part to create. /// Content type string. /// Compression options. ///A new PackagePart. protected override PackagePart CreatePartCore( Uri partUri, string contentType, CompressionOption compressionOption) { // Skipping parameter validation as it is done by CreatePart. EnsureTempPackage(); // the underlying temp package does all the physical work PackagePart result = _tempPackage.Value.CreatePart( partUri, contentType, compressionOption); Uri normalizedPartUri = PackUriHelper.GetNormalizedPartUri(partUri); result = new WriteableOnDemandPackagePart( this, result, TempPackagePartFactory); _activeParts.Add(normalizedPartUri, (WriteableOnDemandPackagePart)result); Trace.SafeWrite( Trace.Packaging, "New part {0}({1})#{2} created.", result.Uri, result.ContentType, result.GetHashCode()); return result; } ////// Deletes a PackagePart. /// ////// When deleting a PackagePart we must: /// a) ensure there is a writable package /// b) remove the temp part /// /// What if the part was already deleted? /// What if delete is the first operation? /// Then a relationship part in the temorary package would be active; /// and the case(s) would be handled by Package.DeletePart. /// /// What if delete is called after a part is created / edited? /// No different then the other cases. /// /// What if we call DeletePart on temp Package? /// Unsure why relationships are not updated twice; once by base /// accessing relationship part, then a second when the underlying /// implementor does for Package.DeletePart. /// /// Note: We should explore only cleaning up the stream; as the rest is /// likely handed by base interacting with the relationship parts. /// /// Uri for the part to delete. protected override void DeletePartCore(Uri partUri) { // Skipping parameter validation as it is done by CreatePart. if (_tempPackage.Value.PartExists(partUri)) { _tempPackage.Value.DeletePart(partUri); Trace.SafeWrite(Trace.Packaging, "Part {0} deleted.", partUri); } Uri normalizedPartUri = PackUriHelper.GetNormalizedPartUri(partUri); if (_activeParts.ContainsKey(normalizedPartUri)) { _activeParts.Remove(normalizedPartUri); } } ////// Release underlying resources; in this case our packages. /// /// Indicates if we are disposing. ////// Critical: /// - we are setting original and temp package /// TreatAsSafe: /// - we are setting them to null as we are disposing /// [SecurityCritical, SecurityTreatAsSafe] protected override void Dispose(bool disposing) { Trace.SafeWrite(Trace.Packaging, "Dispose was called with {0}.", disposing); if (disposing) { if (_tempPackage.Value != null) { ((IDisposable)_tempPackage.Value).Dispose(); _tempPackage.Value = null; } if (_originalPackage.Value != null) { ((IDisposable)_originalPackage.Value).Dispose(); _originalPackage.Value = null; } _activeParts.Clear(); } base.Dispose(disposing); } ////// Flushes our underlying packages. /// ////// Only the original package will have data integrity at this point. /// This is by design as the scope of the class is to be a change log. /// protected override void FlushCore() { if (_tempPackage.Value != null) { _tempPackage.Value.Flush(); } } ////// Returns an existing PackagePart. /// ////// When getting an existing PackagePart we must: /// a) Create a proxy (WriteableOnDemandPackagePart) if there is not /// already one; if there is we just return it. /// b) We must return the same instance of the proxy for any request /// for that part, as new instances will not have any way of /// knowing which internal part is 'active' (original/temp) within /// the proxy. /// /// What if the part does not exist? /// What if the part has been deleted? /// These cases are handled by Package.GetPart. /// /// What if the part ends up being edited? /// That is the reason for the proxy, on edit the internal reference /// will be updated to the temp object and it will service the call. /// /// What if a part has already been edited? /// That is why we must return the active part. /// /// The Uri of the part to return. ///An existing PackagePart. protected override PackagePart GetPartCore(Uri partUri) { // Skipping parameter validation as it is done by CreatePart. PackagePart result = null; Uri normalizedPartUri = PackUriHelper.GetNormalizedPartUri(partUri); #if DEBUG if (_activeParts.ContainsKey(normalizedPartUri)) { Trace.SafeWrite( Trace.Packaging, "WARNING: GetPartCore called multiple times for {0}.", partUri); } #endif // We can get the part from three places, which we check in this order: // 1) Our saved list of active parts. It's important to get it from // here if possible because all references should point to a single // instance of our WriteableOnDemandPackagePart class. // 2) The temp package. If there is a change to a part it will be here, // and we want to return the user's changes. // 3) The original package. // Even if the part exists in our list of active parts, as part of our // contract with the Packaging team we still must use PartExists to // check if the part is actually present in either the temporary or the // original package. If the part does not exist in either the original // or the temporary package, this method will return null. bool canGetFromTempPackage = (_tempPackage.Value != null) && (_tempPackage.Value.PartExists(partUri)); bool canGetFromOriginalPackage = canGetFromTempPackage ? false : _originalPackage.Value.PartExists(partUri); if (_activeParts.ContainsKey(normalizedPartUri) && (canGetFromTempPackage || canGetFromOriginalPackage)) { result = _activeParts[normalizedPartUri]; } else if (canGetFromTempPackage) { result = _tempPackage.Value.GetPart(partUri); result = new WriteableOnDemandPackagePart( this, result, TempPackagePartFactory); _activeParts.Add(normalizedPartUri, (WriteableOnDemandPackagePart)result); Trace.SafeWrite( Trace.Packaging, "GetPartCore returned {0}({1})#{2} a temp part.", partUri, result.ContentType, result.GetHashCode()); } else if (canGetFromOriginalPackage) { PackagePart original = _originalPackage.Value.GetPart(partUri); result = new WriteableOnDemandPackagePart( this, original, TempPackagePartFactory); _activeParts.Add(normalizedPartUri, (WriteableOnDemandPackagePart)result); Trace.SafeWrite( Trace.Packaging, "GetPartCore returned {0}({1})#{2} a new proxy.", partUri, result.ContentType, result.GetHashCode()); } return result; } ////// Will return all the PackageParts in the Package. /// ////// WARNING: This implementation is based on GetParts implementations /// current behavior. We only expect to be called once on open and then /// our base implementation should handle things. /// ///All PackageParts. protected override PackagePart[] GetPartsCore() { // need to call get parts from the underlying reading package PackagePartCollection parts = _originalPackage.Value.GetParts(); // // a temporary list of proxied package parts which will be use to fill return value List_proxiedParts = new List (); // for all parts not in the active list create a proxy for them // and add to active table foreach (PackagePart part in parts) { _proxiedParts.Add(GetPartCore(part.Uri)); } // return the active table PackagePart[] result = new PackagePart[_proxiedParts.Count]; _proxiedParts.CopyTo(result, 0); return result; } #endregion Protected Methods - Package Overrides #region Protected Properties //------------------------------------------------------------------------- // Protected Properties //------------------------------------------------------------------------- protected SecurityCriticalDataForSet TempPackage { get { return _tempPackage; } } #endregion Protected Properties #region Private Methods //------------------------------------------------------------------------- // Private Methods //-------------------------------------------------------------------------- /// /// A simple stream copy from one PackagePart to another. /// /// The source PackagePart. /// The comparee PackagePart. private static void CopyPackagePartStream(PackagePart original, PackagePart copy) { Stream source = original.GetStream(FileMode.Open, FileAccess.Read); Stream target = copy.GetStream(FileMode.Create, FileAccess.ReadWrite); StreamHelper.CopyStream(source, target); source.Close(); target.Close(); } ///private void EnsureTempPackage() { // if we can not edit ask for it if (_tempPackage.Value == null) { DocumentManager.CreateDefault().EnableEdit(null); } // if we still don't have it fail if (_tempPackage.Value == null) { throw new InvalidOperationException( SR.Get(SRID.PackagingWriteNotSupported)); } } /// /// Will create the temporary package part. /// ////// /// Designed for use as a call back from WriteableOnDemandPackagePart. /// On being called we must: /// a) ensuring there is a writable package /// b) create the 'temp' part /// c) copy the original part /// /// The original PackagePart to copy. ///A writeable PackagePart. private PackagePart TempPackagePartFactory(PackagePart packagePart) { if (packagePart == null) { throw new ArgumentNullException("packagePart"); } EnsureTempPackage(); Uri partUri = packagePart.Uri; PackagePart temp = null; if (!_tempPackage.Value.PartExists(partUri)) { Trace.SafeWrite( Trace.Packaging, "Temporary part {0} does not exist.", partUri); _isDirty = true; temp = _tempPackage.Value.CreatePart( partUri, packagePart.ContentType, packagePart.CompressionOption); Trace.SafeWrite( Trace.Packaging, "Temporary part {0}({1}) created.", temp.Uri, temp.ContentType); CopyPackagePartStream(packagePart, temp); } else { temp = _tempPackage.Value.GetPart(partUri); Trace.SafeWrite( Trace.Packaging, "Temporary part {0}({1}) existed.", temp.Uri, temp.ContentType); } return temp; } #endregion Private Methods #region Private Fields //------------------------------------------------------------------------- // Private Fields //-------------------------------------------------------------------------- ////// Parts that have proxies constructed; this occurs when the are /// referenced. /// private Dictionary_activeParts = new Dictionary (); /// /// The original Package; this one is to be treated as read-only. /// private SecurityCriticalDataForSet_originalPackage; /// /// The temporary Package; this is the one we work in. /// private SecurityCriticalDataForSet_tempPackage; private List _trashCan = new List (); private bool _isDirty; #endregion Private Fields } } // 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
- KeyManager.cs
- ping.cs
- GetChildSubtree.cs
- ContentType.cs
- NoneExcludedImageIndexConverter.cs
- ClientSettings.cs
- PointLight.cs
- OrderByBuilder.cs
- HostingPreferredMapPath.cs
- StringUtil.cs
- OracleCommand.cs
- InfiniteIntConverter.cs
- FunctionCommandText.cs
- printdlgexmarshaler.cs
- SamlConstants.cs
- BatchParser.cs
- FileDialog_Vista.cs
- SystemDiagnosticsSection.cs
- ProcessModelInfo.cs
- SeekableMessageNavigator.cs
- GridView.cs
- AffineTransform3D.cs
- dataSvcMapFileLoader.cs
- ScaleTransform3D.cs
- Comparer.cs
- XmlStreamNodeWriter.cs
- ObjectQueryProvider.cs
- CodeTypeDelegate.cs
- StyleBamlRecordReader.cs
- DocumentReferenceCollection.cs
- XmlArrayItemAttributes.cs
- FtpRequestCacheValidator.cs
- TTSEngineTypes.cs
- InvalidProgramException.cs
- XmlMapping.cs
- MenuItemBindingCollection.cs
- TextReader.cs
- OperationCanceledException.cs
- CalendarButtonAutomationPeer.cs
- ListControlBuilder.cs
- Vector3DConverter.cs
- TypeValidationEventArgs.cs
- EventlogProvider.cs
- TextBox.cs
- Pair.cs
- MsmqInputSessionChannelListener.cs
- PersonalizationStateInfo.cs
- ReferencedCollectionType.cs
- CommonObjectSecurity.cs
- TypeReference.cs
- CodeAttributeArgument.cs
- DecoderExceptionFallback.cs
- FontCacheUtil.cs
- DoubleKeyFrameCollection.cs
- TextTreeNode.cs
- DecodeHelper.cs
- COM2EnumConverter.cs
- XmlElement.cs
- ReadOnlyState.cs
- PerformanceCounterPermissionAttribute.cs
- nulltextcontainer.cs
- _Semaphore.cs
- IgnoreDeviceFilterElementCollection.cs
- ButtonAutomationPeer.cs
- DoubleStorage.cs
- CellParaClient.cs
- DbModificationClause.cs
- CodeObject.cs
- VectorCollection.cs
- MimeObjectFactory.cs
- StorageEntitySetMapping.cs
- GridViewColumnCollection.cs
- XLinq.cs
- SecurityKeyIdentifierClause.cs
- SafeNativeMethodsMilCoreApi.cs
- SelectedCellsCollection.cs
- ListCollectionView.cs
- OdbcConnectionPoolProviderInfo.cs
- AttributeAction.cs
- Floater.cs
- FusionWrap.cs
- DataTableTypeConverter.cs
- LocatorPartList.cs
- QilSortKey.cs
- errorpatternmatcher.cs
- CreateUserWizardStep.cs
- Font.cs
- StringCollection.cs
- StrongNameUtility.cs
- TextFormatterImp.cs
- ArgumentsParser.cs
- TypeUsage.cs
- FontWeights.cs
- QueryPageSettingsEventArgs.cs
- CalendarAutomationPeer.cs
- TreeSet.cs
- ColorTransformHelper.cs
- SystemInformation.cs
- Internal.cs
- ConfigurationSectionCollection.cs