///
/// 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 JetBrains.Omea.OpenAPI;
using JetBrains.DataStructures;
namespace JetBrains.Omea.ResourceStore
{
/**
* Base class for all interfaces implementing IPropertyChangeSet.
*/
internal abstract class PropertyChangeSetBase: IPropertyChangeSet
{
protected bool _newResource;
protected bool _displayNameAffected;
protected PropertyChangeSetBase( bool newResource )
{
_newResource = newResource;
}
public bool IsNewResource
{
get { return _newResource; }
}
public bool IsDisplayNameAffected
{
get { return _displayNameAffected; }
}
public IPropertyChangeSet Merge( IPropertyChangeSet other )
{
MultiPropChangeSet mergeResult = new MultiPropChangeSet( _newResource || other.IsNewResource );
MergeWith( mergeResult );
(other as PropertyChangeSetBase).MergeWith( mergeResult );
return mergeResult;
}
public abstract bool IsPropertyChanged( int propID );
public bool IsPropertyChanged(PropId propId)
{
return IsPropertyChanged(propId.Id);
}
public abstract int[] GetChangedProperties();
public abstract object GetOldValue( int propID );
public abstract LinkChange[] GetLinkChanges( int propID );
public abstract LinkChangeType GetLinkChange( int propID, int targetID );
protected abstract void MergeWith( MultiPropChangeSet cs );
internal abstract bool Intersects( BitArray propBits );
}
/**
* The changeset which describes the change of a single property.
*/
internal class SinglePropChangeSet: PropertyChangeSetBase
{
private int _propID;
private object _oldValue;
private int _linkTargetID;
private LinkChangeType _linkChangeType;
internal SinglePropChangeSet( int propID, object oldValue, bool newResource,
bool displayNameAffected )
: base( newResource )
{
_propID = propID;
_oldValue = oldValue;
_linkTargetID = -1;
_displayNameAffected = displayNameAffected;
}
internal SinglePropChangeSet( int propID, int linkTargetID, LinkChangeType linkChangeType,
bool displayNameAffected )
: base( false )
{
_propID = propID;
_linkTargetID = linkTargetID;
_linkChangeType = linkChangeType;
_displayNameAffected = displayNameAffected;
}
public override int[] GetChangedProperties()
{
return new int[] { _propID };
}
public override bool IsPropertyChanged( int propID )
{
return _propID == propID;
}
public override object GetOldValue( int propID )
{
if ( _propID == propID )
{
return _oldValue;
}
return null;
}
public override LinkChange[] GetLinkChanges( int propID )
{
if ( MyPalStorage.Storage.PropTypes [propID].DataType != PropDataType.Link )
throw new StorageException( "GetLinkChanges() can only be called for link properties" );
if ( _propID == propID )
{
return new LinkChange[] { new LinkChange( _linkTargetID, _linkChangeType ) };
}
return new LinkChange[] {};
}
public override LinkChangeType GetLinkChange( int propID, int targetID )
{
if ( propID != _propID )
{
return LinkChangeType.None;
}
if ( _linkTargetID == -1 )
{
throw new StorageException( "IsLinkAdded() can be called only on link properties" );
}
if ( _linkTargetID == targetID )
{
return _linkChangeType;
}
return LinkChangeType.None;
}
protected override void MergeWith( MultiPropChangeSet cs )
{
if ( _linkTargetID == -1 )
{
cs.AddChangedProp( _propID, _oldValue );
}
else
{
cs.AddChangedLink( _propID, _linkTargetID, _linkChangeType );
}
if ( _displayNameAffected )
{
cs.SetDisplayNameAffected();
}
}
internal override bool Intersects( BitArray propBits )
{
int propID = Math.Abs( _propID );
return propBits.Length > propID && propBits [propID];
}
public override string ToString()
{
if ( _linkTargetID != -1 )
{
return "SinglePropChangeSet: link " + _propID + " to " + _linkTargetID +
((_linkChangeType == LinkChangeType.Add) ? " added" : " deleted");
}
return "SinglePropChangeSet: property " + _propID + " changed";
}
}
/**
* A ChangeSet which describes the change of multiple properties at the same time.
* NOTE: The changeset is filled only in the resource thread, but after the ResourceSaved
* event is fired, it can be accessed from several threads as the event is processed (OM-7003).
* Because of this, locking is only necessary on read methods.
*/
internal class MultiPropChangeSet: PropertyChangeSetBase
{
private IntHashTable _oldValues = new IntHashTable();
private int _updateCounter = 1;
internal MultiPropChangeSet( bool newResource ): base( newResource )
{
}
internal void AddChangedProp( int propID, object oldValue )
{
if ( !_oldValues.ContainsKey( propID ) )
{
_oldValues [propID] = oldValue;
}
}
internal void AddChangedLink( int propID, int targetID, LinkChangeType changeType )
{
ArrayList list = (ArrayList) _oldValues [propID];
if ( list == null )
{
list = new ArrayList();
_oldValues [propID] = list;
}
list.Add( new LinkChange( targetID, changeType ) );
}
internal void SetDisplayNameAffected()
{
_displayNameAffected = true;
}
internal void BeginUpdate()
{
_updateCounter++;
}
internal int EndUpdate()
{
return --_updateCounter;
}
internal bool IsEmpty()
{
return _oldValues.Count == 0;
}
internal int GetUpdateCounter()
{
return _updateCounter;
}
public override int[] GetChangedProperties()
{
int[] result = new int [_oldValues.Count];
lock( _oldValues )
{
int i=0;
foreach( IntHashTable.Entry e in _oldValues )
{
result [i++] = e.Key;
}
}
return result;
}
public override bool IsPropertyChanged( int propID )
{
lock( _oldValues )
{
return _oldValues.ContainsKey( propID );
}
}
public override object GetOldValue( int propID )
{
lock( _oldValues )
{
return _oldValues [propID];
}
}
public override LinkChange[] GetLinkChanges( int propID )
{
if ( MyPalStorage.Storage.PropTypes [propID].DataType != PropDataType.Link )
throw new StorageException( "GetLinkOldValue() can only be called for link properties" );
ArrayList changeList;
lock( _oldValues )
{
changeList = (ArrayList) _oldValues [propID];
}
if ( changeList == null )
return new LinkChange[] {};
return (LinkChange[]) changeList.ToArray( typeof(LinkChange) );
}
public override LinkChangeType GetLinkChange( int propID, int targetID )
{
if ( MyPalStorage.Storage.GetPropDataType( propID ) != PropDataType.Link )
throw new StorageException( "IsLinkAdded() can only be called for link properties" );
ArrayList linkList;
lock( _oldValues )
{
linkList = (ArrayList) _oldValues [propID];
}
if ( linkList == null )
return LinkChangeType.None;
for( int i=0; i