///
/// 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.Generic;
using JetBrains.Omea.Base;
using JetBrains.Omea.Containers;
using JetBrains.Omea.OpenAPI;
using System.Collections.Specialized;
using System.Collections;
using JetBrains.Omea.Database;
using System.Diagnostics;
namespace JetBrains.Omea.ResourceStore
{
/**
* Cached information for a single property type.
*/
internal class PropTypeItem: IPropType
{
private int _id;
private string _name;
private PropDataType _type;
private PropTypeFlags _flags;
private string _displayName;
private string _reverseDisplayName;
private bool _ownerPluginLoaded;
internal PropTypeItem( int ID, string name, PropDataType type, PropTypeFlags flags )
{
_id = ID;
_name = name;
_type = type;
_flags = flags;
_displayName = null;
_reverseDisplayName = null;
_ownerPluginLoaded = true;
}
public int Id
{
get { return _id; }
}
public string Name
{
get { return _name; }
}
public PropDataType DataType
{
get { return _type; }
}
public string DisplayName
{
get { return _displayName; }
}
public string ReverseDisplayName
{
get { return _reverseDisplayName; }
}
public PropTypeFlags Flags
{
get { return _flags; }
set
{
if ( _flags != value )
{
(MyPalStorage.Storage.PropTypes as PropTypeCollection).UpdatePropTypeFlags( _id, value );
}
}
}
public bool OwnerPluginLoaded
{
get { return _ownerPluginLoaded; }
}
public bool HasFlag( PropTypeFlags flag )
{
return (_flags & flag) != 0;
}
internal void SetDisplayNames( string displayName, string reverseDisplayName )
{
_displayName = displayName;
_reverseDisplayName = reverseDisplayName;
}
internal void SetDataType( PropDataType dataType )
{
_type = dataType;
}
internal void SetFlags( PropTypeFlags flags )
{
_flags = flags;
}
internal void SetOwnerPluginUnloaded()
{
_ownerPluginLoaded = false;
}
}
/**
* Collection of property types.
*/
internal class PropTypeCollection: IPropTypeCollection
{
private MyPalStorage _storage;
private ITable _propTypeTable;
private ArrayList _propTypeCache = new ArrayList();
private PropTypeItem[] _pseudoProps = new PropTypeItem[ 3 ];
private Hashtable _propTypeNameCache = CollectionsUtil.CreateCaseInsensitiveHashtable();
private bool _propTypesCached;
private Dictionary _propIdCache = new Dictionary();
internal PropTypeCollection( MyPalStorage storage, ITable propTypeTable )
{
_storage = storage;
_propTypeTable = propTypeTable;
_pseudoProps [0] = new PropTypeItem( ResourceProps.Id,
"", PropDataType.Int, PropTypeFlags.Normal );
_pseudoProps [1] = new PropTypeItem( ResourceProps.DisplayName,
"DisplayName", PropDataType.String, PropTypeFlags.Normal );
_pseudoProps [1].SetDisplayNames( "Name", null );
_pseudoProps [2] = new PropTypeItem( ResourceProps.Type,
"Type", PropDataType.String, PropTypeFlags.Normal );
}
public IEnumerator GetEnumerator()
{
return new SkipNullEnumerator( _propTypeCache );
}
public IPropType this [int propID]
{
get
{
if ( propID < 0 )
{
PropTypeItem revItem = FindPropTypeItem( -propID );
if ( revItem.HasFlag( PropTypeFlags.DirectedLink ) )
{
return revItem;
}
throw new StorageException( "Invalid property type ID " + propID );
}
if ( propID >= ResourceProps.Id )
{
return _pseudoProps [propID - ResourceProps.Id];
}
return FindPropTypeItem( propID );
}
}
public IPropType this [string propName]
{
get
{
IPropType propType = (IPropType) _propTypeNameCache [propName];
if ( propType == null )
{
throw new StorageException( "Invalid property type name " + propName );
}
return propType;
}
}
public int Count
{
get { return _propTypeNameCache.Count; }
}
public int Register( string name, PropDataType propType )
{
return Register( name, propType, PropTypeFlags.Normal, null );
}
public PropId Register(string name, PropDataTypeGeneric dataType)
{
int id = Register(name, dataType.Type);
return InternPropId(new PropId(id));
}
private PropId InternPropId(PropId propId)
{
lock(_propIdCache)
{
if (!_propIdCache.ContainsKey(propId.Id))
{
_propIdCache[propId.Id] = propId;
}
return (PropId) _propIdCache[propId.Id];
}
}
public int Register( string name, PropDataType propType, PropTypeFlags flags )
{
return Register( name, propType, flags, null );
}
public PropId Register(string name, PropDataTypeGeneric dataType, PropTypeFlags flags)
{
int id = Register(name, dataType.Type, flags);
return InternPropId(new PropId(id));
}
public int Register( string name, PropDataType dataType, PropTypeFlags flags, IPlugin ownerPlugin )
{
bool newPropType = false;
int ID = RegisterPropTypeInternal( name, dataType, flags, false, out newPropType );
CreateOrUpdatePropTypeResource( name, dataType, flags, ownerPlugin, ID, newPropType );
return ID;
}
public PropId Register(string name, PropDataTypeGeneric dataType, PropTypeFlags flags,
IPlugin ownerPlugin)
{
int id = Register(name, dataType.Type, flags, ownerPlugin);
return InternPropId(new PropId(id));
}
internal void CreateOrUpdatePropTypeResource( string name, PropDataType dataType, PropTypeFlags flags, IPlugin ownerPlugin, int ID, bool newPropType )
{
if ( newPropType )
{
IResource res;
try
{
res = CreatePropTypeResource( ID, name, dataType, flags );
}
catch( ResourceRestrictionException ex ) // OM-9471
{
MyPalStorage.Storage.OnIndexCorruptionDetected( "ResourceRestrictionException when creating PropType resource: " +
ex.Message );
return;
}
_storage.SetOwnerPlugin( res, ownerPlugin );
}
else
{
IResource res = _storage.FindUniqueResource( "PropType", "Name", name );
if ( res != null )
{
res.SetProp( "Flags", (int) this [name].Flags ); // ensure OR'ed flags are applied correctly
_storage.SetOwnerPlugin( res, ownerPlugin );
}
else
{
MyPalStorage.Storage.OnIndexCorruptionDetected( "Could not find PropType resource for property type " + name );
}
}
}
/**
* Sets the display name for a property or a non-directed link.
*/
public void RegisterDisplayName( int propID, string displayName )
{
if ( this [propID].DataType == PropDataType.Link && this [propID].HasFlag( PropTypeFlags.DirectedLink ) )
{
throw new StorageException( "Both Source and Target display names must be specified for directed links" );
}
RegisterPropDisplayNameInternal( propID, displayName, null );
}
public void RegisterDisplayName(PropId propId, string displayName)
{
RegisterDisplayName(propId.Id, displayName);
}
/**
* Sets the display name for a directed link,
*/
public void RegisterDisplayName( int propID, string fromDisplayName, string toDisplayName )
{
if ( this [propID].DataType != PropDataType.Link || !this[propID].HasFlag( PropTypeFlags.DirectedLink ) )
{
throw new StorageException( "Both Source and Target display names can only be specified for directed links" );
}
RegisterPropDisplayNameInternal( propID, fromDisplayName, toDisplayName );
}
public void RegisterDisplayName(PropId propId, string fromDisplayName, string toDisplayName)
{
RegisterDisplayName(propId.Id, fromDisplayName, toDisplayName);
}
/**
* Checks if all property types in the specified list are registered.
*/
public bool Exist( params string[] propTypeNames )
{
foreach( string name in propTypeNames )
{
if ( _propTypeNameCache [name] == null ) return false;
}
return true;
}
private PropTypeItem FindPropTypeItem( int propID )
{
if ( propID < 0 || propID >= _propTypeCache.Count )
throw new StorageException( "Invalid property type ID " + propID );
PropTypeItem item = (PropTypeItem) _propTypeCache [propID];
if (item == null)
throw new StorageException( "Invalid property type ID " + propID );
return item;
}
/**
* Loads the property types to the cache hash table.
*/
internal void CachePropTypes()
{
IResultSet rs = _propTypeTable.CreateResultSet( 0 );
try
{
foreach( IRecord rec in rs )
{
int ID = rec.GetIntValue( 0 );
string name = rec.GetStringValue( 1 );
AddPropTypeToCache( ID, name, (PropDataType) rec.GetIntValue( 2 ),
(PropTypeFlags) rec.GetIntValue( 3 ) );
}
}
finally
{
rs.Dispose();
}
_propTypesCached = true;
}
///
/// Adds a property type to the cache.
///
internal void AddPropTypeToCache( int ID, string name, PropDataType propType, PropTypeFlags flags )
{
if ( ID < 0 || ID > 65536 )
throw new BadIndexesException( "Invalid property type ID " + ID );
lock( _propTypeCache )
{
PropTypeItem propTypeItem = new PropTypeItem( ID, name, propType, flags );
while( _propTypeCache.Count < ID )
_propTypeCache.Add( null );
if ( _propTypeCache.Count == ID )
{
_propTypeCache.Add( propTypeItem );
}
else
{
_propTypeCache [ID] = propTypeItem;
}
_propTypeNameCache [name] = propTypeItem;
}
}
internal void CachePropDisplayNames()
{
// now that the cache is filled, we can use the properties to load
// display names
foreach( PropTypeItem item in _propTypeCache )
{
if ( item != null )
{
IResource propTypeRes = _storage.FindUniqueResource( "PropType", "ID", item.Id );
if ( propTypeRes != null )
{
item.SetDisplayNames( propTypeRes.GetStringProp( "PropDisplayName" ),
propTypeRes.GetStringProp( "ReverseDisplayName" ) );
}
}
}
}
/**
* Adds a record for the specified prop type to the DB.
*/
internal int RegisterPropTypeInternal( string name, PropDataType propType, PropTypeFlags flags,
bool forceType, out bool newPropType )
{
_storage.CheckOwnerThread();
IRecord rec = _propTypeTable.GetRecordByEqual( 1, name );
if ( rec != null )
{
if ( !_propTypeNameCache.ContainsKey( name ) )
{
throw new BadIndexesException( "Property type " + name + " found in PropTypes table but missing in name cache" );
}
bool recordChanged = false;
if ( rec.GetIntValue( 2 ) != (int) propType )
{
if ( forceType )
{
rec.SetValue( 2, IntInternalizer.Intern( (int) propType ) );
((PropTypeItem) this[name]).SetDataType( propType );
recordChanged = true;
}
else
{
throw new StorageException( "Inconsistent registration for property type " + name +
": old type " + (PropDataType) rec.GetIntValue( 2 ) + ", new type " + propType );
}
}
int propId = rec.GetIntValue( 0 );
PropTypeFlags newFlags = flags | this [propId].Flags;
if ( rec.GetIntValue( 3 ) != (int) newFlags )
{
rec.SetValue( 3, (int) newFlags );
recordChanged = true;
}
if ( recordChanged )
{
rec.Commit();
}
newPropType = false;
PropTypeItem propTypeItem = (PropTypeItem) _propTypeCache [propId];
propTypeItem.SetFlags( newFlags );
return propId;
}
if ( (flags & ( PropTypeFlags.DirectedLink | PropTypeFlags.CountUnread )) != 0 &&
propType != PropDataType.Link )
{
throw new StorageException( "DirectedLink and CountUnread flags can be used only on Link properties" );
}
int ID;
lock( _propTypeTable )
{
IRecord propertyType = _propTypeTable.NewRecord();
propertyType.SetValue( 1, name );
propertyType.SetValue( 2, IntInternalizer.Intern( (int) propType ) );
propertyType.SetValue( 3, (int) flags );
_storage.SafeCommitRecord( propertyType, "PropTypeCollection.RegisterPropTypeInternal" );
ID = propertyType.GetID();
if ( ID > 65536 )
{
MyPalStorage.Storage.OnIndexCorruptionDetected( "Invalid next ID in property type table" );
}
}
AddPropTypeToCache( ID, name, propType, flags );
newPropType = true;
return ID;
}
///
/// Creates a resource describing the property type.
///
private IResource CreatePropTypeResource( int ID, string name, PropDataType propType, PropTypeFlags flags )
{
IResource res = _storage.BeginNewResource( "PropType" );
res.SetProp( _storage.Props.Name, name );
res.SetProp( _storage.Props.TypeId, ID );
res.SetProp( _storage.Props.DataType, (int) propType );
res.SetProp( _storage.Props.Flags, (int) flags );
res.EndUpdate();
return res;
}
internal void UpdatePropTypeFlags( int propId, PropTypeFlags flags )
{
PropTypeItem propType = (PropTypeItem) _propTypeCache [propId];
propType.SetFlags( flags );
IResource res = _storage.FindUniqueResource( "PropType", "ID", propId );
if ( res != null )
{
res.SetProp( "Flags", (int) flags );
UpdatePropTypeRecord( propType.Name, res );
}
else
{
MyPalStorage.Storage.OnIndexCorruptionDetected( "Could not find PropType resource with ID " + propId );
}
}
/**
* When a PropType resource is changed, updates the DB data for it.
*/
internal void UpdatePropType( Resource res )
{
string propName = res.GetStringProp( "Name" );
if ( propName == null )
{
MyPalStorage.Storage.OnIndexCorruptionDetected( "UpdatePropType: Name property missing on PropType resource" );
return;
}
int propId = this [propName].Id;
UpdatePropTypeRecord( propName, res );
PropTypeItem propTypeItem = FindPropTypeItem( propId );
Debug.Assert( propTypeItem.Name == propName );
propTypeItem.SetFlags( (PropTypeFlags) res.GetIntProp( "Flags" ) );
}
private void UpdatePropTypeRecord( string propName, IResource res )
{
IResultSet rs = _propTypeTable.CreateModifiableResultSet( 1, propName );
try
{
IEnumerator enumerator = rs.GetEnumerator();
using( (IDisposable) enumerator )
{
if ( !enumerator.MoveNext() )
throw new StorageException( "Cannot find the property type to be updated" );
IRecord rec = (IRecord) enumerator.Current;
if ( rec.GetIntValue( 2 ) != res.GetIntProp( "DataType" ) )
{
throw new StorageException( "Invalid attempt to change data type of property " + propName +
" from " + (PropDataType) rec.GetIntValue( 2 ) +
" to " + (PropDataType) res.GetIntProp( "DataType" ) );
}
rec.SetValue( 3, res.GetIntProp( "Flags" ) );
_storage.SafeCommitRecord( rec, "PropTypeCollection.UpdatePropTypeRecord" );
}
}
finally
{
rs.Dispose();
}
}
/**
* Unregisters the specified property type and deletes all properties
* of that type.
*/
public void Delete( int id )
{
PropTypeItem item = FindPropTypeItem( id );
if ( item.DataType == PropDataType.Link )
{
_storage.DeleteLinksOfType( id );
}
else
{
_storage.DeletePropsOfType( id );
}
ResourceRestrictions.DeletePropRestrictions( id );
IResultSet rs = _propTypeTable.CreateModifiableResultSet( 0, id );
try
{
SafeRecordEnumerator enumerator = new SafeRecordEnumerator( rs, "PropTypes.Delete" );
using( enumerator )
{
if ( !enumerator.MoveNext() )
{
MyPalStorage.Storage.OnIndexCorruptionDetected( "PropTypeCollection.Delete: Attempt to delete non-existing property type " + id );
}
else
{
IRecord rec = enumerator.Current;
_storage.SafeDeleteRecord( rec, "PropTypes.Delete" );
}
}
}
finally
{
rs.Dispose();
}
IResource propTypeRes = _storage.FindUniqueResource( "PropType", "ID", id );
Debug.Assert( propTypeRes != null );
propTypeRes.Delete();
}
internal void RegisterPropDisplayNameInternal( int propID, string fromDisplayName, string toDisplayName )
{
PropTypeItem propTypeItem = (PropTypeItem) _propTypeCache [propID];
IResource propTypeRes = _storage.FindUniqueResource( "PropType", "ID", propID );
if ( propTypeRes == null )
{
MyPalStorage.Storage.OnIndexCorruptionDetected( "Property type not found in RegisterPropDisplayNameInternal" );
return;
}
propTypeRes.SetProp( "PropDisplayName", fromDisplayName );
if ( toDisplayName == null )
{
propTypeRes.DeleteProp( "ReverseDisplayName" );
}
else
{
propTypeRes.SetProp( "ReverseDisplayName", toDisplayName );
}
propTypeItem.SetDisplayNames( fromDisplayName, toDisplayName );
}
/**
* Returns the display name for a property or link.
*/
public string GetPropDisplayName( int propId )
{
bool reverse = false;
bool directedLink = false;
PropTypeItem ptItem = (PropTypeItem) this [propId];
if ( ptItem.DataType == PropDataType.Link && ptItem.HasFlag( PropTypeFlags.DirectedLink ) )
{
directedLink = true;
if ( propId < 0 )
{
propId = -propId;
reverse = true;
}
}
string result;
if ( reverse )
{
result = ptItem.ReverseDisplayName;
if ( result == null )
{
result = ptItem.Name + " Target";
}
}
else
{
result = ptItem.DisplayName;
if ( result == null )
{
if ( directedLink )
{
result = ptItem.Name + " Source";
}
else
{
result = ptItem.Name;
}
}
}
return result;
}
internal bool IsValidType( int propId )
{
if ( propId < 0 )
{
return false;
}
if ( _propTypesCached )
{
if ( propId >= _propTypeCache.Count || _propTypeCache [propId] == null )
{
return false;
}
}
return true;
}
}
internal class SkipNullEnumerator: IEnumerator
{
private IEnumerator _baseEnumerator;
internal SkipNullEnumerator( IEnumerable baseEnumerable )
{
_baseEnumerator = baseEnumerable.GetEnumerator();
}
public void Reset()
{
_baseEnumerator.Reset();
}
public object Current
{
get
{
return _baseEnumerator.Current;
}
}
public bool MoveNext()
{
while( true )
{
if ( !_baseEnumerator.MoveNext() )
return false;
if ( _baseEnumerator.Current != null )
return true;
}
}
}
}