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
- Script.cs
- FilteredXmlReader.cs
- MetadataException.cs
- DataComponentNameHandler.cs
- UpdateProgress.cs
- KeyboardDevice.cs
- SqlException.cs
- DataGrid.cs
- DataServiceQueryProvider.cs
- DataControlImageButton.cs
- CqlGenerator.cs
- MobileControlBuilder.cs
- SqlReorderer.cs
- Identity.cs
- NamespaceList.cs
- SqlProfileProvider.cs
- RecordsAffectedEventArgs.cs
- HttpListenerContext.cs
- AttributeEmitter.cs
- ContextMenu.cs
- OdbcConnectionStringbuilder.cs
- Material.cs
- VisualProxy.cs
- RemoteWebConfigurationHostServer.cs
- ComboBox.cs
- IntermediatePolicyValidator.cs
- PropertyConverter.cs
- ProfessionalColorTable.cs
- UpdateExpressionVisitor.cs
- SqlRetyper.cs
- InvalidOleVariantTypeException.cs
- WebBrowserContainer.cs
- MaterialGroup.cs
- SHA256Cng.cs
- Exceptions.cs
- DetailsViewUpdatedEventArgs.cs
- SQLStringStorage.cs
- WebRequestModuleElementCollection.cs
- SqlClientFactory.cs
- RequiredAttributeAttribute.cs
- SimplePropertyEntry.cs
- DataGridLength.cs
- TableRow.cs
- HashSet.cs
- JsonWriter.cs
- SessionStateContainer.cs
- BindingBase.cs
- Rect3DConverter.cs
- GrammarBuilder.cs
- NavigationPropertySingletonExpression.cs
- CanonicalFormWriter.cs
- StreamGeometry.cs
- RelatedPropertyManager.cs
- MultiPropertyDescriptorGridEntry.cs
- TypeBuilder.cs
- ErrorEventArgs.cs
- ExpressionParser.cs
- InkCanvas.cs
- HtmlHead.cs
- VisualState.cs
- mediaeventargs.cs
- WinFormsSecurity.cs
- ActiveXHost.cs
- PagerSettings.cs
- FunctionParameter.cs
- RectangleConverter.cs
- HealthMonitoringSectionHelper.cs
- WindowsStatic.cs
- ScriptManagerProxy.cs
- ApplyTemplatesAction.cs
- CaseDesigner.xaml.cs
- LineServicesRun.cs
- BamlRecordWriter.cs
- CompressStream.cs
- ListDictionaryInternal.cs
- OracleConnectionString.cs
- UndoManager.cs
- AnimatedTypeHelpers.cs
- EventOpcode.cs
- RIPEMD160Managed.cs
- DataExpression.cs
- EDesignUtil.cs
- CallbackException.cs
- DateBoldEvent.cs
- PageCache.cs
- RSAPKCS1SignatureFormatter.cs
- UriTemplateTrieNode.cs
- IChannel.cs
- HostnameComparisonMode.cs
- WebContext.cs
- ObjectSelectorEditor.cs
- ChtmlLinkAdapter.cs
- PropertyDescriptorGridEntry.cs
- AsyncInvokeContext.cs
- PermissionRequestEvidence.cs
- MatchingStyle.cs
- HierarchicalDataSourceControl.cs
- WebScriptEnablingBehavior.cs
- GorillaCodec.cs
- SessionIDManager.cs