/// /// 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.DataStructures; namespace JetBrains.Omea.ResourceStore { internal abstract class ResourceRestriction { protected string _resourceType; protected int _propId; protected ResourceRestriction() {} protected ResourceRestriction( string resourceType, int propId ) { _resourceType = resourceType; _propId = propId; } public abstract void CheckResource( IResource res, IPropertyChangeSet cs ); public abstract void DeleteFromResourceStore(); public string ResourceType { get { return _resourceType; } } public int PropId { get { return _propId; } } } /** * represents single link restriction */ internal class LinkRestriction: ResourceRestriction { public LinkRestriction( string fromResourceType, string toResourceType, int linkType, int minCount, int maxCount ) : base( fromResourceType, linkType ) { _toResourceType = toResourceType; _minCount = minCount; _maxCount = maxCount; } public LinkRestriction( IResource lr ) { if( lr.Type != RestrictionResourceType ) throw new StorageException( "Attempt to load link restriction from an inappropriate resource"); _resourceType = lr.GetStringProp( ResourceRestrictions.propFromResourceType ); _toResourceType = lr.GetStringProp( ResourceRestrictions.propToResourceType ); _propId = lr.GetIntProp( ResourceRestrictions.propLinkType ); _minCount = lr.GetIntProp( ResourceRestrictions.propMinCount ); _maxCount = lr.GetIntProp( ResourceRestrictions.propMaxCount ); } public void SaveToResourceStore() { IResource lr = MyPalStorage.Storage.BeginNewResource( RestrictionResourceType ); try { lr.SetProp( ResourceRestrictions.propFromResourceType, _resourceType ); if( _toResourceType != null ) lr.SetProp( ResourceRestrictions.propToResourceType, _toResourceType ); lr.SetProp( ResourceRestrictions.propLinkType, _propId ); lr.SetProp( ResourceRestrictions.propMinCount, _minCount ); lr.SetProp( ResourceRestrictions.propMaxCount, _maxCount ); } finally { lr.EndUpdate(); } } public override void DeleteFromResourceStore() { IResourceList restList = MyPalStorage.Storage.FindResources( RestrictionResourceType, ResourceRestrictions.propLinkType, _propId ); foreach( IResource rest in restList ) { if ( rest.GetStringProp( ResourceRestrictions.propFromResourceType ) == _resourceType ) { rest.Delete(); } } } #region System.Object overrides public override int GetHashCode() { return _resourceType.GetHashCode() + _propId; } public override bool Equals( object obj ) { if( !( obj is LinkRestriction ) ) return false; LinkRestriction lr = (LinkRestriction) obj; return _resourceType == lr._resourceType && _propId == lr._propId; } #endregion /** * main checking predicate * returns true if a resource corresponds to the restriction */ public override void CheckResource( IResource res, IPropertyChangeSet cs ) { if( res.Type != _resourceType ) return; bool hasAdds = false, hasDeletes = false; LinkChange[] changes = cs.GetLinkChanges( _propId ); for( int i=0; i= 0 && linkCount > _maxCount ) { throw new ResourceRestrictionException( "Resource of type " + res.Type + " doesn't correspond to maximum link count restriction on property " + MyPalStorage.Storage.GetPropName( _propId ) ); } } public static string RestrictionResourceType { get { return "LinkRestriction"; } } public string ToResourceType { get { return _toResourceType; } } public int MinCount { get { return _minCount; } } public int MaxCount { get { return _maxCount; } } private string _toResourceType; private int _minCount; private int _maxCount; } internal class UniqueRestriction: ResourceRestriction { public UniqueRestriction( string resourceType, int propId ) : base( resourceType, propId ) { } public UniqueRestriction( IResource res ) { _resourceType = res.GetStringProp( ResourceRestrictions.propFromResourceType ); _propId = res.GetIntProp( ResourceRestrictions.propUniquePropId ); } public void SaveToResourceStore() { IResource lr = MyPalStorage.Storage.BeginNewResource( RestrictionResourceType ); try { lr.SetProp( ResourceRestrictions.propFromResourceType, _resourceType ); lr.SetProp( ResourceRestrictions.propUniquePropId, _propId ); } finally { lr.EndUpdate(); } } public override void DeleteFromResourceStore() { IResourceList restList = MyPalStorage.Storage.FindResources( RestrictionResourceType, ResourceRestrictions.propUniquePropId, _propId ); foreach( IResource rest in restList ) { if ( rest.GetStringProp( ResourceRestrictions.propFromResourceType ) == _resourceType ) { rest.Delete(); } } } public override void CheckResource( IResource res, IPropertyChangeSet cs ) { object propValue = res.GetProp( _propId ); if ( propValue != null ) { IResourceList resList = MyPalStorage.Storage.FindResources( res.Type, _propId, propValue ); if ( resList.Count > 1 ) { int dupId = -1; for( int i=0; i as resource ID=" + dupId ); } } } } public static string RestrictionResourceType { get { return "UniqueRestriction"; } } public override bool Equals( object obj ) { UniqueRestriction rhs = obj as UniqueRestriction; if ( rhs == null ) return false; return _propId == rhs._propId && _resourceType == rhs._resourceType; } public override int GetHashCode() { return _propId ^ _resourceType.GetHashCode(); } } internal class CustomRestriction: ResourceRestriction { private IResourceRestriction _restriction; private string _restrictionClass; public CustomRestriction( string resourceType, int propId, IResourceRestriction restriction ) : base( resourceType, propId ) { _restriction = restriction; _restrictionClass = _restriction.GetType().FullName; } public CustomRestriction( IResource res ) { _resourceType = res.GetStringProp( ResourceRestrictions.propFromResourceType ); _propId = res.GetIntProp( ResourceRestrictions.propUniquePropId ); _restrictionClass = res.GetStringProp( ResourceRestrictions.propCustomRestrictionClass ); } public override void CheckResource( IResource res, IPropertyChangeSet cs ) { if ( _restriction == null ) throw new ResourceRestrictionException( "Custom resource restriction implementation not registered" ); _restriction.CheckResource( res ); } public static string RestrictionResourceType { get { return "CustomRestriction"; } } public string RestrictionClass { get { return _restrictionClass; } } internal void SetImplementation( IResourceRestriction restriction ) { _restriction = restriction; } internal void SaveToResourceStore() { IResource lr = MyPalStorage.Storage.BeginNewResource( RestrictionResourceType ); try { lr.SetProp( ResourceRestrictions.propFromResourceType, _resourceType ); lr.SetProp( ResourceRestrictions.propUniquePropId, _propId ); lr.SetProp( ResourceRestrictions.propCustomRestrictionClass, _restrictionClass ); } finally { lr.EndUpdate(); } } public override void DeleteFromResourceStore() { IResourceList restList = MyPalStorage.Storage.FindResources( RestrictionResourceType, ResourceRestrictions.propUniquePropId, _propId ); foreach( IResource rest in restList ) { if ( rest.GetStringProp( ResourceRestrictions.propFromResourceType ) == _resourceType ) { rest.Delete(); } } } } /** * whole space of link restrictions */ public sealed class ResourceRestrictions { public static void RegisterTypes() { _store = MyPalStorage.Storage; _store.ResourceTypes.Register( LinkRestriction.RestrictionResourceType, String.Empty, string.Empty, ResourceTypeFlags.Internal | ResourceTypeFlags.NoIndex ); _store.ResourceTypes.Register( UniqueRestriction.RestrictionResourceType, String.Empty, string.Empty, ResourceTypeFlags.Internal | ResourceTypeFlags.NoIndex ); _store.ResourceTypes.Register( CustomRestriction.RestrictionResourceType, String.Empty, string.Empty, ResourceTypeFlags.Internal | ResourceTypeFlags.NoIndex ); _propFromResourceType = _store.PropTypes.Register( "fromResourceType", PropDataType.String, PropTypeFlags.Internal ); _propToResourceType = _store.PropTypes.Register( "toResourceType", PropDataType.String, PropTypeFlags.Internal ); _propLinkType = _store.PropTypes.Register( "LinkType", PropDataType.Int, PropTypeFlags.Internal ); _propUniquePropId = _store.PropTypes.Register( "UniquePropId", PropDataType.Int, PropTypeFlags.Internal ); _propMinCount = _store.PropTypes.Register( "MinCount", PropDataType.Int, PropTypeFlags.Internal ); _propMaxCount = _store.PropTypes.Register( "MaxCount", PropDataType.Int, PropTypeFlags.Internal ); propCustomRestrictionClass = _store.PropTypes.Register( "CustomRestrictionClass", PropDataType.String, PropTypeFlags.Internal ); _restrictions = new HashMap(); _customRestrictions = new HashMap(); foreach( IResource res in _store.GetAllResources( LinkRestriction.RestrictionResourceType ) ) { LinkRestriction lr = new LinkRestriction( res ); if ( lr.ResourceType != null ) { AddResourceRestriction( lr ); } } foreach( IResource res in _store.GetAllResources( UniqueRestriction.RestrictionResourceType ) ) { UniqueRestriction restriction = new UniqueRestriction( res ); if ( restriction.ResourceType != null ) { AddResourceRestriction( restriction ); } } foreach( IResource res in _store.GetAllResources( CustomRestriction.RestrictionResourceType ) ) { CustomRestriction restriction = new CustomRestriction( res ); if ( restriction.ResourceType != null ) { _customRestrictions [restriction.RestrictionClass] = restriction; AddResourceRestriction( restriction ); } } _active = true; } /** * empty default ctor is necessary to be sure that static ctor is executed */ private ResourceRestrictions() {} internal static int propFromResourceType { get { return _propFromResourceType; } } internal static int propToResourceType { get { return _propToResourceType; } } internal static int propLinkType { get { return _propLinkType; } } internal static int propMinCount { get { return _propMinCount; } } internal static int propMaxCount { get { return _propMaxCount; } } internal static int propUniquePropId { get { return _propUniquePropId; } } internal static void RegisterLinkRestriction( string fromResourceType, int linkType, string toResourceType, int minCount, int maxCount ) { if( !_active ) return; if ( fromResourceType == null ) { throw new ArgumentNullException( "fromResourceType" ); } if ( !MyPalStorage.Storage.ResourceTypes.Exist( fromResourceType ) ) { throw new ArgumentException( "Invalid resource type " + fromResourceType, fromResourceType ); } if ( MyPalStorage.Storage.GetPropDataType( linkType ) != PropDataType.Link ) { throw new Exception( "Link restrictions may only be registered for link properties" ); } LinkRestriction lr = new LinkRestriction( fromResourceType, toResourceType, linkType, minCount, maxCount ); if ( AddResourceRestriction( lr ) ) { lr.SaveToResourceStore(); } } internal static void RegisterCustomRestriction( string resourceType, int propId, IResourceRestriction restriction ) { if ( resourceType == null ) { throw new ArgumentNullException( "resourceType" ); } if ( !MyPalStorage.Storage.ResourceTypes.Exist( resourceType ) ) { throw new ArgumentException( "Invalid resource type " + resourceType, resourceType ); } string restrictionClass = restriction.GetType().FullName; CustomRestriction existingRestriction = (CustomRestriction) _customRestrictions [restrictionClass]; if ( existingRestriction != null ) { existingRestriction.SetImplementation( restriction ); } else { CustomRestriction customRestriction = new CustomRestriction( resourceType, propId, restriction ); AddResourceRestriction( customRestriction ); customRestriction.SaveToResourceStore(); _customRestrictions [restrictionClass] = customRestriction; } } internal static void DeleteCustomRestriction( string resourceType, int propId ) { lock( _restrictions ) { HashSet restrictionSet = (HashSet) _restrictions [resourceType]; if ( restrictionSet != null ) { foreach( HashSet.Entry e in restrictionSet ) { CustomRestriction restriction = e.Key as CustomRestriction; if ( restriction != null && restriction.PropId == propId ) { restrictionSet.Remove( restriction ); _customRestrictions.Remove( restriction.RestrictionClass ); restriction.DeleteFromResourceStore(); } } } } } internal static void RegisterRestrictionOnDelete( string resourceType, IResourceRestriction restriction ) { RegisterCustomRestriction( resourceType, ResourceProps.Id, restriction ); } internal static void DeleteRestrictionOnDelete( string resourceType ) { DeleteCustomRestriction( resourceType, ResourceProps.Id ); } internal static void RegisterUniqueRestriction( string resourceType, int propId ) { if ( resourceType == null ) { throw new ArgumentNullException( "resourceType" ); } if ( !MyPalStorage.Storage.ResourceTypes.Exist( resourceType ) ) { throw new ArgumentException( "Invalid resource type " + resourceType, resourceType ); } PropDataType dataType = MyPalStorage.Storage.PropTypes [propId].DataType; if (dataType != PropDataType.Int && dataType != PropDataType.String && dataType != PropDataType.Date) { throw new StorageException( "Unique restrictions may only be registered for int, string or date properties" ); } UniqueRestriction restriction = new UniqueRestriction( resourceType, propId ); if ( AddResourceRestriction( restriction ) ) { restriction.SaveToResourceStore(); } } internal static void DeleteUniqueRestriction( string resourceType, int propId ) { lock( _restrictions ) { HashSet restrictionSet = (HashSet) _restrictions [resourceType]; if ( restrictionSet != null ) { UniqueRestriction restriction = new UniqueRestriction( resourceType, propId ); restrictionSet.Remove( restriction ); // this will delete the restriction which is equal to the given one restriction.DeleteFromResourceStore(); } } } internal static bool UniqueRestrictionExists( string resourceType, int propId ) { Guard.NullArgument( resourceType, "resourceType" ); lock( _restrictions ) { HashSet restrictionSet = (HashSet) _restrictions [resourceType]; if ( restrictionSet != null ) { return restrictionSet.Contains( new UniqueRestriction( resourceType, propId ) ); } return false; } } /// /// Deletes all restrictions on the specified property type. /// /// The ID of the property type. internal static void DeletePropRestrictions( int propId ) { lock( _restrictions ) { foreach( HashMap.Entry e in _restrictions ) { ArrayList restrictionsToDelete = new ArrayList(); HashSet restrictionsSet = (HashSet) e.Value; foreach( HashSet.Entry restrictionEntry in restrictionsSet ) { ResourceRestriction restriction = (ResourceRestriction) restrictionEntry.Key; if ( restriction.PropId == propId ) { restrictionsToDelete.Add( restriction ); } } foreach( ResourceRestriction restriction in restrictionsToDelete ) { restrictionsSet.Remove( restriction ); restriction.DeleteFromResourceStore(); } } } } private static bool AddResourceRestriction( ResourceRestriction restriction ) { lock( _restrictions ) { HashMap.Entry E = _restrictions.GetEntry( restriction.ResourceType ); HashSet restrictionsSet = ( E == null ) ? new HashSet() : (HashSet) E.Value; if( !restrictionsSet.Contains( restriction ) ) { restrictionsSet.Add( restriction ); if( E == null ) _restrictions[ restriction.ResourceType ] = restrictionsSet; return true; } return false; } } public static void CheckResource( IResource res, IPropertyChangeSet changeSet ) { if( _active ) { lock( _restrictions ) { HashSet restrictionsSet = (HashSet) _restrictions[ res.Type ]; if( restrictionsSet != null ) { foreach( HashSet.Entry E in restrictionsSet ) { ResourceRestriction restriction = (ResourceRestriction) E.Key; if ( ( restriction.PropId != ResourceProps.Id && changeSet.IsNewResource ) || changeSet.IsPropertyChanged( restriction.PropId ) ) { restriction.CheckResource( res, changeSet ); } } } } } } internal static void CheckResourceDelete( IResource res ) { CheckResource( res, new SinglePropChangeSet( ResourceProps.Id, res.Id, false, false ) ); } public static int GetMinLinkCountRestriction( string fromResourceType, int linkType ) { LinkRestriction restriction = FindLinkRestriction( fromResourceType, linkType ); if ( restriction != null ) { return restriction.MinCount; } return 0; } public static int GetMaxLinkCountRestriction( string fromResourceType, int linkType ) { LinkRestriction restriction = FindLinkRestriction( fromResourceType, linkType ); if ( restriction != null ) { return restriction.MaxCount; } return Int32.MaxValue; } public static string GetLinkResourceTypeRestriction( string fromResourceType, int linkType ) { LinkRestriction restriction = FindLinkRestriction( fromResourceType, linkType ); if ( restriction != null ) { return restriction.ToResourceType; } return null; } private static LinkRestriction FindLinkRestriction( string fromResourceType, int linkType ) { lock( _restrictions ) { HashSet restrictionsSet = (HashSet) _restrictions[ fromResourceType ]; if( restrictionsSet != null ) { foreach( HashSet.Entry E in restrictionsSet ) { ResourceRestriction restriction = (ResourceRestriction) E.Key; if ( restriction.PropId == linkType ) { LinkRestriction lr = restriction as LinkRestriction; if ( lr != null ) { return lr; } } } } return null; } } private static MyPalStorage _store; private static int _propFromResourceType; private static int _propToResourceType; private static int _propLinkType; private static int _propMinCount; private static int _propMaxCount; internal static int propCustomRestrictionClass; private static HashMap _restrictions; private static HashMap _customRestrictions; private static bool _active = false; private static int _propUniquePropId; } }