/// /// Copyright © 2003-2008 JetBrains s.r.o. /// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder). /// using System; using System.Collections; using System.Diagnostics; using System.Windows.Forms; namespace JetBrains.Omea.OpenAPI { /// /// A helper class that automatically marshals resource write operations to the resource thread. /// /// /// Do not modify resources in non-resource threads! At most times when Omea is running (except for the time when the /// method is called), only one thread is designated /// as the resource store write thread, and all operations that modify the resource store /// (creating resources, changing resource properties, deleting resources) must be executed /// in that thread. The class provides an easy way to invoke a resource write /// operation in the resource thread synchronously or asynchronously. /// Even though a proxy is not needed when working on a resource thread, proxying resource modifications will not affect the performance significantly in this case. /// There is no global ResourceProxy instance. A new proxy object should be created each time you're going to modify another resource. A proxy instance is bound to a resource passed to its constructor for the whole proxy lifetime, and the wrapped resource can be extracted with . /// There are two resource proxy usage scenarios: Immediate and Batch. /// /// /// Immediate Mode /// /// In the Immediate case a property gets modified or deleted immediately, either synchronously or asynchronously. should not be called. /// /// /// /// Batch Mode /// /// In the Batch case a pair of ( and ) or ( and ) calls is made. Any property modifications invoked inside this pair are not applied immediately but are deferred until one of the End… functions is called. When it occurs, all the modifications are committed either synchronously or asynchronously. /// To check if a particular modification function supports working in a batch, see its description. Note that Batch mode does not affect the property modification notifications that arrive to the event listeners. The only difference is that all the notifications happen not before the batch is committed with either End… function. It is unspecified whether all the modifications of a single property in a batch are optimized into a single modification or occur one by one as invoked (that also applies to property change notifications). /// /// /// /// Property modifications occur synchronously and asynchronously. Here synchronous modification means that the calling thread halts execution until the property assignment completes in the resource thread, while asynchronous modification just gets queued to the resource thread job list and execution continues without waiting for the property change to occur. /// You may control the priority of asynchronous property assignment by altering the proeprty value. It is recommended to assign the highest priority () to assignments initiated from the user interface in order to decrease the UI response lag, and execute other assignments with lower priority values. See the constructor overload that takes the priority parameter if you plan on using the “inline” version. /// Most of the functions have both asynchronous and synchronous versions indicated by the presence or absence of "…Async" suffix. Note that synchronous or asynchronous versions of individual property assignments behave identically when working in Batch mode (after calling ) and synchronous or asynchronous nature of the final commit operation is ruled by the End… function that you call, either for synchronous commit or for asynchronous commit. /// /// /// Schedule an asynchronous resource modification and continue execution, “inline” usage: /// /// // Assign a string value to a property accessed by its property name /// // Execution is asynchronous, in immediate mode /// new ResourceProxy( resource ).SetPropAsync( "Name", "John Doe" ); /// /// Modify a property and wait until the modification completes in the resource thread: /// /// // Assign a boolean value to a property accessed by its ID /// // Execution is synchronous, in immediate mode /// ResourceProxy proxy = new ResourceProxy( resource ); /// proxy.SetProp( 239, true ); /// /// Schedule a modification of a set of properties: /// /// ResourceProxy proxy = new ResourceProxy( resource ); /// /// // Enter Batch mode /// proxy.BeginUpdate(); /// /// // Schedule property modifications /// // Note that the resource does not get modified immediately when we /// // execute SetProp and notification events do not fire /// // Using SetPropAsync instead of SetProp would produce the same effect here /// // because we are in the Batch mode /// proxy.SetProp( "Name", "John Doe" ); /// proxy.SetProp( "Age", 45 ); /// proxy.SetProp( "Permitted", true ); /// /// // Commit the changes, do not wait for completion /// // After this call the notification events will fire /// EndUpdateAsync(); /// /// // If we had used EndUpdate() here instead we would have waited for /// // the modification to complete on the resource thread /// /// public class ResourceProxy { /// /// Type of the pending operation. /// internal enum OperationType { /// /// The Resource Proxy is about to assign a new property value to the encapsulated resource. /// SetProp, /// /// The Resource Proxy is about to change the user interface name of the encapsulated resource. /// SetDisplayName, /// /// The Resource Proxy is about to link the encapsulated resource to another one. /// AddLink, /// /// The Resource Proxy is about to unlink the encapsulated resource from another one. /// DeleteLink } ; private class PendingOperation { private int _propID; private object _target; private OperationType _opType; internal PendingOperation( int propID, object target, OperationType opType ) { _propID = propID; _target = target; _opType = opType; } internal int PropID { get { return _propID; } } internal object Target { get { return _target; } } internal OperationType OpType { get { return _opType; } } } private IResource _resource; private string _newResourceType = null; private ArrayList _pendingUpdates = null; private int _batchUpdateCount = 0; private JobPriority _asyncPriority = JobPriority.Normal; /// /// Creates a resource proxy for the specified resource. /// /// Encapsulated resource. /// /// After you create a proxy that encapsulates the specific resource, all the calls that modify that resource will be done through this proxy instance. /// Use the property to access the encapsulated resource at a later time. /// Even though a proxy is not needed when working on a resource thread, proxying resource modifications will not affect the performance. /// public ResourceProxy( IResource resource ) { if( resource == null ) throw new ArgumentNullException( "resource" ); _resource = resource; } /// /// Creates a resource proxy for the specified resource. /// /// Encapsulated resource. /// Priority for the asynchonous operations, see for details. /// /// Use this constructor overload if you need to specify the priority of asynchronous operations, for example, when the modification is done in response to a user action and must occur as soon as possible. In this case, the value must be used. /// After you create a proxy that encapsulates the specific resource, all the calls that modify that resource will be done through this proxy instance. /// Use the property to access the encapsulated resource at a later time. allows to get or set the priority level after the proxy is created. /// Even though a proxy is not needed when working on a resource thread, proxying resource modifications will not affect the performance. /// /// new ResourceProxy(res, JobPriority.Immediate).SetPropAsync("Count", 239); /// 2.1 public ResourceProxy(IResource resource, JobPriority asyncpriority) { if( resource == null ) throw new ArgumentNullException( "resource" ); _resource = resource; _asyncPriority = asyncpriority; } /// /// Internal ctor, used in the BeginNewResource static function /// private ResourceProxy( string resType ) { _newResourceType = resType; _resource = null; } /// /// Creates a new ResourceProxy instance for asynchronously creating an instance /// of a resource of a specified type. /// /// The type of the resource to create. /// The proxy instance. /// You can set properties and add links to the resource proxy instance. /// The actual resource is created and saved when or /// is called. public static ResourceProxy BeginNewResource( string resType ) { if( resType == null ) throw new ArgumentNullException( "resType" ); ResourceProxy proxy = new ResourceProxy( resType ); proxy._batchUpdateCount++; return proxy; } /// /// The resource wrapped by this instance. /// /// /// The resource wrapped by this instance. /// /// This is the resource which is modified when you call methods of this proxy instance. /// /// /// IResource resource = … /// /// ResourceProxy proxy = new ResourceProxy( resource ); /// Debug.Assert( Object.ReferenceEquals( resource, proxy.Resource ) ); /// /// public IResource Resource { get { return _resource; } } /// /// Priority of the asynchronous job that performs modifications of the resource. /// /// /// Priority of the asynchronous job that performs modifications of the resource. /// /// /// All the resource modifications must be performed in the resource thread. implements the marshalling for you. Synchronous modifications (by either , , , , or without prior call to ; or by a call to ) are executed with and the calling thread waits for the modification to complete. The priority setting represented by does not affect this execution. /// The asynchronous modifications (by either , , or without prior call to ; or by a call to ) are queued for execution with and the calling thread does not wait for the job to complete. The priority value from is assigned to the job and affects its execution. /// It is recommended that you specify the highest priority () for all property assignments initiated by user interface in order to decrease the UI response lag, while background operations could employ lower priority values. /// /// /// /// IResource resource = … /// /// ResourceProxy proxy = new ResourceProxy( resource ); /// proxy.AsyncPriority = JobPriority.Immediate; /// proxy.SetPropAsync( "Unread", true ); /// /// public JobPriority AsyncPriority { get { return _asyncPriority; } set { _asyncPriority = value; } } /// /// Begins a batch update of the resource. /// /// Operations accumulated during a batch update are executed in a single resource /// thread operation when or is called. public void BeginUpdate() { _batchUpdateCount++; } /// /// Assigns a new value to the resource property synchronously, or schedules the property update if has been called. /// /// Name of the property. /// New value for the property, may be null. /// /// marshals resource property modification to the resource thread, as resource properties can be modified in the resource thread only. The resource being modified is passed to the proxy constructor and can be retrieved from the read-only property. Because it cannot be changed during the proxy lifetime, you have to create a new proxy instance if you want to modify another resource. /// If used without prior calling , the function executes immediately and synchronously, that is, the property update action is marshalled to the resource thread and execution of the current thread is stopped until it completes. /// If you want to update more than one resource property at a time, it's recommended to use the batch update scheme via . In this case, all the property modifications inside the and / pair do not execute immediately but are deferred to be executed when any of the End… funcitons is called. See for details and examples. /// If the property value in is null, the property is deleted from the resource (same as ). /// Another version of this function that accepts a property ID instead of property name, , is considered to have better performance because it need not lookup the property ID. /// /// /// This example covers the immediate synchronous use of , without a call. For batch update case, see an example for function. /// /// IResource resource = … /// /// ResourceProxy proxy = new ResourceProxy( resource ); // Wrap the resource with a resource proxy /// … /// proxy.SetProp( "Name", "John Doe" ); // Access a property by its name /// … /// proxy.SetProp( "Age", 45 ); /// … /// proxy.SetProp( "Permitted", true ); /// /// public void SetProp( string propName, object propValue ) { SetProp( Core.ResourceStore.GetPropId( propName ), propValue, false ); } public void SetProp( PropId propId, T propValue ) { SetProp( propId.Id, propValue ); } /// /// Assigns a new value to the resource property synchronously, or schedules the property update if has been called. /// /// ID of the property. /// New value for the property, may be null. /// /// marshals resource property modification to the resource thread, as resource properties can be modified in the resource thread only. The resource being modified is passed to the proxy constructor and can be retrieved from the read-only property. Because it cannot be changed during the proxy lifetime, you have to create a new proxy instance if you want to modify another resource. /// If used without prior calling , the function executes immediately and synchronously, that is, the property update action is marshalled to the resource thread and execution of the current thread is stopped until it completes. /// If you want to update more than one resource property at a time, it's recommended to use the batch update scheme via . In this case, all the property modifications inside the and / pair do not execute immediately but are deferred to be executed when any of the End… funcitons is called. See for details and examples. /// If the property value in is null, the property is deleted from the resource (same as ). /// Another version of this function that accepts a property name instead of property ID, , is considered to have worse performance because it needs to make a property ID lookup. /// /// /// This example covers the immediate synchronous use of , without a call. For batch update case, see an example for function. /// /// IResource resource = … /// /// ResourceProxy proxy = new ResourceProxy( resource ); // Wrap the resource with a resource proxy /// /// proxy.SetProp( 239, true ); // Access a property by its ID /// /// public void SetProp( int propId, object propValue ) { SetProp( propId, propValue, false ); } /// /// Assigns a new value to the resource property asynchronously, or schedules the property update if has been called. /// /// Name of the property. /// New value for the property, may be null. /// /// marshals resource property modification to the resource thread, as resource properties can be modified in the resource thread only. The resource being modified is passed to the proxy constructor and can be retrieved from the read-only property. Because it cannot be changed during the proxy lifetime, you have to create a new proxy instance if you want to modify another resource. /// If used without prior calling , the function executes immediately and asynchronously, that is, the property update action is marshalled to the resource thread and the current thread continues its execution without waiting for the property assignment to complete. The marshalling is done by executing an asynchronous job on the resource thread, and you can modify the priority of this job by changing the property. It is recommended that you specify the highest priority () for all property assignments initiated by user interface in order to decrease the UI response lag, while background operations could employ lower priority values. /// If you want to update more than one resource property at a time, it's recommended to use the batch update scheme via . In this case, all the property modifications inside the and / pair do not execute immediately but are deferred to be executed when any of the End… funcitons is called. See for details and examples. /// If the property value in is null, the property is deleted from the resource (same as ). /// Another version of this function that accepts a property ID instead of property name, , is considered to have better performance because it need not lookup the property ID. /// /// /// This example covers the immediate asynchronous use of , without a call. For batch update case, see an example for function. /// /// IResource resource = … /// /// ResourceProxy proxy = new ResourceProxy( resource ); // Wrap the resource with a resource proxy /// … /// proxy.SetPropAsync( "Name", "John Doe" ); // Access a property by its name /// … /// proxy.SetPropAsync( "Age", 45 ); /// … /// proxy.SetPropAsync( "Permitted", true ); /// /// or, /// /// IResource resource = … /// /// // Wrap the resource with a resource proxy and use it in-place /// new ResourceProxy( resource ).SetPropAsync( "Company", "JetBrains" ); /// /// public void SetPropAsync( string propName, object propValue ) { SetProp( Core.ResourceStore.GetPropId( propName ), propValue, true ); } /// /// Assigns a new value to the resource property asynchronously, or schedules the property update if has been called. /// /// ID of the property. /// New value for the property, may be null. /// /// marshals resource property modification to the resource thread, as resource properties can be modified in the resource thread only. The resource being modified is passed to the proxy constructor and can be retrieved from the read-only property. Because it cannot be changed during the proxy lifetime, you have to create a new proxy instance if you want to modify another resource. /// If used without prior calling , the function executes immediately and asynchronously, that is, the property update action is marshalled to the resource thread and the current thread continues its execution without waiting for the property assignment to complete. The marshalling is done by executing an asynchronous job on the resource thread, and you can modify the priority of this job by changing the property. It is recommended that you specify the highest priority () for all property assignments initiated by user interface in order to decrease the UI response lag, while background operations could employ lower priority values. /// If you want to update more than one resource property at a time, it's recommended to use the batch update scheme via . In this case, all the property modifications inside the and / pair do not execute immediately but are deferred to be executed when any of the End… funcitons is called. See for details and examples. /// If the property value in is null, the property is deleted from the resource (same as ). /// Another version of this function that accepts a property name instead of property ID, , is considered to have worse performance because it needs to make a property ID lookup. /// /// /// This example covers the immediate asynchronous use of , without a call. For batch update case, see an example for function. /// /// IResource resource = … /// /// ResourceProxy proxy = new ResourceProxy( resource ); // Wrap the resource with a resource proxy /// … /// proxy.SetPropAsync( 239, true ); // Access a property by its ID /// /// or, /// /// IResource resource = … /// /// // Wrap the resource with a resource proxy and use it in-place /// new ResourceProxy( resource ).SetPropAsync( 239, true ); /// /// public void SetPropAsync( int propId, object propValue ) { SetProp( propId, propValue, true ); } public void SetPropAsync( PropId propId, T propValue) { SetProp(propId.Id, propValue); } /// /// Sets the display name of the target resource. /// /// The new display name value. /// By default, the display name is automatically generated from the properties of the resource, /// based on the template which is specified when the resource type is registered. /// If a display name for a resource is assigned explicitly, it overrides the default generated /// display name, but an explicitly assigned display name is not updated automatically /// when the properties of a resource are changed. /// public void SetDisplayName( string displayName ) { if( _batchUpdateCount == 0 && Core.ResourceStore.IsOwnerThread() ) { _resource.DisplayName = displayName; return; } AddPendingOperation( new PendingOperation( 0, displayName, OperationType.SetDisplayName ), true ); } /// /// Internal implementation for all the and overloads. /// /// Property ID as passed by the caller or extracted from the property name. /// New property value. /// Whether the assignment should be executed synchronously or not. private void SetProp( int propId, object propValue, bool async ) { if( Core.ResourceStore.PropTypes[ propId ].DataType == PropDataType.Link && propValue != null && propValue == _resource ) { throw new StorageException( "Cannot link a resource to itself (resource type " + _resource.Type + ", property type " + Core.ResourceStore.PropTypes[ propId ].Name + ")" ); } if( _batchUpdateCount == 0 && Core.ResourceStore.IsOwnerThread() ) { if( propValue == null ) { _resource.DeleteProp( propId ); } else { _resource.SetProp( propId, propValue ); } return; } AddPendingOperation( new PendingOperation( propId, propValue, OperationType.SetProp ), async ); } /// /// Deletes the specified resource property synchronously, or schedules the property deletion if has been called. /// /// Name of the property to be deleted. /// /// marshals resource property deletion to the resource thread, as resource properties can be modified in the resource thread only. The resource being modified is passed to the proxy constructor and can be retrieved from the read-only property. Because it cannot be changed during the proxy lifetime, you have to create a new proxy instance if you want to modify another resource. /// If used without prior calling , the function executes immediately and synchronously, that is, the property deletion action is marshalled to the resource thread and execution of the current thread is stopped until it completes. /// If you want to update more than one resource property at a time, it's recommended to use the batch update scheme via . In this case, all the property modifications inside the and / pair do not execute immediately but are deferred to be executed when any of the End… funcitons is called. See for details and examples. /// It is also safe to delete a property by supplying a null as a property value to one of the functions. /// Another version of this function that accepts a property ID instead of property name, , is considered to have better performance because it need not lookup the property ID. /// /// /// This example covers the immediate synchronous use of , without a call. For batch update case, see an example for function. /// /// IResource resource = … /// /// ResourceProxy proxy = new ResourceProxy( resource ); // Wrap the resource with a resource proxy /// proxy.DeleteProp( "Comment" ); // Access a property by its name /// /// Another way to delete this property is to call proxy.SetProp( "Comment", null );. /// public void DeleteProp( string propName ) { SetProp( propName, null ); } /// /// Deletes the specified resource property synchronously, or schedules the property deletion if has been called. /// /// ID of the property to be deleted. /// /// marshals resource property deletion to the resource thread, as resource properties can be modified in the resource thread only. The resource being modified is passed to the proxy constructor and can be retrieved from the read-only property. Because it cannot be changed during the proxy lifetime, you have to create a new proxy instance if you want to modify another resource. /// If used without prior calling , the function executes immediately and synchronously, that is, the property deletion action is marshalled to the resource thread and execution of the current thread is stopped until it completes. /// If you want to update more than one resource property at a time, it's recommended to use the batch update scheme via . In this case, all the property modifications inside the and / pair do not execute immediately but are deferred to be executed when any of the End… funcitons is called. See for details and examples. /// It is also safe to delete a property by supplying a null as a property value to one of the functions. /// Another version of this function that accepts a property name instead of property ID, , is considered to have worse performance because it needs to make a property ID lookup. /// /// /// This example covers the immediate synchronous use of , without a call. For batch update case, see an example for function. /// /// IResource resource = … /// /// ResourceProxy proxy = new ResourceProxy( resource ); // Wrap the resource with a resource proxy /// proxy.DeleteProp( 239 ); // Access a property by its ID /// /// Another way to delete this property is to call proxy.SetProp( 239, null );. /// public void DeleteProp( int propId ) { SetProp( propId, null ); } public void DeleteProp(PropId propId) { DeleteProp(propId.Id); } /// /// Assigns a new value to the resource property asynchronously, or schedules the property update if has been called. /// /// Name of the property to be deleted. /// /// marshals resource property deletion to the resource thread, as resource properties can be modified in the resource thread only. The resource being modified is passed to the proxy constructor and can be retrieved from the read-only property. Because it cannot be changed during the proxy lifetime, you have to create a new proxy instance if you want to modify another resource. /// If used without prior calling , the function executes immediately and asynchronously, that is, the property deletion action is marshalled to the resource thread and the current thread continues its execution without waiting for the property assignment to complete. The marshalling is done by executing an asynchronous job on the resource thread, and you can modify the priority of this job by changing the property. It is recommended that you specify the highest priority () for all property assignments initiated by user interface in order to decrease the UI response lag, while background operations could employ lower priority values. /// If you want to update more than one resource property at a time, it's recommended to use the batch update scheme via . In this case, all the property modifications inside the and / pair do not execute immediately but are deferred to be executed when any of the End… funcitons is called. See for details and examples. /// It is also safe to delete a property by supplying a null as a property value to one of the functions. /// Another version of this function that accepts a property ID instead of property name, , is considered to have better performance because it need not lookup the property ID. /// /// /// This example covers the immediate asynchronous use of , without a call. For batch update case, see an example for function. /// /// IResource resource = … /// /// // Access a property by its name /// new ResourceProxy( resource ).DeletePropAsync( "Comment" ); // Wrap the resource with a resource proxy /// /// or, /// /// IResource resource = … /// /// // Delete the property by assigning it a null value /// new ResourceProxy( resource ).SetPropAsync( "Comment", null ); /// /// public void DeletePropAsync( string propName ) { SetProp( Core.ResourceStore.GetPropId( propName ), null, true ); } /// /// Assigns a new value to the resource property asynchronously, or schedules the property update if has been called. /// /// ID of the property to be deleted. /// /// marshals resource property deletion to the resource thread, as resource properties can be modified in the resource thread only. The resource being modified is passed to the proxy constructor and can be retrieved from the read-only property. Because it cannot be changed during the proxy lifetime, you have to create a new proxy instance if you want to modify another resource. /// If used without prior calling , the function executes immediately and asynchronously, that is, the property deletion action is marshalled to the resource thread and the current thread continues its execution without waiting for the property assignment to complete. The marshalling is done by executing an asynchronous job on the resource thread, and you can modify the priority of this job by changing the property. It is recommended that you specify the highest priority () for all property assignments initiated by user interface in order to decrease the UI response lag, while background operations could employ lower priority values. /// If you want to update more than one resource property at a time, it's recommended to use the batch update scheme via . In this case, all the property modifications inside the and / pair do not execute immediately but are deferred to be executed when any of the End… funcitons is called. See for details and examples. /// It is also safe to delete a property by supplying a null as a property value to one of the functions. /// Another version of this function that accepts a property name instead of property ID, , is considered to have worse performance because it needs to make a property ID lookup. /// /// /// This example covers the immediate asynchronous use of , without a call. For batch update case, see an example for function. /// /// IResource resource = … /// /// // Access a property by its id /// new ResourceProxy( resource ).DeletePropAsync( 239 ); // Wrap the resource with a resource proxy /// /// or, /// /// IResource resource = … /// /// // Delete the property by assigning it a null value /// new ResourceProxy( resource ).SetPropAsync( 239, null ); /// /// public void DeletePropAsync( int propId ) { SetProp( propId, null, true ); } public void DeletePropAsync(PropId propId) { DeletePropAsync(propId.Id); } /// /// Adds a link with the specified property name to the specified target resource. /// /// Name of the link property. /// Resource to which the link is added. public void AddLink( string propName, IResource target ) { AddLink( Core.ResourceStore.GetPropId( propName ), target ); } /// /// Adds a link with the specified property ID to the specified target resource. /// /// ID of the link property. /// Resource to which the link is added. public void AddLink( int propId, IResource target ) { if( Core.ResourceStore.PropTypes[ propId ].DataType == PropDataType.Link && target != null && target == _resource ) { throw new StorageException( "Cannot link a resource to itself (resource type " + _resource.Type + ", property type " + Core.ResourceStore.PropTypes[ propId ].Name + ")" ); } if( _batchUpdateCount == 0 && Core.ResourceStore.IsOwnerThread() ) { _resource.AddLink( propId, target ); return; } AddPendingOperation( new PendingOperation( propId, target, OperationType.AddLink ), false ); } public void AddLink(PropId propId, IResource target) { AddLink(propId.Id, target); } /// /// Deletes a link with the specified property name to the specified resource. /// /// Name of the link property. /// Resource to which the link is deleted. public void DeleteLink( string propName, IResource target ) { DeleteLink( Core.ResourceStore.GetPropId( propName ), target ); } /// /// Deletes a link with the specified property ID to the specified resource. /// /// ID of the link property. /// Resource to which the link is deleted. public void DeleteLink( int propId, IResource target ) { if( _batchUpdateCount == 0 && Core.ResourceStore.IsOwnerThread() ) { _resource.DeleteLink( propId, target ); return; } AddPendingOperation( new PendingOperation( propId, target, OperationType.DeleteLink ), false ); } public void DeleteLink(PropId propId, IResource target) { DeleteLink(propId.Id, target); } /// /// Deletes all links with the specified property name. /// /// Name of the property for which the links are deleted. public void DeleteLinks( string propName ) { DeleteLinks( Core.ResourceStore.GetPropId( propName ) ); } /// /// Deletes all links with the specified property ID. /// /// ID of the property for which the links are deleted. /// If is a directed link, only the links from the /// resource are deleted. To delete links to the resource, specify a negative property /// ID (for example, -5 instead of 5). public void DeleteLinks( int propId ) { if( _batchUpdateCount == 0 && Core.ResourceStore.IsOwnerThread() ) { _resource.DeleteLinks( propId ); return; } AddPendingOperation( new PendingOperation( propId, null, OperationType.DeleteLink ), false ); } public void DeleteLinks(PropId propId) { DeleteLinks(propId.Id); } /// /// Synchronously commits a batch update of a resource. /// public void EndUpdate() { EndUpdate( false ); } /// /// Asynchronously commits a batch update of the resource. /// public void EndUpdateAsync() { EndUpdate( true ); } private void EndUpdate( bool async ) { if( _batchUpdateCount == 0 ) { throw new InvalidOperationException( "EndUpdate() called before BeginUpdate()" ); } if( (_pendingUpdates != null && _pendingUpdates.Count > 0) || _resource == null ) { if( Core.ResourceStore.IsOwnerThread() ) ProcessPendingUpdates(); else { string name = GetOperationName(); if( async ) Core.ResourceAP.QueueJob( _asyncPriority, name, new MethodInvoker( ProcessPendingUpdates ) ); else Core.ResourceAP.RunUniqueJob( name, new MethodInvoker( ProcessPendingUpdates ) ); } } _batchUpdateCount--; } private void AddPendingOperation( PendingOperation op, bool async ) { lock( this ) { if( _pendingUpdates == null ) { _pendingUpdates = new ArrayList(); } _pendingUpdates.Add( op ); } if( _batchUpdateCount == 0 ) { if( async ) Core.ResourceAP.QueueJob( _asyncPriority, GetOperationName(), new MethodInvoker( ProcessPendingUpdates ) ); else Core.ResourceAP.RunUniqueJob( GetOperationName(), new MethodInvoker( ProcessPendingUpdates ) ); } } private string GetOperationName() { if( _newResourceType != null ) { return "ResourceProxy for new resource of type " + _newResourceType; } else { return "ResourceProxy for resource " + _resource.Id + " of type " + _resource.Type; } } /// /// Executes the deferred property update tasks upon EndUpdate/EndUpdateAsync. /// private void ProcessPendingUpdates() { Debug.Assert( _resource != null || _newResourceType != null ); if( _resource == null ) { _resource = Core.ResourceStore.BeginNewResource( _newResourceType ); } else { try { _resource.BeginUpdate(); } catch( ResourceDeletedException ) { // the resource may have been deleted since the proxy was created; // ignore and exit silently return; } } lock( this ) { if( _pendingUpdates != null ) { foreach( PendingOperation op in _pendingUpdates ) { if( op.Target is IResource && ((IResource) op.Target).IsDeleted ) continue; if( op.OpType == OperationType.AddLink ) { IResource target = (IResource) op.Target; if( op.PropID < 0 ) { target.AddLink( -op.PropID, _resource ); } else { _resource.AddLink( op.PropID, target ); } } else if( op.OpType == OperationType.DeleteLink ) { if( op.Target == null ) { _resource.DeleteLinks( op.PropID ); } else { _resource.DeleteLink( op.PropID, (IResource) op.Target ); } } else if( op.OpType == OperationType.SetDisplayName ) { _resource.DisplayName = (string) op.Target; } else if( op.Target == null ) { _resource.DeleteProp( op.PropID ); } else { _resource.SetProp( op.PropID, op.Target ); } } _pendingUpdates.Clear(); } } _resource.EndUpdate(); } /// /// Immediately and synchronously deletes the encapsulated resource. /// /// /// This function does not support Batch mode execution (see ) because it is useless to batch any property modifications together with the whole resource deletion. /// When you call this function, the resource deletion job gets started in the resource thread and the function waits for it to complete. The calling thread execution is stopped until the job completes. /// After the resource is deleted, the proxy becomes invalid because property modifications are prohibited for the deleted resources. However, read-only resource operations can still be executed. /// You should not delete resources directly if you are executing in a thread other than the resource thread because this is not a read-only operation. /// To delete a particular resource property, use . It is also possible to delete a property by assigning it a null value with . /// /// /// /// IResource resource = … /// /// // Wait for the resource to be deleted /// new ResourceProxy( resource ).Delete(); /// /// public void Delete() { if( _resource != null ) { Core.ResourceAP.RunUniqueJob( "ResourceProxy for deleting resource " + _resource.Id, new MethodInvoker( DoDelete ) ); } } /// /// Immediately and asynchronously deletes the encapsulated resource. /// /// /// This function does not support Batch mode execution (see ) because it is useless to batch any property modifications together with the whole resource deletion. /// When you call this function, the resource deletion job gets schedulled for execution on the resource thread and the function does not wait for it to complete. The calling thread execution continues immediately. /// After the resource is deleted, the proxy becomes invalid because property modifications are prohibited for the deleted resources. However, read-only resource operations can still be executed. /// You should not delete resources directly if you are executing in a thread other than the resource thread because this is not a read-only operation. /// To delete a particular resource property, use . It is also possible to delete a property by assigning it a null value with . /// /// /// /// IResource resource = … /// /// // Initiate resource deletion and continue execution /// new ResourceProxy( resource ).DeleteAsync(); /// /// public void DeleteAsync() { if( _resource != null ) { Core.ResourceAP.QueueJob( _asyncPriority, "ResourceProxy for deleting resource " + _resource.Id, new MethodInvoker( DoDelete ) ); } } private void DoDelete() { if( _resource != null ) { _resource.Delete(); } } } }