///
/// 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 JetBrains.Omea.Containers;
using JetBrains.Omea.OpenAPI;
using System.Collections;
using System.Collections.Specialized;
using JetBrains.Omea.Database;
using System.Diagnostics;
using System.Globalization;
namespace JetBrains.Omea.ResourceStore
{
/**
* Cached information for a single resource type.
*/
internal class ResourceTypeItem: IResourceType
{
private int _id;
private string _name;
private string _displayName;
private DisplayNameMask _displayNameTemplate;
private ResourceTypeFlags _flags;
private bool _ownerPluginLoaded;
private ResourceList _resourcesOfType;
internal ResourceTypeItem( int ID, string name, string displayNameTemplate, ResourceTypeFlags flags )
{
_id = ID;
_name = name;
_displayName = name;
_displayNameTemplate = new DisplayNameMask( displayNameTemplate, true );
_flags = flags;
_ownerPluginLoaded = true;
}
public int Id
{
get { return _id; }
}
public string Name
{
get { return _name; }
}
public string DisplayName
{
get
{
if ( _displayName == null || _displayName.Length == 0 )
{
return _name;
}
return _displayName;
}
set
{
(MyPalStorage.Storage.ResourceTypes as ResourceTypeCollection).UpdateResourceType(
_name, _displayNameTemplate.ToString(), _flags, value );
IResource res = MyPalStorage.Storage.FindUniqueResource( "ResourceType",
MyPalStorage.Storage.Props.Name, _name );
res.SetProp( "PropDisplayName", value );
}
}
public string ResourceDisplayNameTemplate
{
get { return _displayNameTemplate.ToString(); }
set
{
if ( _displayNameTemplate.ToString() != value )
{
(MyPalStorage.Storage.ResourceTypes as ResourceTypeCollection).UpdateResourceType(
_name, value, _flags, _displayName );
IResource res = MyPalStorage.Storage.FindUniqueResource( "ResourceType",
MyPalStorage.Storage.Props.Name, _name );
if ( res == null )
{
MyPalStorage.Storage.OnIndexCorruptionDetected( "set_ResourceDisplayNameTemplate: Could not find resource for resource type " +
_name );
}
else
{
res.SetProp( MyPalStorage.Storage.Props.DisplayNameMask, value );
}
}
}
}
internal DisplayNameMask DisplayNameTemplate
{
get { return _displayNameTemplate; }
}
public ResourceTypeFlags Flags
{
get { return _flags; }
set
{
ResourceTypeCollection resTypes = MyPalStorage.Storage.ResourceTypes as ResourceTypeCollection;
resTypes.UpdateResourceType( _name, _displayNameTemplate.ToString(), value, _displayName );
IResource res = MyPalStorage.Storage.FindUniqueResource( "ResourceType",
MyPalStorage.Storage.Props.Name, _name );
resTypes.SetResourceTypeFlags( res, value );
}
}
public bool HasFlag( ResourceTypeFlags flag )
{
return (_flags & flag) != 0;
}
internal void SetFlags( ResourceTypeFlags flags )
{
_flags = flags;
}
internal void SetDisplayName( string displayName )
{
_displayName = displayName;
}
internal void SetDisplayNameTemplate( string displayNameTemplate, bool validate )
{
_displayNameTemplate = new DisplayNameMask( displayNameTemplate, validate );
}
public bool OwnerPluginLoaded
{
get { return _ownerPluginLoaded; }
}
internal void SetOwnerPluginUnloaded()
{
_ownerPluginLoaded = false;
}
internal ResourceList ResourcesOfType
{
get { return _resourcesOfType; }
set { _resourcesOfType = value; }
}
}
/**
* A collection of registered resource types.
*/
internal class ResourceTypeCollection: IResourceTypeCollection
{
private MyPalStorage _storage;
private ITable _resourceTypeTable;
private ArrayList _resourceTypeCache = new ArrayList();
private Hashtable _resourceTypeNameCache = CollectionsUtil.CreateCaseInsensitiveHashtable();
internal struct ResourceTypeFlagMapping
{
public ResourceTypeFlags Flag;
public string Prop;
public object Value;
public ResourceTypeFlagMapping( ResourceTypeFlags flag, string prop, object value )
{
Flag = flag;
Prop = prop;
Value = value;
}
}
internal ResourceTypeFlagMapping[] _flagMap = new ResourceTypeFlagMapping[]
{ new ResourceTypeFlagMapping( ResourceTypeFlags.Internal, "Internal", 1 ),
new ResourceTypeFlagMapping( ResourceTypeFlags.ResourceContainer, "ResourceContainer", 1 ),
new ResourceTypeFlagMapping( ResourceTypeFlags.NoIndex, "NoIndex", 1 ),
new ResourceTypeFlagMapping( ResourceTypeFlags.CanBeUnread, "CanBeUnread", true ),
new ResourceTypeFlagMapping( ResourceTypeFlags.FileFormat, "FileFormat", 1 ) };
internal ResourceTypeCollection( MyPalStorage storage, ITable resourceTypeTable )
{
_storage = storage;
_resourceTypeTable = resourceTypeTable;
}
public IEnumerator GetEnumerator()
{
return new SkipNullEnumerator( _resourceTypeCache );
}
public IResourceType this [int ID]
{
get
{
if ( ID < 0 || ID >= _resourceTypeCache.Count )
throw new StorageException( "Invalid resource type ID " + ID + ": cache count " + _resourceTypeCache.Count );
ResourceTypeItem item = (ResourceTypeItem) _resourceTypeCache [ID];
if (item == null)
{
throw new StorageException( "Invalid resource type ID " + ID + ": null element in cache" );
}
return item;
}
}
internal IResourceType GetItemSafe( int id )
{
if ( id < 0 || id >= _resourceTypeCache.Count )
{
MyPalStorage.Storage.OnIndexCorruptionDetected( "Invalid resource type ID " + id + ": cache count " + _resourceTypeCache.Count );
return null;
}
ResourceTypeItem item = (ResourceTypeItem) _resourceTypeCache [id];
if (item == null)
{
MyPalStorage.Storage.OnIndexCorruptionDetected( "Invalid resource type ID " + id + ": cache count " + _resourceTypeCache.Count );
}
return item;
}
public IResourceType this [string name]
{
get
{
ResourceTypeItem item = (ResourceTypeItem) _resourceTypeNameCache [name];
if ( item == null )
throw new StorageException( "Invalid resource type name " + name );
return item;
}
}
public int Count
{
get { return _resourceTypeNameCache.Count; }
}
/**
* Registers a new resource type with a default display name, or returns the ID of the
* existing type if it has already been registered.
*/
public int Register( string name, string resourceDisplayNameTemplate )
{
return Register( name, name, resourceDisplayNameTemplate, ResourceTypeFlags.Normal, null );
}
public int Register( string name, string resourceDisplayNameTemplate, ResourceTypeFlags flags )
{
return Register( name, name, resourceDisplayNameTemplate, flags, null );
}
/**
* Registers a new resource type, or returns the ID of the existing type if
* it has already been registered.
*/
public int Register( string name, string displayName, string resourceDisplayNameTemplate )
{
return Register( name, displayName, resourceDisplayNameTemplate, ResourceTypeFlags.Normal, null );
}
/**
* Registers a new resource type, or returns the ID of the existing type if
* it has already been registered.
*/
public int Register( string name, string displayName, string resourceDisplayNameTemplate,
ResourceTypeFlags flags )
{
return Register( name, displayName, resourceDisplayNameTemplate, flags, null );
}
public int Register( string name, string displayName, string resourceDisplayNameTemplate,
ResourceTypeFlags flags, IPlugin ownerPlugin )
{
if ( resourceDisplayNameTemplate == null )
{
resourceDisplayNameTemplate = "";
}
// creating the mask instance validates it, and we don't want to get exceptions
// after some of the data has been created
new DisplayNameMask( resourceDisplayNameTemplate, true );
bool newType = false;
int ID = RegisterResourceTypeInternal( name, resourceDisplayNameTemplate, flags, false, out newType );
CreateOrUpdateResourceTypeResource( name, displayName, resourceDisplayNameTemplate, flags, ownerPlugin, ID, newType );
return ID;
}
internal void CreateOrUpdateResourceTypeResource( string name, string displayName,
string resourceDisplayNameTemplate, ResourceTypeFlags flags, IPlugin ownerPlugin,
int ID, bool newType )
{
ResourceTypeFlags oldFlags = ResourceTypeFlags.Normal;
IResource res;
if ( newType )
{
try
{
res = CreateResourceTypeResource( ID, name, resourceDisplayNameTemplate );
}
catch( ResourceRestrictionException ex )
{
MyPalStorage.Storage.OnIndexCorruptionDetected( "ResourceRestrictionException when creating PropType resource: " +
ex.Message );
return;
}
}
else
{
res = _storage.FindUniqueResource( "ResourceType", _storage.Props.Name, name );
oldFlags = this [name].Flags;
}
if ( res == null )
{
MyPalStorage.Storage.OnIndexCorruptionDetected( "Could not find ResourceType resource with name " + name );
}
else
{
SetResourceTypeFlags( res, flags );
res.SetProp( _storage.Props.PropDisplayName, displayName );
_storage.SetOwnerPlugin( res, ownerPlugin );
}
UpdateResourceTypeCache( ID, resourceDisplayNameTemplate, flags | oldFlags );
if ( newType )
{
_storage.CacheResourceTypePredicate( (ResourceTypeItem) _resourceTypeCache [ID] );
}
}
///
/// Creates a resource entry for the specified resource type.
///
private IResource CreateResourceTypeResource( int ID, string name, string displayNameMask )
{
IResource res = _storage.BeginNewResource( "ResourceType" );
res.SetProp( _storage.Props.TypeId, ID );
res.SetProp( _storage.Props.Name, name );
res.SetProp( _storage.Props.DisplayNameMask, displayNameMask );
res.EndUpdate();
return res;
}
/**
* Checks if all resource types in the specified list are registered.
*/
public bool Exist( params string[] resourceTypeNames )
{
foreach( string name in resourceTypeNames )
{
if ( _resourceTypeNameCache [name] == null )
return false;
}
return true;
}
/**
* Loads the resource types to the cache hash table.
*/
internal void CacheResourceTypes()
{
int count = 0;
IResultSet rs = _resourceTypeTable.CreateResultSet( 0 );
try
{
foreach( IRecord rec in rs )
{
int ID = rec.GetIntValue( 0 );
string name = rec.GetStringValue( 1 );
AddResourceTypeToCache( ID, name, "", ResourceTypeFlags.Normal );
count++;
}
}
finally
{
rs.Dispose();
}
Debug.WriteLine( "Loaded " + count + " resource types to cache" );
}
///
/// Adds a new ResourceTypeItem to the resource type cache.
///
private void AddResourceTypeToCache( int ID, string name, string displayNameTemplate,
ResourceTypeFlags flags )
{
if ( ID < 0 || ID > 65536 )
throw new BadIndexesException( "Invalid resource type ID " + ID );
lock( _resourceTypeCache )
{
ResourceTypeItem item = new ResourceTypeItem( ID, name, displayNameTemplate, flags );
while( _resourceTypeCache.Count < ID )
{
_resourceTypeCache.Add( null );
}
if ( _resourceTypeCache.Count == ID )
{
_resourceTypeCache.Add( item );
}
else
{
_resourceTypeCache [ID] = item;
}
_resourceTypeNameCache [name] = item;
}
}
/**
* Loads the flags, display name masks and display names of resource types
* to the cache hash table.
*/
internal void CacheResourceTypeFlags()
{
foreach( ResourceTypeItem item in _resourceTypeCache )
{
if ( item != null )
{
IResource res = _storage.FindUniqueResource( "ResourceType", "Name", item.Name );
if ( res != null )
{
item.SetFlags( GetFlagsFromResource( res ) );
item.SetDisplayName( res.GetStringProp( "PropDisplayName" ) );
item.SetDisplayNameTemplate( res.GetStringProp( "DisplayNameMask" ), false );
}
}
}
}
private ResourceTypeFlags GetFlagsFromResource( IResource res )
{
ResourceTypeFlags flags = ResourceTypeFlags.Normal;
foreach( ResourceTypeFlagMapping flagMapping in _flagMap )
{
if ( flagMapping.Value.Equals( res.GetProp( flagMapping.Prop ) ) )
{
flags |= flagMapping.Flag;
}
}
return flags;
}
internal void SetResourceTypeFlags( IResource res, ResourceTypeFlags flags )
{
foreach( ResourceTypeFlagMapping flagMapping in _flagMap )
{
if ( ( flags & flagMapping.Flag ) != 0 )
{
res.SetProp( flagMapping.Prop, flagMapping.Value );
}
else
{
res.DeleteProp( flagMapping.Prop );
}
}
}
/**
* Adds a record for the specified resource type to the DB.
*/
internal int RegisterResourceTypeInternal( string name, string displayNameTemplate, ResourceTypeFlags flags,
bool skipChecks, out bool newType )
{
_storage.CheckOwnerThread();
IRecord rec = _resourceTypeTable.GetRecordByEqual( 1, name );
if ( rec != null )
{
if ( !_resourceTypeNameCache.ContainsKey( name ) )
{
throw new BadIndexesException( "Resource type " + name + " found in ResourceTypes table but missing in name cache" );
}
string oldDisplayNameTemplate = rec.GetStringValue( 2 );
if ( !skipChecks && String.Compare( oldDisplayNameTemplate, displayNameTemplate, true, CultureInfo.InvariantCulture ) != 0 )
{
if ( oldDisplayNameTemplate.Length == 0 )
{
rec.SetValue( 2, displayNameTemplate );
_storage.SafeCommitRecord( rec, "ResourceTypeCollection.RegisterResourceTypeInternal" );
}
else
{
throw new StorageException( "Inconsistent display name template for resource type " + name +
"\nOld: " + oldDisplayNameTemplate + " New: " + displayNameTemplate );
}
}
newType = false;
return rec.GetIntValue( 0 );
}
int ID;
lock( _resourceTypeTable )
{
IRecord resourceType = _resourceTypeTable.NewRecord();
resourceType.SetValue( 1, name );
resourceType.SetValue( 2, displayNameTemplate );
_storage.SafeCommitRecord( resourceType, "ResourceTypeCollection.RegisterResourceTypeInternal" );
ID = resourceType.GetID();
if ( ID > 65536 )
{
MyPalStorage.Storage.OnIndexCorruptionDetected( "Invalid next ID in property type table" );
}
}
AddResourceTypeToCache( ID, name, displayNameTemplate, flags );
newType = true;
return ID;
}
/**
* When a ResourceType resource is changed, updates the DB data for it.
*/
internal void UpdateResourceTypeFromResource( Resource res )
{
UpdateResourceType( res.GetStringProp( "Name" ), res.GetStringProp( "DisplayNameMask" ),
GetFlagsFromResource( res ), res.GetStringProp( "PropDisplayName" ) );
}
internal void UpdateResourceType( string name, string displayNameTemplate, ResourceTypeFlags flags,
string propDisplayName )
{
ICountedResultSet rs = _resourceTypeTable.CreateModifiableResultSet( 1, name );
try
{
if ( rs.Count > 0 )
{
IRecord rec = rs [0];
if ( rec.GetStringValue( 2 ) != displayNameTemplate )
{
rec.SetValue( 2, displayNameTemplate );
_storage.SafeCommitRecord( rec, "ResourceTypeCollection.UpdateResourceType" );
}
int resID = rec.GetIntValue( 0 );
UpdateResourceTypeCache( resID, displayNameTemplate, flags );
ResourceTypeItem item = (ResourceTypeItem) this [resID];
item.SetDisplayName( propDisplayName );
}
}
finally
{
rs.Dispose();
}
}
internal void UpdateResourceTypeCache( int resTypeID, string displayNameMask, ResourceTypeFlags flags )
{
ResourceTypeItem item = (ResourceTypeItem) _resourceTypeCache [resTypeID];
Debug.Assert( item != null );
Debug.Assert( item.Id == resTypeID );
item.SetDisplayNameTemplate( displayNameMask, true );
item.SetFlags( flags );
}
public void Delete( string name )
{
ResourceTypeItem item = (ResourceTypeItem) this [name];
IResourceList resList = _storage.GetAllResources( name );
resList.DeleteAll();
_resourceTypeCache [item.Id] = null;
_resourceTypeNameCache.Remove( name );
ICountedResultSet rs = _resourceTypeTable.CreateModifiableResultSet( 0, item.Id );
try
{
_storage.SafeDeleteRecord( rs [0], "ResourceTypes.Delete" );
}
finally
{
rs.Dispose();
}
IResource resourceTypeRes = _storage.FindUniqueResource( "ResourceType", "Name", name );
Debug.Assert( resourceTypeRes != null );
resourceTypeRes.Delete();
}
}
}