/// /// 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.Base; using JetBrains.Omea.OpenAPI; using JetBrains.Omea.Containers; using System.Diagnostics; namespace JetBrains.Omea.ResourceStore { /** * The intersection of multiple predicates. */ internal class IntersectionPredicate: ResourceListDerivedPredicate { // For MatchResource(), all predicates are treated the same. // For GetMatchingResources(), we divide the predicates in two classes - // some (like PropValuePredicate) will return a small result set, others // (like TypeResourceListPredicate) - a large one. We call the first type // "selective", and the second type "filtering". Thus, we intersect // the result sets from all selective predicates with each other and then // filter them through the filtering ones. private ResourceListPredicate[] _sourcePredicatesFiltering; private ResourceListPredicate[] _sourcePredicatesSelective; private int _minSelectionCost = 0; private int _knownType = -1; internal static bool _traceIntersections = false; internal IntersectionPredicate( params ResourceListPredicate[] predicates ) : base( predicates ) { if ( predicates.Length < 2 ) { throw new ArgumentOutOfRangeException( "At least 2 predicates required for intersection" ); } } private void BuildSelectionPlan( ResourceListPredicate[] predicates ) { _minSelectionCost = Int32.MaxValue; int maxSelectionCost = Int32.MinValue; int minSelectionCostCount = 0; int zeroCostCount = 0; for( int i=0; i 0 && cost < _minSelectionCost ) { _minSelectionCost = cost; minSelectionCostCount = 1; } else if ( cost == _minSelectionCost ) { minSelectionCostCount++; } else if ( cost == 0 ) { zeroCostCount++; } maxSelectionCost = Math.Max( cost, maxSelectionCost ); int knownType = predicates [i].GetKnownType(); if ( knownType >= 0 ) { _knownType = knownType; } } if ( zeroCostCount == predicates.Length ) { _minSelectionCost = 0; } if ( _minSelectionCost == maxSelectionCost && zeroCostCount == 0 ) { // we need to have at least one selective predicate _sourcePredicatesSelective = new ResourceListPredicate [1]; _sourcePredicatesSelective [0] = predicates [0]; _sourcePredicatesFiltering = new ResourceListPredicate [predicates.Length-1]; Array.Copy( predicates, 1, _sourcePredicatesFiltering, 0, predicates.Length-1 ); } else { int selCount = minSelectionCostCount + zeroCostCount; int filtCount = predicates.Length - selCount; _sourcePredicatesSelective = new ResourceListPredicate [selCount]; _sourcePredicatesFiltering = new ResourceListPredicate [filtCount]; int selIndex = 0, filtIndex = 0; for( int i=0; i= _sourcePredicatesSelective.Length ) { throw new Exception( "Index out of bounds: selCount=" + selCount + ", filtCount=" + filtCount + ", selIndex=" + selIndex + ", minSelectionCost=" + _minSelectionCost ); } _sourcePredicatesSelective [selIndex++] = predicates [i]; } else { if ( filtIndex >= _sourcePredicatesFiltering.Length ) { throw new Exception( "Index out of bounds: selCount=" + selCount + ", filtCount=" + filtCount + ", filtIndex=" + filtIndex + ", minSelectionCost=" + _minSelectionCost ); } _sourcePredicatesFiltering [filtIndex++] = predicates [i]; } } } } protected override string GetDerivationName() { return "Intersection"; } internal override int GetKnownType() { return _knownType; } internal void AddSource( ResourceListPredicate pred ) { lock ( this ) { if ( _sourcePredicatesSelective != null ) { if ( pred.GetSelectionCost() <= _minSelectionCost ) { _sourcePredicatesSelective = AddPredicate( _sourcePredicatesSelective, pred ); } else { _sourcePredicatesFiltering = AddPredicate( _sourcePredicatesFiltering, pred ); } } _sourcePredicates = AddPredicate( _sourcePredicates, pred ); } } internal override int GetSelectionCost() { return _minSelectionCost; } internal override IntArrayList GetMatchingResources( out bool sortedById ) { lock ( this ) { if ( _sourcePredicatesSelective == null ) { BuildSelectionPlan( _sourcePredicates ); } sortedById = true; if ( _sourcePredicatesSelective.Length == 0 ) { return new IntArrayList(); } if ( _traceIntersections ) { Debug.WriteLine( "Intersection source predicate selective: " + _sourcePredicatesSelective [0] ); } int i = 1; object syncObject; IntArrayList result = _sourcePredicatesSelective [0].GetSortedMatchingResourcesRef( out syncObject ); if( syncObject != null ) { if( _sourcePredicatesSelective.Length == 1 ) { lock( syncObject ) { result = (IntArrayList) result.Clone(); } } else { i = 2; object syncObject2; IntArrayList list2 = _sourcePredicatesSelective [1].GetSortedMatchingResourcesRef( out syncObject2 ); using( new MultiLock( syncObject, syncObject2 ) ) { result = IntArrayList.IntersectSortedNew( result, list2 ); } } } for( ; result.Count > 0 && i < _sourcePredicatesSelective.Length; i++ ) { if ( _traceIntersections ) { Debug.WriteLine( "Intersection source predicate selective: " + _sourcePredicatesSelective [i] ); } IntArrayList list2 = _sourcePredicatesSelective [i].GetSortedMatchingResourcesRef( out syncObject ); MTSafeInPlaceIntersectSorted( syncObject, result, list2 ); } int skipIndex = -1; int filterCount = _sourcePredicatesFiltering.Length; if ( result.Count > 100 && filterCount > 0 ) { filterCount--; // If the first intersection predicate returned too many results, run the // first filtering predicate as selection and intersect, instead of loading // all the resources and checking filters skipIndex = FindCheapestFilteringPredicate(); if ( _traceIntersections ) { Debug.WriteLine( "Intersection cheapest filtering source: " + _sourcePredicatesFiltering [skipIndex] ); } IntArrayList list2 = _sourcePredicatesFiltering [skipIndex].GetSortedMatchingResourcesRef( out syncObject ); MTSafeInPlaceIntersectSorted( syncObject, result, list2 ); } // filter the result by applying non-selective predicates if ( result.Count > 0 && filterCount > 0 ) { if ( _traceIntersections ) { for( i=0; i<_sourcePredicatesFiltering.Length; i++ ) { Debug.WriteLine( "Intersection source predicate filtering: " + _sourcePredicatesFiltering [i] ); } } int srcIndex = 0, destIndex = 0; while( srcIndex < result.Count ) { if ( MatchFiltering( result [srcIndex], skipIndex ) ) { result [destIndex] = result [srcIndex]; destIndex++; } srcIndex++; } result.RemoveRange( destIndex, result.Count-destIndex ); } return result; } } private static void MTSafeInPlaceIntersectSorted( object syncObject, IntArrayList result, IntArrayList list2 ) { if( syncObject == null ) { result.IntersectSorted( list2 ); } else { lock( syncObject ) { result.IntersectSorted( list2 ); } } } private int FindCheapestFilteringPredicate() { int minCost = Int32.MaxValue; int minIndex = 0; for( int i=0; i<_sourcePredicatesFiltering.Length; i++ ) { int cost = _sourcePredicatesFiltering [i].GetSelectionCost(); if ( cost < minCost ) { minIndex = i; minCost = cost; } } return minIndex; } private bool MatchFiltering( int resID, int skipIndex ) { IResource res; try { res = MyPalStorage.Storage.LoadResource( resID, true, -1 ); if ( res.IsDeleted ) return false; } catch( InvalidResourceIdException ) { return false; } for( int i=0; i<_sourcePredicatesFiltering.Length; i++ ) { if ( i == skipIndex ) continue; if ( _sourcePredicatesFiltering [i].MatchResource( res, null ) == PredicateMatch.None ) { return false; } } return true; } internal override PredicateMatch MatchResource( IResource res, IPropertyChangeSet cs ) { int oldMatch = 0; int newMatch = 0; int len = _sourcePredicates.Length; for( int i=0; i Intersect(A,B,C) * If any of the sources of the intersection are also intersections, * collapses their sources into the current intersection. */ private void ExpandIntersections( ArrayList newPredicateList, ref bool optimized ) { for( int i=0; i<_sourcePredicates.Length; i++ ) { IntersectionPredicate sourceIntersection = _sourcePredicates [i] as IntersectionPredicate; if ( sourceIntersection != null ) { newPredicateList.AddRange( sourceIntersection._sourcePredicates ); optimized = true; } else { newPredicateList.Add( _sourcePredicates [i] ); } } } /** * Intersect(A,A,B) -> Intersect(A,B) * Removes equal predicates from the list of source predicates. */ private void RemoveEqualSources( ArrayList newPredicateList, ref bool optimized ) { for( int i=newPredicateList.Count-1; i >= 0; i-- ) { for( int j=0; j A * Removes from the specified array list union predicates which contain * members of the current intersection. */ private void RemoveIntersectingUnions( ArrayList newPredicateList, ref bool optimized ) { for( int i=newPredicateList.Count-1; i >= 0; i-- ) { UnionPredicate sourceUnion = newPredicateList [i] as UnionPredicate; if ( sourceUnion != null ) { for( int j=0; j= 0; i-- ) { if ( ((ResourceListPredicate) newPredicateList [i]).HasTypePredicate( knownTypeId ) ) { newPredicateList.RemoveAt( i ); optimized = true; } } } } } }