|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Xml;
using System.IO;
using System.Security;
using System.ComponentModel.Design.Serialization;
using System.Windows.Xps.Packaging;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Markup;
using System.Windows.Threading;
using MS.Internal;
using System.Printing;
namespace System.Windows.Xps.Serialization
{
/// <summary>
/// This class defines all necessary methods that are necessary to provide
/// asynchronous serialization services for persisting an AVALON root object
/// into an XPS package. It glues together all necessary serializers and
/// type converters for different type of objects to produce the correct
/// serialized content in the package.
/// </summary>
public sealed class XpsSerializationManagerAsync :
XpsSerializationManager,
IXpsSerializationManagerAsync
{
/// <summary>
/// Constructor to create and initialize the XpsSerializationManagerAsync
/// </summary>
public
XpsSerializationManagerAsync(
BasePackagingPolicy packagingPolicy,
bool batchMode
):
base(packagingPolicy, batchMode)
{
_dispatcher = Dispatcher.CurrentDispatcher;
_serializationOperationCanceled = false;
this._currentPageXmlWriter = null;
this._isBatchWorkItemInProgress = false;
_operationStack = new Stack();
_batchOperationQueue = new Queue();
XpsDriverDocEventManager xpsDocEventManager = base.GetXpsDriverDocEventManager();
if (xpsDocEventManager != null)
{
XpsSerializationCompletedInternal += new XpsSerializationCompletedEventHandler(xpsDocEventManager.ForwardSerializationCompleted);
}
}
/// <summary>
///
/// </summary>
/// <exception cref="ArgumentNullException">serializedObject is NULL.</exception>
/// <exception cref="XpsSerializationException">serializedObject is not a supported type.</exception>
public
override
void
SaveAsXaml(
Object serializedObject
)
{
ArgumentNullException.ThrowIfNull(serializedObject);
if (!IsSerializedObjectTypeSupported(serializedObject))
{
throw new XpsSerializationException(SR.ReachSerialization_NotSupported);
}
if(Simulator == null)
{
Simulator = new ReachHierarchySimulator(this,
serializedObject);
}
if(!IsSimulating)
{
Simulator.BeginConfirmToXPSStructure(IsBatchMode);
IsSimulating = true;
}
if(IsBatchMode)
{
//
// Add the Visual received in to the queue
//
BatchOperationWorkItem batchOperationWorkItem = new BatchOperationWorkItem(BatchOperationType.batchWrite,
serializedObject);
_batchOperationQueue.Enqueue(batchOperationWorkItem);
PostSerializationTask(new DispatcherOperationCallback(InvokeSaveAsXamlBatchWorkItem));
}
else
{
ReachSerializer reachSerializer = GetSerializer(serializedObject);
if(reachSerializer != null)
{
//
// Prepare the context that is going to be pushed on the stack
//
SerializationManagerOperationContextStack
contextStack = new SerializationManagerOperationContextStack(reachSerializer,
serializedObject);
//
// At this stage, start calling another method which would peak at the stack
//
_operationStack.Push(contextStack);
PostSerializationTask(new DispatcherOperationCallback(InvokeSaveAsXamlWorkItem));
}
else
{
throw new XpsSerializationException(SR.ReachSerialization_NoSerializer);
}
}
}
internal
Object
InvokeSaveAsXamlWorkItem(
Object arg
)
{
//
// PreSharp uses message numbers that the C# compiler doesn't know about.
// Disable the C# complaints, per the PreSharp documentation.
#pragma warning disable 1634, 1691
//
// PreSharp complains about catching NullReference (and other) exceptions.
// This is an async model and we need to catch all exception ourselves and then
// send them to the completion delegate
#pragma warning disable 56500
try
{
if(!_serializationOperationCanceled)
{
if(_operationStack.Count > 0)
{
Object objectOnStack = _operationStack.Pop();
if(objectOnStack.GetType() ==
typeof(System.Windows.Xps.Serialization.SerializationManagerOperationContextStack))
{
SerializationManagerOperationContextStack context =
(SerializationManagerOperationContextStack)objectOnStack;
context.ReachSerializer.SerializeObject(context.SerializedObject);
}
else if(typeof(System.Windows.Xps.Serialization.ReachSerializerContext).IsAssignableFrom(objectOnStack.GetType()))
{
ReachSerializerContext context = (ReachSerializerContext)objectOnStack;
context.Serializer.AsyncOperation(context);
}
PostSerializationTask(new DispatcherOperationCallback(InvokeSaveAsXamlWorkItem));
}
else
{
Simulator.EndConfirmToXPSStructure(IsBatchMode);
XPSSerializationCompletionMethod();
}
}
}
catch(Exception e)
{
if(CriticalExceptions.IsCriticalException(e))
{
throw;
}
//
// Indicate that an error happened
//
bool canceled = false;
XpsSerializationCompletedEventArgs args = new XpsSerializationCompletedEventArgs(canceled,
null,
e);
_serializationOperationCanceled = true;
PostSerializationTask(new DispatcherOperationCallback(OnXPSSerializationCompleted), args);
return null;
}
#pragma warning restore 56500
#pragma warning restore 1634, 1691
return null;
}
/// <remarks>
/// The logic in the method IsAsyncWorkPending *MUST* mirror the logic in InvokeSaveAsXamlBatchWorkItem
/// IsAsyncWorkPending must return true when the manager is in a state that
/// causes InvokeSaveAsXamlBatchWorkItem to process pending items.
/// <remarks>
internal
Object
InvokeSaveAsXamlBatchWorkItem(
Object arg
)
{
//
// PreSharp uses message numbers that the C# compiler doesn't know about.
// Disable the C# complaints, per the PreSharp documentation.
#pragma warning disable 1634, 1691
//
// PreSharp complains about catching NullReference (and other) exceptions.
// This is an async model and we need to catch all exception ourselves and then
// send them to the completion delegate
#pragma warning disable 56500
try
{
// This logic must be mirrored in IsAsyncWorkPending see remarks.
if(!_serializationOperationCanceled)
{
if(!_isBatchWorkItemInProgress && _batchOperationQueue.Count > 0)
{
BatchOperationWorkItem batchOperationWorkItem = (BatchOperationWorkItem)_batchOperationQueue.Dequeue();
if(batchOperationWorkItem.OperationType == BatchOperationType.batchWrite)
{
_currentPageXmlWriter = Simulator.SimulateBeginFixedPage();
ReachSerializer reachSerializer = GetSerializer(batchOperationWorkItem.SerializedObject);
if(reachSerializer != null)
{
//
// Prepare the context that is going to be pushed on the stack
//
SerializationManagerOperationContextStack
contextStack = new SerializationManagerOperationContextStack(reachSerializer,
batchOperationWorkItem.SerializedObject);
//
// At this stage, start calling another method which would peak at the stack
//
_operationStack.Push(contextStack);
PostSerializationTask(new DispatcherOperationCallback(InvokeSaveAsXamlBatchWorkItem));
}
else
{
throw new XpsSerializationException(SR.ReachSerialization_NoSerializer);
}
_isBatchWorkItemInProgress = true;
}
else if(batchOperationWorkItem.OperationType == BatchOperationType.batchCommit)
{
Simulator.EndConfirmToXPSStructure(IsBatchMode);
XPSSerializationCompletionMethod();
}
}
else
{
if(_operationStack.Count > 0)
{
Object objectOnStack = _operationStack.Pop();
if(objectOnStack.GetType() ==
typeof(System.Windows.Xps.Serialization.SerializationManagerOperationContextStack))
{
SerializationManagerOperationContextStack context =
(SerializationManagerOperationContextStack)objectOnStack;
context.ReachSerializer.SerializeObject(context.SerializedObject);
}
else if(typeof(System.Windows.Xps.Serialization.ReachSerializerContext).IsAssignableFrom(objectOnStack.GetType()))
{
ReachSerializerContext context = (ReachSerializerContext)objectOnStack;
context.Serializer.AsyncOperation(context);
}
PostSerializationTask(new DispatcherOperationCallback(InvokeSaveAsXamlBatchWorkItem));
}
else
{
Simulator.SimulateEndFixedPage(_currentPageXmlWriter);
_isBatchWorkItemInProgress = false;
_currentPageXmlWriter = null;
}
}
}
}
catch(Exception e)
{
if(CriticalExceptions.IsCriticalException(e))
{
throw;
}
//
// Indicate that an error happened
//
bool canceled = false;
XpsSerializationCompletedEventArgs args = new XpsSerializationCompletedEventArgs(canceled,
null,
e);
_serializationOperationCanceled = true;
PostSerializationTask(new DispatcherOperationCallback(OnXPSSerializationCompleted), args);
return null;
}
#pragma warning restore 56500
#pragma warning restore 1634, 1691
return null;
}
/// <summary>
///
/// </summary>
public
void
CancelAsync(
)
{
bool canceled = true;
XpsSerializationCompletedEventArgs e = new XpsSerializationCompletedEventArgs(canceled,
null,
null);
_serializationOperationCanceled = true;
PostSerializationTask(new DispatcherOperationCallback(OnXPSSerializationCompleted), e);
}
/// <summary>
///
/// </summary>
public
override
void
Commit(
)
{
if(IsBatchMode && IsSimulating)
{
// Wait for pending items to complete synchronously
// otherwise the caller may dispose the underlying resource
// before our async operations can commit remaining data
WaitForPendingAsyncItems();
// Post a final commit item
// It's important to first drain all prior items before posting this item
// otherwise a pending item may post a new item causing undesirable interleaving
BatchOperationWorkItem batchOperationWorkItem = new BatchOperationWorkItem(BatchOperationType.batchCommit,
null);
_batchOperationQueue.Enqueue(batchOperationWorkItem);
PostSerializationTask(new DispatcherOperationCallback(InvokeSaveAsXamlBatchWorkItem));
// Wait for pending items to complete synchronously
WaitForPendingAsyncItems();
}
}
/*/// <summary>
///
/// </summary>
public
event
XPSSerializationProgressChangedEventHandler XPSSerializationProgressChanged;*/
/// <summary>
///
/// </summary>
public
event
XpsSerializationCompletedEventHandler XpsSerializationCompleted;
/// <summary>
/// XpsDriverDocEventManager subscribes for this event. We want to avoid chaining internal
/// subscribers as they might delay the event to be raised for external (app) subscribers.
/// </summary>
internal
event
XpsSerializationCompletedEventHandler XpsSerializationCompletedInternal;
/// <summary>
///
/// </summary>
internal
override
Type
GetSerializerType(
Type objectType
)
{
Type serializerType = null;
if (typeof(System.Windows.Documents.FixedDocument).IsAssignableFrom(objectType))
{
serializerType = typeof(FixedDocumentSerializerAsync);
}
else if (typeof(System.Windows.Documents.PageContentCollection).IsAssignableFrom(objectType))
{
serializerType = typeof(ReachPageContentCollectionSerializerAsync);
}
else if(typeof(System.Windows.Documents.PageContent).IsAssignableFrom(objectType))
{
serializerType = typeof(ReachPageContentSerializerAsync);
}
else if(typeof(System.Windows.Controls.UIElementCollection).IsAssignableFrom(objectType))
{
serializerType = typeof(ReachUIElementCollectionSerializerAsync);
}
else if(typeof(System.Windows.Documents.FixedPage).IsAssignableFrom(objectType))
{
serializerType = typeof(FixedPageSerializerAsync);
}
else if (typeof(System.Windows.Documents.FixedDocumentSequence).IsAssignableFrom(objectType))
{
serializerType = typeof(DocumentSequenceSerializerAsync);
}
else if (typeof(System.Windows.Documents.DocumentReferenceCollection).IsAssignableFrom(objectType))
{
serializerType = typeof(ReachDocumentReferenceCollectionSerializerAsync);
}
else if (typeof(System.Windows.Documents.DocumentReference).IsAssignableFrom(objectType))
{
serializerType = typeof(ReachDocumentReferenceSerializerAsync);
}
else if (typeof(System.Windows.Documents.DocumentPaginator).IsAssignableFrom(objectType))
{
serializerType = typeof(DocumentPaginatorSerializerAsync);
}
else if (typeof(System.Windows.Documents.DocumentPage).IsAssignableFrom(objectType))
{
serializerType = typeof(DocumentPageSerializerAsync);
}
else if (typeof(System.Windows.Media.Visual).IsAssignableFrom(objectType))
{
serializerType = typeof(ReachVisualSerializerAsync);
}
else if (typeof(System.Printing.PrintTicket).IsAssignableFrom(objectType))
{
serializerType = typeof(PrintTicketSerializer);
}
if(serializerType == null)
{
base.GetSerializerType(objectType);
}
return serializerType;
}
Stack
IXpsSerializationManagerAsync.OperationStack
{
get
{
return _operationStack;
}
}
private
void
PostSerializationTask(
DispatcherOperationCallback taskItem
)
{
_dispatcher.BeginInvoke(DispatcherPriority.Background,
taskItem,
null);
}
private
void
PostSerializationTask(
DispatcherOperationCallback taskItem,
object arg
)
{
_dispatcher.BeginInvoke(DispatcherPriority.Background,
taskItem,
arg);
}
private
void
XPSSerializationCompletionMethod(
)
{
bool canceled = false;
XpsSerializationCompletedEventArgs e = new XpsSerializationCompletedEventArgs(canceled,
null,
null);
PostSerializationTask(new DispatcherOperationCallback(OnXPSSerializationCompleted), e);
}
private
object
OnXPSSerializationCompleted(
object operationState
)
{
XpsSerializationCompletedEventArgs e = operationState as XpsSerializationCompletedEventArgs;
if (XpsSerializationCompleted != null)
{
XpsSerializationCompleted(this, e);
}
if (XpsSerializationCompletedInternal != null)
{
XpsSerializationCompletedInternal(this, e);
}
return null;
}
private
void
WaitForPendingAsyncItems(
)
{
do
{
_dispatcher.Invoke(DispatcherPriority.Background, (DispatcherOperationCallback)(_ => null), null);
}
while (IsAsyncWorkPending());
}
/// <remarks>
/// The logic in the method IsAsyncWorkPending *MUST* mirror the logic in InvokeSaveAsXamlBatchWorkItem
/// IsAsyncWorkPending must return true when the manager is in a state that
/// causes InvokeSaveAsXamlBatchWorkItem to process pending items.
/// <remarks>
private
bool
IsAsyncWorkPending(
)
{
// This logic must mirror InvokeSaveAsXamlBatchWorkItem see remarks.
if(!_serializationOperationCanceled)
{
if(!_isBatchWorkItemInProgress && _batchOperationQueue.Count > 0)
{
// InvokeSaveAsXamlBatchWorkItem is expected to process an item from _batchOperationQueue
return true;
}
else {
if (_operationStack.Count > 0)
{
// InvokeSaveAsXamlBatchWorkItem is expected to process an item from _operationStack
return true;
}
}
}
return false;
}
private
Dispatcher _dispatcher;
private
Stack _operationStack;
private
Queue _batchOperationQueue;
private
bool _serializationOperationCanceled;
private
XmlWriter _currentPageXmlWriter;
private
bool _isBatchWorkItemInProgress;
};
internal class SerializationManagerOperationContextStack
{
public
SerializationManagerOperationContextStack(
ReachSerializer serializer,
Object serializedObject
)
{
this._serializer = serializer;
this._serializedObject = serializedObject;
}
public
ReachSerializer
ReachSerializer
{
get
{
return _serializer;
}
}
public
Object
SerializedObject
{
get
{
return _serializedObject;
}
}
private
ReachSerializer _serializer;
private
Object _serializedObject;
}
/// <summary>
///
/// </summary>
public
delegate
void
XpsSerializationProgressChangedEventHandler(
object sender,
XpsSerializationProgressChangedEventArgs e
);
/// <summary>
///
/// </summary>
public
delegate
void
XpsSerializationCompletedEventHandler(
object sender,
XpsSerializationCompletedEventArgs e
);
/// <summary>
///
/// </summary>
public class XpsSerializationCompletedEventArgs :
AsyncCompletedEventArgs
{
/// <summary>
///
/// </summary>
public
XpsSerializationCompletedEventArgs(
bool canceled,
object state,
Exception exception
) :
base(exception, canceled, state)
{
}
};
/// <summary>
///
/// </summary>
public class XpsSerializationProgressChangedEventArgs :
ProgressChangedEventArgs
{
/// <summary>
///
/// </summary>
public
XpsSerializationProgressChangedEventArgs(
XpsWritingProgressChangeLevel writingLevel,
int pageNumber,
int progressPercentage,
object userToken) :
base( progressPercentage, userToken )
{
this._pageNumber = pageNumber;
this._writingLevel = writingLevel;
}
/// <summary>
///
/// </summary>
public
XpsWritingProgressChangeLevel
WritingLevel
{
get
{
return _writingLevel;
}
}
/// <summary>
///
/// </summary>
public int PageNumber
{
get
{
return _pageNumber;
}
}
private int _pageNumber = 0;
private XpsWritingProgressChangeLevel _writingLevel = XpsWritingProgressChangeLevel.None;
}
/// <summary>
///
/// </summary>
public class XpsSerializationPrintTicketRequiredEventArgs :
EventArgs
{
/// <summary>
///
/// </summary>
public
XpsSerializationPrintTicketRequiredEventArgs(
PrintTicketLevel printTicketLevel,
int sequence
)
{
_level = printTicketLevel;
_sequence = sequence;
_printTicket = null;
_modified = false;
}
/// <summary>
///
/// </summary>
public
PrintTicket
PrintTicket
{
set
{
_printTicket = value;
}
get
{
return _printTicket;
}
}
/// <summary>
///
/// </summary>
public
PrintTicketLevel
PrintTicketLevel
{
get
{
return _level;
}
}
internal
bool
Modified
{
set
{
_modified = value;
}
get
{
return _modified;
}
}
/// <summary>
///
/// </summary>
public
int
Sequence
{
get
{
return _sequence;
}
}
private
int _sequence;
private
bool _modified;
private
PrintTicket _printTicket;
private
PrintTicketLevel _level;
};
/// <summary>
///
/// </summary>
public enum PrintTicketLevel
{
/// <summary>
///
/// </summary>
None = 0,
/// <summary>
///
/// </summary>
FixedDocumentSequencePrintTicket = 1,
/// <summary>
///
/// </summary>
FixedDocumentPrintTicket = 2,
/// <summary>
///
/// </summary>
FixedPagePrintTicket = 3
};
/// <summary>
///
/// </summary>
public enum XpsWritingProgressChangeLevel
{
/// <summary>
///
/// </summary>
None = 0,
/// <summary>
///
/// </summary>
FixedDocumentSequenceWritingProgress = 1,
/// <summary>
///
/// </summary>
FixedDocumentWritingProgress = 2,
/// <summary>
///
/// </summary>
FixedPageWritingProgress = 3
};
internal enum BatchOperationType
{
batchWrite = 1,
batchCommit = 2
};
internal class BatchOperationWorkItem
{
public
BatchOperationWorkItem(
BatchOperationType type,
Object serializedObject
)
{
this._type = type;
this._serializedObject = serializedObject;
}
public
BatchOperationType
OperationType
{
get
{
return _type;
}
}
public
Object
SerializedObject
{
get
{
return _serializedObject;
}
}
private
BatchOperationType _type;
Object _serializedObject;
};
}
|