///
/// 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.Globalization;
using System.Text;
using JetBrains.Omea.Base;
using JetBrains.Omea.Database;
using JetBrains.Omea.OpenAPI;
using JetBrains.Omea.Containers;
using JetBrains.DataStructures;
namespace JetBrains.Omea.ResourceStore
{
/**
* An abstract class containing the list of resources. Instances of this
* class cannot be directly created; use methods of other classes to
* obtain ResourceLists.
* All ResourceLists are live (meaning, they reflect the changes in the underlying
* database).
*/
public class ResourceList: IResourceList, IUpdateListener
{
internal IntArrayList _list = null;
private ResourceListPredicate _predicate;
private ResourceComparer _lastComparer = null;
private ArrayList _propertyProviders = null;
private BitArray _watchedProperties = null;
private ResourceIdCollection _idCollection = null;
private bool _watchDisplayName;
private bool _isLive;
private bool _handlersAttached;
private bool _explicitSort = false;
private bool _updatePriority = false;
private ValidResourcesEnumerable _validResourcesEnumerable = null;
private event ResourceIndexEventHandler ResourceAddedInternal;
private event ResourceIndexEventHandler ResourceDeletingInternal;
private event ResourcePropIndexEventHandler ResourceChangedInternal;
private event ResourcePropIndexEventHandler ChangedResourceDeletingInternal;
protected internal ResourceList( ResourceListPredicate predicate, bool live )
{
_predicate = predicate;
_isLive = live;
}
internal void SetUpdatePriority()
{
_updatePriority = true;
}
private void SetLive()
{
if ( !_handlersAttached )
{
_handlersAttached = true;
MyPalStorage.Storage.AddUpdateListener( this, _updatePriority );
}
}
protected internal ResourceListPredicate Predicate
{
get { return _predicate; }
}
protected bool IsInstantiated
{
get
{
return _list != null;
}
}
internal void Instantiate()
{
Instantiate( true );
}
internal void Instantiate( bool optimize )
{
if ( _list == null )
{
ResourceListPredicate oldPredicate = _predicate;
if ( optimize )
{
_predicate = _predicate.Optimize( _isLive );
}
if ( MyPalStorage.TraceOperations && !(_predicate is PlainListPredicate ) )
{
if ( !Object.ReferenceEquals( oldPredicate, _predicate ) )
{
Trace.WriteLine( "Predicate before optimization: " + oldPredicate.ToString() );
}
Trace.WriteLine( "Instantiating list " + _predicate.ToString() );
}
bool predicateSortedById = false;
_list = _predicate.GetMatchingResources( out predicateSortedById );
if ( _isLive && !_handlersAttached )
{
SetLive();
}
if ( _lastComparer != null )
{
DoSort( _lastComparer, true );
}
else if ( predicateSortedById )
{
_lastComparer = new ResourceComparer( this, new SortSettings( ResourceProps.Id, true ), false );
}
}
}
/**
* Disconnects the handlers of the resource list and moves it back to predicate state.
*/
public void Deinstantiate()
{
lock( this )
{
DetachHandlers();
_list = null;
}
}
public IResource Find(Predicate predicate)
{
lock( this )
{
foreach( IResource res in this )
{
if ( predicate(res)) return res;
}
}
return null;
}
public event ResourceIndexEventHandler ResourceAdded
{
add
{
if ( _isLive )
{
SetLive();
}
ResourceAddedInternal += value;
}
remove
{
ResourceAddedInternal -= value;
}
}
public event ResourceIndexEventHandler ResourceDeleting
{
add
{
if ( _isLive )
{
SetLive();
}
ResourceDeletingInternal += value;
}
remove
{
ResourceDeletingInternal -= value;
}
}
public event ResourcePropIndexEventHandler ResourceChanged
{
add
{
if ( _isLive )
{
SetLive();
}
ResourceChangedInternal += value;
}
remove
{
ResourceChangedInternal -= value;
}
}
public event ResourcePropIndexEventHandler ChangedResourceDeleting
{
add
{
if ( _isLive )
{
SetLive();
}
ChangedResourceDeletingInternal += value;
}
remove
{
ChangedResourceDeletingInternal -= value;
}
}
protected void Add( IResource res )
{
int index = -1;
lock( this )
{
if ( _list != null )
{
index = FindInsertIndex( res );
_list.Insert( index, res.Id );
}
}
OnResourceAdded( res, index );
}
private void OnResourceAdded( IResource res, int index )
{
if ( ResourceAddedInternal != null )
{
ResourceAddedInternal( this, new ResourceIndexEventArgs( res, index ) );
}
}
void IUpdateListener.ResourceDeleting( IResource resource )
{
if ( !_handlersAttached )
{
return;
}
if ( _predicate.MatchResource( resource, null ) == PredicateMatch.Match )
{
Remove( resource, null );
}
}
protected internal void Remove( IResource res, IPropertyChangeSet cs )
{
if ( _list == null )
{
RemoveAt( res, -1, cs );
}
else
{
lock( this )
{
int index = IndexOf( res.Id );
if ( index >= 0 )
{
RemoveAt( res, index, cs );
}
}
}
}
protected void RemoveAt( IResource res, int index, IPropertyChangeSet cs )
{
// proceed with delete even if the ResourceDeleting handler throws an exception
try
{
if ( ResourceDeletingInternal != null )
{
ResourceDeletingInternal( this, new ResourceIndexEventArgs( res, index ) );
}
if ( cs != null && ChangedResourceDeletingInternal != null )
{
ChangedResourceDeletingInternal( this, new ResourcePropIndexEventArgs( res, index, cs ) );
}
}
finally
{
if ( index >= 0 )
{
_list.RemoveAt( index );
}
}
}
public int IndexOf( IResource res )
{
if ( res == null )
{
throw new ArgumentNullException( "res" );
}
return IndexOf( res.Id );
}
public int IndexOf( int resID )
{
lock( this )
{
Instantiate();
if ( IsIDSort() )
{
int result = _list.BinarySearch( resID );
if ( result < 0 )
return -1;
return result;
}
else
{
return _list.IndexOf( resID );
}
}
}
public bool Contains( IResource res )
{
if ( res == null )
{
throw new ArgumentNullException( "res" );
}
return _predicate.MatchResource( res, null ) == PredicateMatch.Match;
}
/**
* Tells that the client of the resource list wants to receive notifications on the
* change of the specified property. (If no watches have been added, changes of any
* properties are reported to the client.
*/
public void AddPropertyWatch( int propID )
{
if ( propID == ResourceProps.DisplayName )
{
if ( _watchedProperties == null )
{
// mark that we have a filter
_watchedProperties = new BitArray( 1 );
}
_watchDisplayName = true;
return;
}
MyPalStorage.Storage.GetPropDataType( propID ); // this validates the property ID
if ( _watchedProperties == null )
{
_watchedProperties = new BitArray( propID+1 );
}
else if ( _watchedProperties.Length < propID+1 )
{
_watchedProperties.Length = propID+1;
}
_watchedProperties [propID] = true;
}
public void AddPropertyWatch( int[] propIds )
{
for( int i=0; i= 0 )
{
// check if the position of the resource in the sorted order is still valid
int prevCmpResult = 0, nextCmpResult = 0;
while( index > 0 )
{
IResource prevResource = MyPalStorage.Storage.TryLoadResource( list [index-1] );
if ( prevResource == null || prevResource.IsDeleted )
{
lock( this )
{
if ( _list == null )
{
return;
}
RemoveAt( prevResource, index-1, null );
index--;
}
continue;
}
prevCmpResult = _lastComparer.CompareResources( prevResource, res );
break;
}
while( index < list.Count-1 )
{
IResource nextResource = MyPalStorage.Storage.TryLoadResource( list [index+1] );
if ( nextResource == null || nextResource.IsDeleted )
{
lock( this )
{
if ( _list == null )
{
return;
}
RemoveAt( nextResource, index+1, null );
}
continue;
}
nextCmpResult = _lastComparer.CompareResources( res, nextResource );
break;
}
if ( prevCmpResult > 0 || nextCmpResult > 0 )
{
int newIndex;
lock( this )
{
if ( _list == null )
{
return;
}
RemoveAt( res, index, null );
newIndex = FindInsertIndex( res );
_list.Insert( newIndex, res.Id );
}
OnResourceAdded( res, newIndex );
}
}
}
public int Count
{
get
{
Instantiate();
return _list.Count;
}
}
public IResource this[ int index ]
{
get
{
Instantiate();
return MyPalStorage.Storage.LoadResource( _list [index], true, _predicate.GetKnownType() );
}
}
public IResourceIdCollection ResourceIds
{
get
{
Instantiate();
if ( _idCollection == null )
{
_idCollection = new ResourceIdCollection( this );
}
return _idCollection;
}
}
protected internal IntArrayList ResourceIdArray
{
get
{
Instantiate();
return _list;
}
}
public IEnumerable ValidResources
{
get
{
if ( _validResourcesEnumerable == null )
{
_validResourcesEnumerable = new ValidResourcesEnumerable( this );
}
return _validResourcesEnumerable;
}
}
public bool IsLive
{
get { return _isLive; }
}
public virtual void Dispose()
{
if ( _propertyProviders != null )
{
foreach( IPropertyProvider provider in _propertyProviders )
{
provider.ResourceChanged -= new PropertyProviderChangeEventHandler( provider_ResourceChanged );
}
}
DetachHandlers();
}
private void DetachHandlers()
{
if ( _handlersAttached && MyPalStorage.Storage != null )
{
_handlersAttached = false;
MyPalStorage.Storage.RemoveUpdateListener( this, _updatePriority );
}
}
/**
* Adds a provider for virtual property values to the list.
*/
public void AttachPropertyProvider( IPropertyProvider provider )
{
if ( _propertyProviders == null )
{
_propertyProviders = new ArrayList();
}
_propertyProviders.Add( provider );
provider.ResourceChanged += new PropertyProviderChangeEventHandler( provider_ResourceChanged );
}
/**
* Adds all providers from the specified collection, if they are not
* already added to the provider list.
*/
private void AttachPropertyProviders( ICollection coll )
{
if ( coll != null )
{
if ( _propertyProviders == null )
{
_propertyProviders = new ArrayList();
}
foreach( IPropertyProvider provider in coll )
{
if ( _propertyProviders.IndexOf( provider ) < 0 )
{
_propertyProviders.Add( provider );
provider.ResourceChanged += new PropertyProviderChangeEventHandler( provider_ResourceChanged );
}
}
}
}
private void provider_ResourceChanged( object sender, PropertyProviderChangeEventArgs e )
{
Resource res;
try
{
res = (Resource) MyPalStorage.Storage.LoadResource( e.ResourceId );
}
catch( StorageException )
{
return;
}
MyPalStorage.Storage.OnResourceSaved( res, e.PropId, e.OldValue );
}
/**
* Checks if any of the resources in the list has the property with
* the specified name.
*/
public bool HasProp( string propName )
{
return HasProp( MyPalStorage.Storage.GetPropId( propName ) );
}
/**
* Checks if any of the resources in the list has the property with
* the specified ID.
*/
public virtual bool HasProp( int propID )
{
lock( this )
{
if ( _propertyProviders != null )
{
for( int i=0; i<_propertyProviders.Count; i++ )
{
if ( ( _propertyProviders [i] as IPropertyProvider).HasProp( propID ) )
{
return true;
}
}
}
IPropType propType = MyPalStorage.Storage.PropTypes [propID];
if ( propType.HasFlag( PropTypeFlags.Virtual ) )
{
return false;
}
PropDataType dataType = propType.DataType;
if ( dataType == PropDataType.LongString || dataType == PropDataType.Blob || dataType == PropDataType.Double )
{
Instantiate();
for( int i=0; i<_list.Count; i++)
{
IResource res = MyPalStorage.Storage.TryLoadResource( _list [i] );
if ( res != null && res.HasProp( propID ) )
return true;
}
return false;
}
}
return Intersect( MyPalStorage.Storage.FindResourcesWithProp( null, propID ) ).Count > 0;
}
/**
* Checks if all the resources in the list have the property with the
* specified name.
*/
public bool AllHasProp( string propName )
{
return AllHasProp( MyPalStorage.Storage.GetPropId( propName ) );
}
/**
* Checks if all the resources in the list have the property with the
* specified name.
*/
public bool AllHasProp( int propID )
{
lock( this )
{
Instantiate();
if ( _list.Count == 0 )
return false;
for( int i=0; i<_list.Count; i++ )
{
IResource res = MyPalStorage.Storage.LoadResource( _list [i] );
if ( !res.HasProp( propID ) )
return false;
}
}
return true;
}
/**
* Checks if all resources in the list have the specified type.
*/
public bool AllResourcesOfType( string type )
{
lock( this )
{
Instantiate();
if ( Count == 0 )
return false;
for( int i=0; i= 0 )
{
return new string[] { MyPalStorage.Storage.ResourceTypes [_predicate.GetKnownType()].Name };
}
if( Count > 0 )
{
if ( Count == 1 )
{
return new string[] { this [0].Type };
}
HashSet processedTypes = new HashSet();
ArrayList resTypeList = ArrayListPool.Alloc();
try
{
foreach( IResource res in ValidResources )
{
if ( !processedTypes.Contains( res.Type ) )
{
processedTypes.Add( res.Type );
resTypeList.Add( res.Type );
}
}
resTypeList.Sort();
return (string[]) resTypeList.ToArray( typeof(string) );
}
finally
{
ArrayListPool.Dispose( resTypeList );
}
}
}
return emptyStringArray;
}
private static string[] emptyStringArray = new string[] {};
/**
* Returns the value of the specified property for the resource
* with the given index.
*/
public virtual object GetPropValue( IResource res, int propId )
{
if ( _propertyProviders != null )
{
for( int i=0; i<_propertyProviders.Count; i++ )
{
object val = (_propertyProviders [i] as IPropertyProvider).GetPropValue( res, propId );
if ( val != null )
return val;
}
}
return res.GetProp( propId );
}
/**
* Returns the text of the specified property for the resource with
* the given index.
*/
public string GetPropText( int index, string propName )
{
return GetPropText( index, MyPalStorage.Storage.GetPropId( propName ) );
}
public string GetPropText( int index, int propId )
{
return GetPropText( this [index], propId );
}
public string GetPropText( IResource res, int propId )
{
if ( MyPalStorage.Storage.GetPropDataType( propId ) == PropDataType.Link )
return res.GetPropText( propId );
object propValue = GetPropValue( res, propId );
if ( propValue == null )
return "";
if ( propValue is Double )
{
double dblValue = (double) propValue;
return dblValue.ToString( "N" );
}
else
return propValue.ToString();
}
/**
* Checks if the object at the specified index in the list has the
* specified property.
*/
public bool HasProp( int index, string propName )
{
return HasProp( index, MyPalStorage.Storage.GetPropId( propName ) );
}
public bool HasProp( int index, int propID )
{
return GetPropValue( this [index], propID ) != null;
}
public bool HasProp( IResource res, int propID )
{
return GetPropValue( res, propID ) != null;
}
public string SortProps
{
get
{
if ( _lastComparer == null )
return "";
return _lastComparer.SortSettings.ToString( MyPalStorage.Storage );
}
}
public int[] SortPropIDs
{
get
{
if ( _lastComparer== null )
return null;
return (int[]) _lastComparer.SortSettings.SortProps.Clone();
}
}
public int[] SortDirections
{
get
{
if ( _lastComparer == null )
return null;
int[] result = new int [_lastComparer.SortSettings.SortProps.Length];
for( int i=0; i= 0; i-- )
{
IResource res;
try
{
res = MyPalStorage.Storage.LoadResource( _list [i] );
}
catch( ResourceDeletedException )
{
continue;
}
catch( InvalidResourceIdException )
{
continue;
}
res.Delete();
}
}
internal static bool IsSameSort( ResourceList resList1, ResourceList resList2 )
{
int[] sortProps1 = resList1.SortPropIDs;
int[] sortProps2 = resList2.SortPropIDs;
if ( sortProps1 == null || sortProps2 == null )
return false;
if ( sortProps1.Length != sortProps2.Length )
return false;
for( int i=0; i