/// /// 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