///
/// 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.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using JetBrains.Omea.Base;
using JetBrains.Omea.Database;
using JetBrains.Omea.Containers;
using JetBrains.Omea.OpenAPI;
using ICSharpCode.SharpZipLib.Zip;
using JetBrains.DataStructures;
namespace JetBrains.Omea.ResourceStore
{
public class MyPalStorage: IResourceStore
{
private static MyPalStorage _theStorage;
private static string _dbPath = ".\\db";
private DBStructure _dbStructure;
private IDatabase _database;
private bool _indexesRebuilt;
private bool _initializationComplete = false;
private bool _shutDown = false;
private static bool _trace = false;
private ITable _propTypeTable;
private ITable _resourceTypeTable;
private ITable _resources;
private ITable _intProps;
private ITable _stringProps;
private ITable _longStringProps;
private ITable _dateProps;
private ITable _blobProps;
private ITable _doubleProps;
private ITable _boolProps;
private ITable _stringListProps;
private ITable _links;
private ArrayList _propTables = new ArrayList();
private ArrayList _contentTables = new ArrayList();
private IntObjectCache _resourceCache;
private IntWeakHashTable _resourceWeakCache;
private IntWeakHashTable _transientResources = new IntWeakHashTable( 256 ); // resource ID -> Resource
private PropTypeCollection _propTypes;
private ResourceTypeCollection _resourceTypes;
private ResourceStoreProps _props;
private IntHashTable _updatingResources = new IntHashTable(); // resource ID -> MultiPropChangeSet
private ITextIndexManager _textIndexManager;
private static int _resourceCacheSize = 2048;
private ResourceListUpdateManager _updateManager = new ResourceListUpdateManager();
public event ResourcePropEventHandler ResourceSaved;
public event EventHandler ResourceDeleting;
public event LinkEventHandler LinkAdded;
public event LinkEventHandler LinkDeleted;
public event EventHandler IndexCorruptionDetected;
public event ThreadExceptionEventHandler IOErrorDetected;
private Thread _ownerThread = null;
private static IProgressWindow _progressWindow = null;
private IResourceList _emptyResourceList;
private bool _repairRequired = false;
private bool _fullRepairRequired = false;
private bool _indexCorruptionReported = false;
private bool _ioErrorReported = false;
private HashMap _predicateCache = new HashMap(); // ResourceListPredicate -> CachingPredicate
private ArrayList _displayNameProviders = new ArrayList();
private IntObjectCache _loadedResourceTypes = new IntObjectCache( 256 );
private ObjectCache _findUniqueResourceCache = new ObjectCache( 1024 );
private SpinWaitLock _findUniqueResourceCacheLock = new SpinWaitLock();
public static void SetProgressWindow( IProgressWindow progressWindow )
{
_progressWindow = progressWindow;
}
public static MyPalStorage Storage
{
[DebuggerStepThrough] get { return _theStorage; }
}
public static bool TraceOperations
{
get { return _trace; }
set { _trace = value; }
}
public static int ResourceCacheSize
{
get { return _resourceCacheSize; }
set { _resourceCacheSize = value; }
}
public static string DBPath
{
get { return _dbPath; }
set { _dbPath = value; }
}
public bool RepairRequired
{
get { return _repairRequired; }
}
public bool FullRepairRequired
{
get { return _fullRepairRequired; }
}
internal void SetRepairRequired()
{
_repairRequired = true;
}
protected internal MyPalStorage()
{
if ( _theStorage != null && !_theStorage._shutDown )
{
throw new InvalidOperationException( "Attempt to create multiple ResourceStore instances" );
}
_theStorage = this;
_transientResources.ValueDead += new IntWeakHashTable.ValueDeadDelegate( HandleTransientResourceDead );
RebuildProgressListenerEvent +=new RebuildProgressEventHandler(_theStorage_RebuildProgressListenerEvent);
}
public ITextIndexManager TextIndexManager
{
get { return _textIndexManager; }
set { _textIndexManager = value; }
}
public Thread OwnerThread
{
get { return _ownerThread; }
set { _ownerThread = value; }
}
public IPropTypeCollection PropTypes
{
get { return _propTypes; }
}
public IResourceTypeCollection ResourceTypes
{
get { return _resourceTypes; }
}
internal ResourceStoreProps Props
{
get { return _props; }
}
public static void CreateDatabase()
{
if (!Directory.Exists( DBPath ) )
Directory.CreateDirectory( DBPath );
DBStructure dbStructure =
new DBStructure( DBPath, "MyPal", DatabaseMode.Create );
TableStructure table = dbStructure.CreateTable( "PropTypes" );
table.CreateColumn( "Id", ColumnType.Integer, true );
table.CreateColumn( "Name", ColumnType.String, true );
table.CreateColumn( "Type", ColumnType.Integer, false );
table.CreateColumn( "Flags", ColumnType.Integer, false );
table = dbStructure.CreateTable( "ResourceTypes" );
table.CreateColumn( "Id", ColumnType.Integer, true );
table.CreateColumn( "Name", ColumnType.String, true );
table.CreateColumn( "DisplayNameMask", ColumnType.String, false );
table = dbStructure.CreateTable( "Resources" );
table.CreateColumn( "Id", ColumnType.Integer, true );
table.CreateColumn( "TypeID", ColumnType.Integer, true );
table = dbStructure.CreateTable( "IntProps" );
table.CreateColumn( "Id", ColumnType.Integer, false );
table.CreateColumn( "PropType", ColumnType.Integer, false );
table.CreateColumn( "PropValue", ColumnType.Integer, false );
table.SetCompoundIndexWithValue( "Id", "PropType", "PropValue" );
table.SetCompoundIndexWithValue( "PropType", "PropValue", "Id" );
table = dbStructure.CreateTable( "StringProps" );
table.CreateColumn( "Id", ColumnType.Integer, false );
table.CreateColumn( "PropType", ColumnType.Integer, false );
table.CreateColumn( "PropValue", ColumnType.String, false );
table.SetCompoundIndex( "Id", "PropType" );
table.SetCompoundIndex( "PropType", "PropValue" );
table = dbStructure.CreateTable( "LongStringProps" );
table.CreateColumn( "Id", ColumnType.Integer, false );
table.CreateColumn( "PropType", ColumnType.Integer, false );
table.CreateColumn( "PropValue", ColumnType.String, false );
table.SetCompoundIndex( "Id", "PropType" );
table = dbStructure.CreateTable( "StringListProps" );
table.CreateColumn( "Id", ColumnType.Integer, false );
table.CreateColumn( "PropType", ColumnType.Integer, false );
table.CreateColumn( "PropValue", ColumnType.String, false );
table.SetCompoundIndex( "Id", "PropType" );
table.SetCompoundIndex( "PropType", "PropValue" );
table = dbStructure.CreateTable( "DateProps" );
table.CreateColumn( "Id", ColumnType.Integer, false );
table.CreateColumn( "PropType", ColumnType.Integer, false );
table.CreateColumn( "PropValue", ColumnType.DateTime, false );
table.SetCompoundIndexWithValue( "Id", "PropType", "PropValue" );
table.SetCompoundIndexWithValue( "PropType", "PropValue", "Id" );
table = dbStructure.CreateTable( "BlobProps" );
table.CreateColumn( "Id", ColumnType.Integer, false );
table.CreateColumn( "PropType", ColumnType.Integer, false );
table.CreateColumn( "PropValue", ColumnType.BLOB, false );
table.SetCompoundIndex( "Id", "PropType" );
table = dbStructure.CreateTable( "DoubleProps" );
table.CreateColumn( "Id", ColumnType.Integer, false );
table.CreateColumn( "PropType", ColumnType.Integer, false );
table.CreateColumn( "PropValue", ColumnType.Double, false );
table.SetCompoundIndex( "Id", "PropType" );
table = dbStructure.CreateTable( "BoolProps" );
table.CreateColumn( "Id", ColumnType.Integer, true );
table.CreateColumn( "PropType", ColumnType.Integer, true );
table = dbStructure.CreateTable( "Links" );
table.CreateColumn( "Id", ColumnType.Integer, false );
table.CreateColumn( "Id2", ColumnType.Integer, false );
table.CreateColumn( "PropType", ColumnType.Integer, true );
table.SetCompoundIndexWithValue( "Id", "Id2", "PropType" );
table.SetCompoundIndexWithValue( "Id2", "Id", "PropType" );
dbStructure.SaveStructure();
dbStructure.Shutdown();
}
public static void OpenDatabase( )
{
if ( _theStorage == null || _theStorage._shutDown )
{
new MyPalStorage();
}
_theStorage.DoOpenDatabase();
}
public static bool DatabaseExists()
{
if ( !DBHelper.DatabaseExists( DBPath, "MyPal" ) )
{
Trace.WriteLine( "ResourceStore initialization: database does not exist" );
return false;
}
return true;
}
public static string DBCreatorBuild
{
get
{
if ( _theStorage == null || _theStorage._shutDown )
{
new MyPalStorage();
}
if ( _theStorage._dbStructure == null )
{
_theStorage._dbStructure = new DBStructure( DBPath, "MyPal" );
}
_theStorage._dbStructure.LoadVersionInfo();
return _theStorage._dbStructure.Build;
}
}
protected void DoOpenDatabase()
{
if ( _dbStructure == null )
{
_dbStructure = new DBStructure( DBPath, "MyPal" );
}
_dbStructure.ProgressListenerEvent += new DBStructure.ProgressEventHandler( dbStructure_ProgressListenerEvent );
_dbStructure.LoadStructure( true );
CheckCreateLongStringProps();
CheckCreateBoolProps();
CheckCreateStringListProps();
CheckCreateIndexesWithValue();
if ( !_dbStructure.IsDatabaseCorrect() )
{
_indexesRebuilt = true;
_dbStructure.RebuildIndexes();
}
_dbStructure.LoadStructure();
_database = _dbStructure.Database;
_resourceCache = new IntObjectCache( _resourceCacheSize );
_resourceCache.ObjectRemoved += new IntObjectCacheEventHandler( _resourceCache_ObjectRemoved );
_resourceWeakCache = new IntWeakHashTable( 1024 );
_propTypeTable = CheckGetTable( "PropTypes" );
_resourceTypeTable = CheckGetTable( "ResourceTypes" );
_resources = CheckGetTable( "Resources" );
_intProps = CheckGetTable( "IntProps" );
_stringProps = CheckGetTable( "StringProps" );
_longStringProps = CheckGetTable( "LongStringProps" );
_dateProps = CheckGetTable( "DateProps" );
_blobProps = CheckGetTable( "BlobProps" );
_doubleProps = CheckGetTable( "DoubleProps" );
_boolProps = CheckGetTable( "BoolProps" );
_links = CheckGetTable( "Links" );
_stringListProps = CheckGetTable( "StringListProps" );
_dbStructure.SetSortedColumns();
_propTypes = new PropTypeCollection( this, _propTypeTable );
_resourceTypes = new ResourceTypeCollection( this, _resourceTypeTable );
_contentTables.AddRange( new ITable[]
{ _resources,
_intProps, _stringProps, _longStringProps, _dateProps, _blobProps, _doubleProps,
_boolProps, _links, _stringListProps } );
_propTables.AddRange( new ITable[]
{ _intProps, _stringProps, _dateProps, _blobProps, _doubleProps, _longStringProps,
_stringListProps } );
foreach( ITable table in _theStorage._contentTables )
{
table.AutoFlush = false;
}
_resourceTypes.CacheResourceTypes();
_propTypes.CachePropTypes();
_props = new ResourceStoreProps( _resourceTypes, _propTypes );
_props.Initialize();
_propTypes.CachePropDisplayNames();
_resourceTypes.CacheResourceTypeFlags();
_emptyResourceList = ListFromIds( new IntArrayList(), false );
_loadedResourceTypes.RemoveAll();
foreach( ResourceTypeItem resType in ResourceTypes )
{
CacheResourceTypePredicate( resType );
}
_initializationComplete = true;
}
public static void QueueDefragmentTableIndexes()
{
foreach( ITable table in _theStorage._contentTables )
{
DefragmentTableIndexesInIdleMode( table );
}
}
public static void QueueIdleDatabaseBackup()
{
DateTime lastBackupDate = Core.SettingStore.ReadDate( "ResourceStore", "LastBackupDate", DateTime.MinValue );
Core.ResourceAP.QueueJobAt(
lastBackupDate.AddDays( 1 ), new BackupDatabaseDelegate( BackupDatabase ), true );
}
public void FlushTables()
{
foreach( ITable table in _contentTables )
{
Monitor.Enter( table );
try
{
table.FlushData();
}
finally
{
Monitor.Exit( table );
}
}
}
private delegate void DefragmentTableIndexesInIdleModeDelegate( ITable table );
private static void DefragmentTableIndexesInIdleMode( ITable table )
{
string message = "Defragmenting indexes of " + table.Name;
DateTime when = Core.SettingStore.ReadDate( "ResourceStore", table.Name + "LastDefragTime", DateTime.MinValue ).AddDays( 1 );
if( when > DateTime.Now )
{
Core.ResourceAP.QueueJobAt(
when, message, new DefragmentTableIndexesInIdleModeDelegate( DefragmentTableIndexesInIdleMode ), table );
}
else
{
if( Core.IsSystemIdle && Core.State == CoreState.Running )
{
IStatusWriter writer = Core.UIManager.GetStatusWriter( table, StatusPane.UI );
try
{
writer.ShowStatus( message + "..." );
table.DefragmentIndexes( true );
}
finally
{
writer.ClearStatus();
}
if( Core.IsSystemIdle )
{
Core.SettingStore.WriteDate( "ResourceStore", table.Name + "LastDefragTime", DateTime.Now );
Core.ResourceAP.QueueJobAt( DateTime.Now.AddDays( 1 ),
message, new DefragmentTableIndexesInIdleModeDelegate( DefragmentTableIndexesInIdleMode ), table );
}
}
else
{
int idlePeriod = Core.SettingStore.ReadInt( "Startup", "IdlePeriod", 5 );
Core.ResourceAP.QueueJobAt( DateTime.Now.AddMinutes( idlePeriod ),
message, new DefragmentTableIndexesInIdleModeDelegate( DefragmentTableIndexesInIdleMode ), table );
}
}
}
internal void CacheResourceTypePredicate( ResourceTypeItem resType )
{
if ( _loadedResourceTypes.Count >= _loadedResourceTypes.Size-10 )
{
_loadedResourceTypes = new IntObjectCache( _loadedResourceTypes.Size*2 );
foreach( ResourceTypeItem aResType in ResourceTypes )
{
CacheResourceTypePredicate( aResType );
}
}
else
{
resType.ResourcesOfType = (ResourceList) GetAllResourcesLive( resType.Name );
resType.ResourcesOfType.Sort( new SortSettings( ResourceProps.Id, true ) );
resType.ResourcesOfType.Instantiate( false );
CachePredicate( resType.ResourcesOfType );
_loadedResourceTypes.CacheObject( resType.Id, resType );
}
}
private ITable CheckGetTable( string tableName )
{
ITable table = _database.GetTable( tableName );
// if the index was broken and not rebuilt correctly, Count (returned by the index)
// will be zero, but IsEmpty will return false
try
{
if ( !table.IsEmpty() && table.Count == 0 )
{
table.RebuildIndexes();
}
}
catch( Exception e )
{
throw new IndexRebuildException( "Rebuilding indexes on " + tableName + " failed", e );
}
return table;
}
/**
* Creates the LongStringProps table if it does not exist in the database.
* @return true if the table was created
*/
private bool CheckCreateLongStringProps()
{
try
{
_dbStructure.GetTable( "LongStringProps" );
}
catch( TableDoesNotExistException )
{
TableStructure table = _dbStructure.CreateTable( "LongStringProps" );
table.CreateColumn( "Id", ColumnType.Integer, false );
table.CreateColumn( "PropType", ColumnType.Integer, false );
table.CreateColumn( "PropValue", ColumnType.String, false );
table.SetCompoundIndex( "Id", "PropType" );
_dbStructure.SaveStructure();
return true;
}
return false;
}
/**
* Creates the BoolProps table if it does not exist in the database.
* @return true if the table was created
*/
private bool CheckCreateBoolProps()
{
try
{
_dbStructure.GetTable( "BoolProps" );
}
catch( TableDoesNotExistException )
{
TableStructure table = _dbStructure.CreateTable( "BoolProps" );
table.CreateColumn( "Id", ColumnType.Integer, true );
table.CreateColumn( "PropType", ColumnType.Integer, true );
_dbStructure.SaveStructure();
return true;
}
return false;
}
/**
* Creates the StringListProps table if it does not exist in the database.
*/
private bool CheckCreateStringListProps()
{
try
{
_dbStructure.GetTable( "StringListProps" );
}
catch( TableDoesNotExistException )
{
TableStructure table = _dbStructure.CreateTable( "StringListProps" );
table.CreateColumn( "Id", ColumnType.Integer, false );
table.CreateColumn( "PropType", ColumnType.Integer, false );
table.CreateColumn( "PropValue", ColumnType.String, false );
table.SetCompoundIndex( "Id", "PropType" );
table.SetCompoundIndex( "PropType", "PropValue" );
_dbStructure.SaveStructure();
return true;
}
return false;
}
/**
* If the property tables have regular indexes, drop them and create indexes
* with value instead.
*/
private bool CheckCreateIndexesWithValue()
{
bool changed = false;
TableStructure intProps = _dbStructure.GetTable( "IntProps" );
if( intProps.HasCompoundIndex( "Id", "PropType" ) )
{
changed = true;
intProps.DropCompoundIndex( "Id", "PropType" );
intProps.SetCompoundIndexWithValue( "Id", "PropType", "PropValue" );
intProps.DropCompoundIndex( "PropType", "PropValue" );
intProps.SetCompoundIndexWithValue( "PropType", "PropValue", "Id" );
}
TableStructure dateProps = _dbStructure.GetTable( "DateProps" );
if( dateProps.HasCompoundIndex( "Id", "PropType" ) )
{
changed = true;
dateProps.DropCompoundIndex( "Id", "PropType" );
dateProps.SetCompoundIndexWithValue( "Id", "PropType", "PropValue" );
dateProps.DropCompoundIndex( "PropType", "PropValue" );
dateProps.SetCompoundIndexWithValue( "PropType", "PropValue", "Id" );
}
TableStructure links = _dbStructure.GetTable( "Links" );
if ( links.HasCompoundIndex( "Id", "Id2" ) )
{
changed = true;
links.DropCompoundIndex( "Id", "Id2" );
links.SetCompoundIndexWithValue( "Id", "PropType", "Id2" );
links.DropCompoundIndex( "Id2", "Id" );
links.SetCompoundIndexWithValue( "Id2", "PropType", "Id" );
}
else if ( links.HasCompoundIndexWithValue( "Id", "Id2" ) )
{
changed = true;
links.DropCompoundIndexWithValue( "Id", "Id2" );
links.SetCompoundIndexWithValue( "Id", "PropType", "Id2" );
links.DropCompoundIndexWithValue( "Id2", "Id" );
links.SetCompoundIndexWithValue( "Id2", "PropType", "Id" );
}
if ( changed )
{
_dbStructure.SaveStructure();
}
return changed;
}
public static void CloseDatabase()
{
if ( _theStorage != null && !_theStorage._shutDown )
{
try
{
if ( _theStorage._dbStructure != null && !_theStorage._ioErrorReported )
{
//_theStorage.ReportHandlerLeaks();
Trace.WriteLine( "Closing database..." );
_theStorage._dbStructure.Shutdown();
if ( _theStorage._resourceCache != null )
{
Trace.WriteLine( "Resource cache hit rate: " + (int) ( _theStorage._resourceCache.HitRate() * 100 ) + "%" );
}
}
else
{
Trace.WriteLine( "MyPalStorage.CloseDatabase(): dbStructure is null" );
}
}
finally
{
_theStorage._shutDown = true;
}
}
}
public void DefragmentDatabase( IProgressWindow progressWindow )
{
// for better performance, optimize no more than one table at a time
ArrayList tables = new ArrayList();
tables.Add( _resources );
tables.AddRange( _propTables );
tables.Add( _links );
tables.Add( _propTypeTable );
tables.Add( _resourceTypeTable );
foreach( ITable tbl in tables )
{
if ( CheckDefragmentTable( tbl, progressWindow ) )
return;
}
}
private bool CheckDefragmentTable( ITable table, IProgressWindow progressWindow )
{
RecordsCounts counts = table.ComputeWastedSpace();
long deletedRecordsCount = counts.TotalRecordCount - counts.NormalRecordCount;
if ( deletedRecordsCount > counts.NormalRecordCount / 2 )
{
progressWindow.UpdateProgress( 0, "Defragmenting " + table.Name + "...", null );
table.Defragment();
return true;
}
return false;
}
public static void FlushDatabase()
{
if ( _theStorage != null && _theStorage._database != null )
{
_theStorage._database.Flush();
}
}
private static readonly string[] _dbFiles = new string[]
{
"MyPal.BlobFileSystem.dbUtil",
"MyPal.BlobProps.table.dbUtil",
"MyPal.BoolProps.table.dbUtil",
"MyPal.database.struct.dbUtil",
"MyPal.DateProps.table.dbUtil",
"MyPal.DoubleProps.table.dbUtil",
"MyPal.IntProps.table.dbUtil",
"MyPal.Links.table.dbUtil",
"MyPal.LongStringProps.table.dbUtil",
"MyPal.PropTypes.table.dbUtil",
"MyPal.Resources.table.dbUtil",
"MyPal.ResourceTypes.table.dbUtil",
"MyPal.StringListProps.table.dbUtil",
"MyPal.StringProps.table.dbUtil",
"MyPal_btree.BlobProps.Id#PropType.index.dbUtil",
"MyPal_btree.BoolProps.Id.index.dbUtil",
"MyPal_btree.BoolProps.PropType.index.dbUtil",
"MyPal_btree.DateProps.Id#PropType.index.dbUtil",
"MyPal_btree.DateProps.PropType#PropValue.index.dbUtil",
"MyPal_btree.DoubleProps.Id#PropType.index.dbUtil",
"MyPal_btree.IntProps.Id#PropType.index.dbUtil",
"MyPal_btree.IntProps.PropType#PropValue.index.dbUtil",
"MyPal_btree.Links.Id#PropType.index.dbUtil",
"MyPal_btree.Links.Id2#PropType.index.dbUtil",
"MyPal_btree.Links.PropType.index.dbUtil",
"MyPal_btree.LongStringProps.Id#PropType.index.dbUtil",
"MyPal_btree.PropTypes.Id.index.dbUtil",
"MyPal_btree.PropTypes.Name.index.dbUtil",
"MyPal_btree.Resources.Id.index.dbUtil",
"MyPal_btree.Resources.TypeID.index.dbUtil",
"MyPal_btree.ResourceTypes.Id.index.dbUtil",
"MyPal_btree.ResourceTypes.Name.index.dbUtil",
"MyPal_btree.StringListProps.Id#PropType.index.dbUtil",
"MyPal_btree.StringListProps.PropType#PropValue.index.dbUtil",
"MyPal_btree.StringProps.Id#PropType.index.dbUtil",
"MyPal_btree.StringProps.PropType#PropValue.index.dbUtil"
};
public const string _dbBackupFile = "MyPal.Backup.zip";
public static DateTime BackupTime()
{
return IOTools.GetFileLastWriteTime( IOTools.Combine( Core.SettingStore.ReadString( "ResourceStore", "BackupPath", string.Empty ), _dbBackupFile ) );
}
public static void RestoreFromBackup()
{
CloseDatabase();
string backupPath = Core.SettingStore.ReadString( "ResourceStore", "BackupPath", string.Empty );
RestoreFromBackup( IOTools.Combine( backupPath, _dbBackupFile ) );
}
private delegate void BackupDatabaseDelegate( bool idle );
public static void BackupDatabase( bool idle )
{
if( !_theStorage.IsOwnerThread() )
{
Core.ResourceAP.QueueJob( new BackupDatabaseDelegate( BackupDatabase ), idle );
}
else
{
bool backupEnabled = Core.SettingStore.ReadBool( "ResourceStore", "EnableBackup", false );
if( backupEnabled )
{
string backupPath = Core.SettingStore.ReadString( "ResourceStore", "BackupPath", string.Empty );
if( backupPath.Length == 0 || ( idle && !Core.IsSystemIdle ) )
{
int idlePeriod = Core.SettingStore.ReadInt( "Startup", "IdlePeriod", 5 );
Core.ResourceAP.QueueJobAt( DateTime.Now.AddMinutes( idlePeriod ),
new BackupDatabaseDelegate( BackupDatabase ), idle );
}
else
{
FlushDatabase();
BackupDatabase( IOTools.Combine( backupPath, _dbBackupFile ) );
Core.SettingStore.WriteDate( "ResourceStore", "LastBackupDate", DateTime.Now );
QueueIdleDatabaseBackup();
}
}
}
}
public static void BackupDatabase( string backupFilename )
{
long startTicks = DateTime.Now.Ticks;
long totalLen = 0;
foreach( string dbFile in _dbFiles )
{
if( dbFile.IndexOf( "_btree" ) < 0 )
{
FileInfo fi = IOTools.GetFileInfo( IOTools.Combine( DBPath, dbFile ) );
if( fi != null )
{
totalLen += fi.Length;
}
}
}
byte[] buffer = new byte[ 0x10000 ];
FileStream zipFile = File.Create( backupFilename );
ZipOutputStream zipStream = new ZipOutputStream( zipFile );
IStatusWriter statusWriter = ( ICore.Instance == null ) ? null : Core.UIManager.GetStatusWriter( _theStorage, StatusPane.UI );
long count = 0;
int lastPercent = -1;
foreach( string dbFile in _dbFiles )
{
if( dbFile.IndexOf( "_btree" ) >= 0 )
{
continue;
}
FileStream fs = new FileStream( IOTools.Combine( DBPath, dbFile ), FileMode.Open, FileAccess.Read, FileShare.ReadWrite );
ZipEntry entry = new ZipEntry( Path.GetFileName( dbFile ) );
entry.DateTime = DateTime.Now;
entry.Size = fs.Length;
zipStream.PutNextEntry( entry );
int readBytes;
while( ( readBytes = fs.Read( buffer, 0, buffer.Length ) ) > 0 )
{
if( statusWriter != null )
{
int percent = (int) ( ( count * 100 ) / totalLen );
if( percent > lastPercent )
{
lastPercent = percent;
statusWriter.ShowStatus( "Database backup: " + percent + "%" );
}
}
zipStream.Write( buffer, 0, readBytes );
count += readBytes;
}
fs.Close();
}
zipStream.Finish();
zipStream.Close();
long ticks = DateTime.Now.Ticks - startTicks;
Debug.WriteLine( "Database backup took " + ticks / 10000 + " ms" );
if( statusWriter != null )
{
statusWriter.ClearStatus();
}
}
public static void RestoreFromBackup( string backupFilename )
{
IProgressWindow pw = ( ICore.Instance == null ) ? null : Core.ProgressWindow;
if( pw != null )
{
pw.UpdateProgress( 0, "Restoring database from backup...", null );
}
foreach( string dbFile in _dbFiles )
{
File.Delete( dbFile );
}
FileStream zipFile = File.OpenRead( backupFilename );
ZipInputStream zip = new ZipInputStream( zipFile );
ZipEntry theEntry;
long totalLen = 0;
while ( ( theEntry = zip.GetNextEntry() ) != null )
{
totalLen += theEntry.Size;
}
zip.Close();
zipFile = File.OpenRead( backupFilename );
zip = new ZipInputStream( zipFile );
byte[] buffer = new byte[ 0x10000 ];
long count = 0;
while ( ( theEntry = zip.GetNextEntry() ) != null )
{
FileStream streamWriter = File.Create( IOTools.Combine( DBPath, theEntry.Name ) );
while( true )
{
if( pw != null )
{
int percent = (int) ( count * 100 / totalLen );
if( percent > 100 )
{
percent = 100;
}
pw.UpdateProgress( percent, "Restoring database from backup...", null );
}
int size = zip.Read( buffer, 0, buffer.Length );
if( size == 0 ) break;
streamWriter.Write( buffer, 0, size );
count += size;
}
streamWriter.Close();
}
zip.Close();
}
public static Exception RunFullRepair( RepairProgressEventHandler progressHandler )
{
CloseDatabase();
DBStructure dbStructure = new DBStructure( DBPath, "MyPal" );
dbStructure.ProgressListenerEvent += new DBStructure.ProgressEventHandler( _theStorage_RebuildProgressListenerEvent );
dbStructure.LoadStructure( true );
dbStructure.RebuildIndexes( true );
IDatabase db = dbStructure.OpenDatabase();
ResourceStoreRepair repair = new ResourceStoreRepair( db );
repair.RepairProgress += new RepairProgressEventHandler( progressHandler );
repair.FixErrors = true;
repair.Run();
return repair.RepairException;
}
public string BuildNumber
{
get { return _dbStructure.Build; }
set
{
if ( _dbStructure.Build != value )
{
_dbStructure.Build = value;
_dbStructure.SaveStructure();
}
}
}
public bool IndexesRebuilt
{
get { return _indexesRebuilt; }
}
internal void AddUpdateListener( IUpdateListener listener, bool priority )
{
_updateManager.AddUpdateListener( listener, priority );
}
internal void RemoveUpdateListener( IUpdateListener listener, bool priority )
{
_updateManager.RemoveUpdateListener( listener, priority );
}
public int ResourceSavedHandlerCount
{
get { return _updateManager.ListenerCount; }
}
public int TransientResourceCount
{
get { return _transientResources.Count; }
}
public void TraceLiveResourceLists()
{
_updateManager.TraceListeners();
}
public void TraceDbPerformanceCounters()
{
long result = 0;
result += _propTypeTable.TracePerformanceCounters();
result += _resourceTypeTable.TracePerformanceCounters();
result += _resources.TracePerformanceCounters();
foreach( ITable table in _propTables )
{
result += table.TracePerformanceCounters();
}
result += _links.TracePerformanceCounters();
Trace.WriteLine( "Total DB bytes read: " + Utils.SizeToString( result ) );
}
internal void CheckOwnerThread()
{
if ( !IsOwnerThread() )
{
throw new StorageException( "Write access to the resource store is only allowed from the resource thread" );
}
}
public bool IsOwnerThread()
{
if ( _ownerThread == null )
return true;
return Thread.CurrentThread == _ownerThread;
}
internal void OnIndexCorruptionDetected( string reason )
{
if ( !_initializationComplete )
{
throw new BadIndexesException( "Index corruption detected when opening the database: " + reason );
}
Trace.WriteLine( "MyPalStorage index corruption detected: " + reason );
if ( _indexCorruptionReported )
{
return;
}
_indexCorruptionReported = true;
_fullRepairRequired = true;
if ( IndexCorruptionDetected != null )
{
IndexCorruptionDetected( this, EventArgs.Empty );
}
}
internal void OnIOErrorDetected( Exception exception )
{
Trace.WriteLine( "MyPalStorage I/O error detected: " + exception.ToString() );
if ( _ioErrorReported )
{
return;
}
_ioErrorReported = true;
if ( IOErrorDetected != null )
{
IOErrorDetected( this, new ThreadExceptionEventArgs( exception ) );
}
}
// -- property type operations ---------------------------------------------
public int GetPropId( string name )
{
if ( name == null )
throw new ArgumentNullException( "name" );
return _propTypes [name].Id;
}
internal PropDataType GetPropDataType( int propId )
{
if ( propId == ResourceProps.Id )
return PropDataType.Int;
return _propTypes [propId].DataType;
}
internal string GetPropName( int propID )
{
return _propTypes [propID].Name;
}
internal bool IsLinkDirected( int propID )
{
return _propTypes [propID].HasFlag( PropTypeFlags.DirectedLink );
}
// -- resource type operations ---------------------------------------
/**
* Returns the display name mask of the resource type with the specified ID.
*/
internal DisplayNameMask GetResourceTypeDisplayNameMask( int ID )
{
return (_resourceTypes [ID] as ResourceTypeItem).DisplayNameTemplate;
}
// -- resource operations --------------------------------------------------
public IResource NewResource( string type )
{
if ( type == null )
throw new ArgumentNullException( "type" );
ResourceTypeItem resourceType = (ResourceTypeItem) _resourceTypes [type];
if ( _initializationComplete )
{
ResourceList resourcesOfType = resourceType.ResourcesOfType;
lock( resourcesOfType )
{
if ( resourcesOfType._list == null )
{
resourcesOfType.Instantiate( false );
}
}
}
int typeID = resourceType.Id;
Resource res;
lock( this )
{
int resID = CreateResource( typeID );
res = new Resource( resID, typeID, true );
_resourceCache.CacheObject( res.Id, res );
}
OnResourceSaved( res, new MultiPropChangeSet( true ) );
return res;
}
public IResource BeginNewResource( string type )
{
return BeginNewResource( type, false );
}
public IResource NewResourceTransient( string type )
{
return BeginNewResource( type, true );
}
private IResource BeginNewResource( string type, bool transient )
{
if ( type == null )
throw new ArgumentNullException( "type" );
ResourceTypeItem resourceType = (ResourceTypeItem) _resourceTypes [type];
if ( _initializationComplete )
{
ResourceList resourcesOfType = resourceType.ResourcesOfType;
lock( resourcesOfType )
{
if ( resourcesOfType._list == null )
{
resourcesOfType.Instantiate( false );
}
}
}
int typeID = resourceType.Id;
Resource res;
lock( this )
{
int resID;
if ( transient )
{
resID = _resources.NextID();
Debug.WriteLine( "Created transient resource with ID " + resID );
}
else
{
resID = CreateResource( typeID );
}
res = new Resource( resID, typeID, true );
if ( !transient )
{
_resourceCache.CacheObject( res.Id, res );
}
else
{
res.SetTransient();
_transientResources [res.Id] = res;
}
lock( _updatingResources )
{
_updatingResources [res.Id] = new MultiPropChangeSet( true );
}
}
return res;
}
public IResource LoadResource( int ID )
{
return LoadResource( ID, false, -1 );
}
internal IResource LoadResource( int id, bool allowDeleted, int knownTypeId )
{
Resource res;
lock( this )
{
res = LoadResourceFromCache( id, allowDeleted );
if ( res != null && knownTypeId >= 0 && res.TypeId != knownTypeId )
{
OnIndexCorruptionDetected( "LoadResource: type of resource loaded from cache (" + res.Type +
") does not match known type of resource list (" + ResourceTypes [knownTypeId].Name + ")" );
}
if ( res == null && knownTypeId >= 0 )
{
res = new Resource( id, knownTypeId, false );
_resourceCache.CacheObject( id, res );
return res;
}
if ( res == null )
{
knownTypeId = LoadResourceTypeFromCachedPredicates( id, true );
res = _resourceCache.TryKey( id ) as Resource;
if ( res == null )
{
res = new Resource( id, knownTypeId, false );
_resourceCache.CacheObject( id, res );
}
}
}
return res;
}
public IResource TryLoadResource( int id )
{
lock( this )
{
Resource res = LoadResourceFromCache( id, true );
if ( res == null )
{
int resType = LoadResourceTypeFromCachedPredicates( id, false );
if ( resType != -1 )
{
res = new Resource( id, resType, false );
_resourceCache.CacheObject( id, res );
}
}
else if ( res.IsDeleted )
{
return null;
}
return res;
}
}
internal Resource LoadResourceFromCache( int ID, bool allowDeleted )
{
Resource res = _resourceCache.TryKey( ID ) as Resource;
if ( res == null )
{
res = _resourceWeakCache [ID] as Resource;
if ( res == null )
{
res = _transientResources [ID] as Resource;
}
else if ( res.Id == -1 )
{
if( !allowDeleted )
{
throw new ResourceDeletedException( ID, res.Type );
}
}
}
else if ( res.Id == -1 && !allowDeleted )
{
throw new ResourceDeletedException( ID, res.Type );
}
return res;
}
private int LoadResourceTypeFromCachedPredicates( int id, bool throwException )
{
if ( !_initializationComplete )
{
return LoadResourceType( id, throwException );
}
foreach( ResourceTypeItem item in _loadedResourceTypes )
{
ResourceList resList = item.ResourcesOfType;
lock( resList )
{
if ( resList._list.BinarySearch( id ) >= 0 )
{
_loadedResourceTypes.TryKey( item.Id );
return item.Id;
}
}
}
if ( throwException )
{
// this will throw the InvalidResourceIdException
int resourceType = LoadResourceType( id, true );
if ( resourceType >= 0 )
{
// GetItemSafe() necessary here - see OM-11741
IResourceType resType = _resourceTypes.GetItemSafe( resourceType );
if ( resType != null )
{
throw new StorageException( "LoadResourceType mismatch: resource ID=" + id +
" of type " + resType.Name + " was not found in cached predicates but found on disk" );
}
}
}
return -1;
}
private void _resourceCache_ObjectRemoved( object sender, IntObjectCacheEventArgs e )
{
Resource resource = e.Object as Resource;
if ( resource == null )
{
if ( e.Object == null )
{
return;
}
throw new Exception( "Object of invalid type found in resource cache: "+ e.Object.GetType().Name );
}
_resourceWeakCache [ e.Key ] = resource;
resource.InvalidateDisplayName();
}
internal Resource GetResourceFromCache( int ID )
{
lock( this )
{
Resource res = _resourceCache.TryKey( ID ) as Resource;
if ( res == null )
res = _resourceWeakCache [ID] as Resource;
return res;
}
}
public int GetResourceWeakCacheCount()
{
lock( this )
{
_resourceWeakCache.Compact();
return _resourceWeakCache.Count;
}
}
public int GetUpdatingResourceCount()
{
lock( _updatingResources )
{
return _updatingResources.Count;
}
}
public IEnumerable ResourceCache
{
get { return _resourceCache; }
}
public IEnumerable ResourceWeakCache
{
get { return _resourceWeakCache; }
}
internal int CreateResource( int typeID )
{
CheckOwnerThread();
lock( _resources )
{
IRecord resource = _resources.NewRecord();
resource.SetValue( 1, IntInternalizer.Intern( typeID ) );
SafeCommitRecord( resource, "MyPalStorage.CreateResource" );
return resource.GetID();
}
}
internal void ChangeResourceType( int ID, int typeID )
{
CheckOwnerThread();
lock( _resources )
{
using( ICountedResultSet rs = _resources.CreateModifiableResultSet( 0, ID ) )
{
using( SafeRecordEnumerator enumerator = new SafeRecordEnumerator( rs, "MyPalStorage.ChangeResourceType" ) )
{
if( !enumerator.MoveNext() )
{
throw new InvalidResourceIdException ( ID, "Attempt to change type for an invalid resource " + ID );
}
IRecord rec = enumerator.Current;
if( enumerator.MoveNext() )
{
OnIndexCorruptionDetected( "Duplicate resource ID in MyPalStorage.ChangeResourceType" );
}
rec.SetValue( 1, IntInternalizer.Intern( typeID ) );
SafeCommitRecord( rec, "MyPalStorage.ChangeResourceType" );
}
}
}
}
internal void CommitTransientResource( Resource res )
{
CheckOwnerThread();
lock( this )
{
_transientResources.Remove( res.Id );
_resourceCache.CacheObject( res.Id, res );
}
lock( _resources )
{
IRecord resource = _resources.NewRecord();
resource.SetValue( 0, IntInternalizer.Intern( res.Id ) );
resource.SetValue( 1, IntInternalizer.Intern( res.TypeId ) );
SafeCommitRecord( resource, "MyPalStorage.CommitTransienResource" );
}
}
internal void CleanTransientResource( Resource resource )
{
lock( this )
{
_transientResources.Remove( resource.Id );
lock( _updatingResources )
{
_updatingResources.Remove( resource.Id );
}
}
}
public void CompactTransientResources()
{
lock( this )
{
_transientResources.Compact();
}
}
private void HandleTransientResourceDead( int id )
{
lock( _updatingResources )
{
_updatingResources.Remove( id );
}
}
internal void CheckEndUpdate( IResource res )
{
while( true )
{
bool isUpdating;
lock( _updatingResources )
{
isUpdating = _updatingResources.Contains( res.Id );
}
if ( !isUpdating )
break;
EndUpdateResource( res );
}
}
internal void OnResourceDeleting( IResource res )
{
if ( _trace )
{
Trace.WriteLine( "OnResourceDeleting: " + res.Id );
}
_updateManager.NotifyResourceDeleting( res );
if ( ResourceDeleting != null )
{
ResourceDeleting( res, EventArgs.Empty );
}
}
internal void DeleteResource( IResource res )
{
CheckOwnerThread();
if ( res.Id >= 0 )
{
ResourceRestrictions.CheckResourceDelete( res );
DeleteAllProperties( res.Id );
DeleteAllLinks( res.Id );
if ( _textIndexManager != null )
{
if ( !_resourceTypes [res.Type].HasFlag( ResourceTypeFlags.NoIndex ) )
{
_textIndexManager.DeleteDocumentQueued( res.Id );
}
}
}
using( ICountedResultSet rs = _resources.CreateModifiableResultSet( 0, res.Id ) )
{
using( SafeRecordEnumerator enumerator = new SafeRecordEnumerator( rs, "MyPalStorage.DeleteResource" ) )
{
if( !enumerator.MoveNext() )
{
throw new InvalidResourceIdException ( res.Id, "Attempt to delete an invalid resource " + res.Id );
}
IRecord rec = enumerator.Current;
if( enumerator.MoveNext() )
{
OnIndexCorruptionDetected( "Duplicate resource ID in MyPalStorage.DeleteResource" );
}
SafeDeleteRecord( rec, "MyPalStorage.DeleteResource" );
}
}
}
internal int LoadResourceType( int resId, bool throwException )
{
using( IResultSet rs = _resources.CreateResultSet( 0, resId ) )
{
using( SafeRecordValueEnumerator enumerator = new SafeRecordValueEnumerator( rs, "MyPalStorage.LoadResourceType" ) )
{
if ( !enumerator.MoveNext() )
{
if ( throwException )
throw new InvalidResourceIdException( resId );
return -1;
}
int result = enumerator.GetCurrentIntValue( 1 );
if ( enumerator.MoveNext() )
{
OnIndexCorruptionDetected( "Multiple entries in Resources table found for resource " + resId );
}
return result;
}
}
}
/**
* If a resource is found in the cache, returns the cached resource type;
* otherwise, loads the type from disk.
*/
public int GetResourceType( int resID )
{
Resource res = _resourceCache.TryKey( resID ) as Resource;
if ( res != null )
return res.TypeId;
return LoadResourceType( resID, true );
}
///
/// Commits the specified record and reports possible IOException and BadIndexesException to the
/// ResourceStore client.
///
/// The record to delete.
internal void SafeCommitRecord( IRecord rec, string reason )
{
try
{
rec.Commit();
}
catch( IOException ex )
{
OnIOErrorDetected( ex );
}
catch( BadIndexesException )
{
OnIndexCorruptionDetected( reason );
}
}
///
/// Deletes the specified record and reports possible IOException and BadIndexesException to the
/// ResourceStore client.
///
/// The record to delete.
internal void SafeDeleteRecord( IRecord rec, string reason )
{
try
{
rec.Delete();
}
catch( IOException ex )
{
OnIOErrorDetected( ex );
}
catch( BadIndexesException )
{
OnIndexCorruptionDetected( reason );
}
}
/**
* Creates a boolean property (true value assumed) for the specified resource
* and property type.
*/
internal void CreateBoolProperty( Resource res, int propID )
{
CheckOwnerThread();
lock( _boolProps )
{
IRecord prop = _boolProps.NewRecord();
prop.SetValue( 0, IntInternalizer.Intern( res.Id ) );
prop.SetValue( 1, IntInternalizer.Intern( propID ) );
SafeCommitRecord( prop, "MyPalStorage.CreateBoolProperty" );
}
}
/**
* Deletes (or sets to false, which is the same) a boolean property for
* the specified resource and property type.
*/
internal bool DeleteBoolProperty( Resource res, int propID )
{
CheckOwnerThread();
using( IResultSet rs = GetBoolProperties( res.Id ) )
{
using( SafeRecordEnumerator enumerator = new SafeRecordEnumerator( rs, "MyPalStorage.DeleteBoolProperty" ) )
{
while( enumerator.MoveNext() )
{
IRecord rec = enumerator.Current;
if ( rec.GetIntValue( 1 ) == propID )
{
SafeDeleteRecord( rec, "MyPalStorage.DeleteBoolProperty" );
return true;
}
}
}
}
return false;
}
internal void CreateProperty( Resource res, int propID, object propValue )
{
CheckOwnerThread();
if ( _ioErrorReported )
{
return;
}
ITable propTable = GetPropTable( propID );
lock( propTable )
{
IRecord property = propTable.NewRecord();
property.SetValue( 0, IntInternalizer.Intern( res.Id ) );
property.SetValue( 1, IntInternalizer.Intern( propID ) );
if ( propValue is Stream )
{
try
{
propValue = propTable.CreateBLOB( (Stream)propValue );
}
catch( IOException ex )
{
OnIOErrorDetected( ex );
return;
}
catch( UnauthorizedAccessException ex )
{
OnIOErrorDetected( ex );
return;
}
}
property.SetValue( 2, propValue );
SafeCommitRecord( property, "MyPalStorage.CreateProperty" );
}
if ( res.Type == "ResourceType" && _initializationComplete )
{
_resourceTypes.UpdateResourceTypeFromResource( res );
}
}
internal void UpdateProperty( Resource res, int propId, object propValue )
{
CheckOwnerThread();
ITable propTable = GetPropTable( propId );
using( ICountedResultSet rs = propTable.CreateResultSet( 0, res.Id, 1, propId, false ) )
{
int count = 0;
SafeRecordEnumerator enumerator = new SafeRecordEnumerator( rs, "MyPalStorage.UpdateProperty" );
using( enumerator )
{
while( enumerator.MoveNext() )
{
IRecord rec = enumerator.Current;
if( count == 0 )
{
if ( propValue is Stream )
{
try
{
rec.GetBLOBValue( 2 ).Set( (Stream)propValue );
}
catch( IOException ex )
{
OnIOErrorDetected( ex );
return;
}
catch( UnauthorizedAccessException ex )
{
OnIOErrorDetected( ex );
return;
}
}
else
{
rec.SetValue( 2, propValue );
SafeCommitRecord( rec, "MyPalStorage.UpdateProperty" );
}
}
else
{
SafeDeleteRecord( rec, "MyPalStorage.UpdateProperty" );
}
++count;
}
}
if( count == 0 && !enumerator.IOError )
{
OnIndexCorruptionDetected( "Attempt to update a non-existing property " + propId +
" on resource " + res.Id );
}
if( count > 1 )
{
OnIndexCorruptionDetected( count + " properties with the same resource ID " +
res.Id + " and property type " + GetPropName( propId ) + " found" );
}
}
if ( _initializationComplete )
{
if ( res.Type == "ResourceType" )
{
_resourceTypes.UpdateResourceTypeFromResource( res );
}
else if ( res.Type == "PropType" )
{
_propTypes.UpdatePropType( res );
}
}
}
/**
* Checks if the type of propValue matches the type of the property,
* and returns the table where the property is stored.
*/
internal ITable GetPropTable( int propID )
{
PropDataType propType = GetPropDataType( propID );
return GetPropTable( propType );
}
internal ITable GetPropTable( PropDataType propType )
{
switch( propType )
{
case PropDataType.String:
return _stringProps;
case PropDataType.LongString:
return _longStringProps;
case PropDataType.Int:
return _intProps;
case PropDataType.Date:
return _dateProps;
case PropDataType.Double:
return _doubleProps;
case PropDataType.Blob:
return _blobProps;
case PropDataType.StringList:
return _stringListProps;
default:
throw new StorageException( "Invalid property data type " + propType );
}
}
public IRecord LoadPropertyRecord( PropDataType propType, int offset )
{
ITable table = GetPropTable( propType );
Monitor.Enter( table );
try
{
return table.GetRecord( offset );
}
finally
{
Monitor.Exit( table );
}
}
/**
* Checks that the type of the value passed to SetProp matches the
* type of the property.
*/
internal void CheckValueType( int propID, PropDataType propType, object propValue )
{
bool mismatch = false;
Type expectedType = null;
switch( propType )
{
case PropDataType.Int:
if ( !(propValue is Int32) )
{
mismatch = true;
expectedType = typeof(Int32);
}
break;
case PropDataType.String:
case PropDataType.LongString:
case PropDataType.StringList:
if ( !(propValue is String) )
{
mismatch = true;
expectedType = typeof(String);
}
break;
case PropDataType.Date:
if ( !(propValue is DateTime) )
{
mismatch = true;
expectedType = typeof(DateTime);
}
break;
case PropDataType.Double:
if ( !(propValue is Double ) )
{
mismatch = true;
expectedType = typeof(Double);
}
break;
case PropDataType.Blob:
if ( !(propValue is Stream ) && !(propValue is String) )
{
mismatch = true;
expectedType = typeof(Stream);
}
break;
case PropDataType.Link:
if ( !(propValue is IResource ) )
{
mismatch = true;
expectedType = typeof(IResource);
}
break;
case PropDataType.Bool:
if ( !(propValue is Boolean ) )
{
mismatch = true;
expectedType = typeof(Boolean);
}
break;
default:
throw new StorageException( "Invalid property type " + propType );
}
if ( mismatch )
{
throw new StorageException( "Value type mismatch for property " + GetPropName( propID ) +
": expected " + expectedType.ToString() + ", actual " + propValue.GetType().ToString() );
}
}
internal void DeleteProperty( Resource res, int propID )
{
CheckOwnerThread();
ITable propTable = GetPropTable( propID );
using( ICountedResultSet rs = propTable.CreateResultSet( 0, res.Id, 1, propID, false ) )
{
DeleteAllRecords( rs, "MyPalStorage.DeleteProperty" );
}
}
private void DeleteAllRecords( IResultSet rs, string operation )
{
using( SafeRecordEnumerator enumerator = new SafeRecordEnumerator( rs, operation ) )
{
while( enumerator.MoveNext() )
{
SafeDeleteRecord( enumerator.Current, operation );
}
}
}
internal void DeleteProperty( Resource res, int propID, int index )
{
CheckOwnerThread();
ITable propTable = GetPropTable( propID );
using( IResultSet rs = propTable.CreateResultSet( 0, res.Id, 1, propID, true ) )
{
using( SafeRecordEnumerator enumerator = new SafeRecordEnumerator( rs, "MyPalStorage.DeleteProperty" ) )
{
for( int i=0; i<=index; i++ )
{
if ( !enumerator.MoveNext() )
{
return;
}
}
SafeDeleteRecord( enumerator.Current, "MyPalStorage.DeleteProperty" );
}
}
}
internal void DeleteAllProperties( int resID )
{
foreach( ITable table in _propTables )
{
DeleteAllPropertiesFromTable( resID, table );
}
DeleteAllPropertiesFromTable( resID, _boolProps );
}
private void DeleteAllPropertiesFromTable( int resID, ITable propTable )
{
IResultSet rs = propTable.CreateModifiableResultSet( 0, resID );
using( rs )
{
DeleteAllRecords( rs, "MyPalStorage.DeleteAllPropertiesFromTable" );
}
}
internal void OnResourceSaved( IResource resource, IPropertyChangeSet changeSet )
{
Trace.WriteLineIf( _trace, "OnResourceSaved: " + resource.Id );
ResourceRestrictions.CheckResource( resource, changeSet );
_updateManager.NotifyResourceSaved( resource, changeSet );
if ( ResourceSaved != null )
{
ResourcePropEventArgs args = new ResourcePropEventArgs( resource, changeSet );
ResourceSaved( this, args );
}
}
internal void OnResourceSaved( Resource resource, int propID, object oldValue )
{
DisplayNameMask mask = GetResourceTypeDisplayNameMask( resource.TypeId );
bool maskAffected = mask.DependsOnProperty( propID );
if ( propID == Props.DisplayName )
{
maskAffected = true;
}
if ( maskAffected )
{
resource.InvalidateDisplayName();
}
bool isNewResource = false;
MultiPropChangeSet cs;
lock( _updatingResources )
{
cs = (MultiPropChangeSet) _updatingResources [resource.Id];
}
if ( cs != null )
{
cs.AddChangedProp( propID, oldValue );
if ( maskAffected )
cs.SetDisplayNameAffected();
}
else
{
Trace.WriteLineIf( _trace, "OnResourceSaved: " + resource.Id );
SinglePropChangeSet changeSet = new SinglePropChangeSet( propID, oldValue, isNewResource, maskAffected );
OnResourceSaved( resource, changeSet );
}
}
internal void BeginUpdateResource( IResource resource )
{
lock( _updatingResources )
{
MultiPropChangeSet changeSet = (MultiPropChangeSet) _updatingResources [resource.Id];
if ( changeSet == null )
_updatingResources [resource.Id] = new MultiPropChangeSet( false );
else
changeSet.BeginUpdate();
}
}
internal int GetResourceUpdateCount( IResource resource )
{
lock( _updatingResources )
{
MultiPropChangeSet changeSet = (MultiPropChangeSet) _updatingResources [resource.Id];
if ( changeSet == null )
return 0;
return changeSet.GetUpdateCounter();
}
}
internal int EndUpdateResource( IResource resource )
{
MultiPropChangeSet changeSet;
int newCount;
lock( _updatingResources )
{
changeSet = _updatingResources [resource.Id] as MultiPropChangeSet;
if ( changeSet == null )
throw new StorageException( "IResource.EndUpdate() called without BeginUpdate()" );
newCount = changeSet.EndUpdate();
if ( newCount == 0 )
{
_updatingResources.Remove( resource.Id );
}
}
if ( newCount == 0 && ( changeSet.IsNewResource || !changeSet.IsEmpty() ) )
{
OnResourceSaved( resource, changeSet );
}
return newCount;
}
internal bool IsResourceChanged( Resource resource )
{
lock( _updatingResources )
{
MultiPropChangeSet changeSet = _updatingResources [resource.Id] as MultiPropChangeSet;
if ( changeSet == null )
throw new StorageException( "IResource.IsChanged() can only be called between BeginUpdate() and EndUpdate()" );
return !changeSet.IsEmpty();
}
}
/**
* Fires the ResourceSaved event that is caused by a link change.
*/
private void OnLinkResourceSaved( Resource from, Resource target, int propID, LinkChangeType changeType )
{
DisplayNameMask mask = GetResourceTypeDisplayNameMask( from.TypeId );
bool maskAffected = mask.DependsOnProperty( ( propID < 0 ) ? -propID : propID );
if ( maskAffected )
{
from.InvalidateDisplayName();
}
MultiPropChangeSet cs;
lock( _updatingResources )
{
cs = _updatingResources [from.Id] as MultiPropChangeSet;
}
if ( cs != null )
{
cs.AddChangedLink( propID, target.Id, changeType );
if ( maskAffected )
cs.SetDisplayNameAffected();
}
else if ( !from.IsDeleting )
{
OnResourceSaved( from, new SinglePropChangeSet( propID, target.Id, changeType, maskAffected ) );
}
}
internal void OnLinkAdded( Resource from, Resource target, int propID )
{
OnLinkResourceSaved( from, target, propID, LinkChangeType.Add );
if ( LinkAdded != null )
{
LinkAdded( this, new LinkEventArgs( from, target, propID ) );
}
}
internal void OnLinkDeleted( Resource from, Resource target, int propID )
{
if ( !from.IsDeleting )
{
OnLinkResourceSaved( from, target, propID, LinkChangeType.Delete );
}
if ( LinkDeleted != null )
{
LinkDeleted( this, new LinkEventArgs( from, target, propID ) );
}
}
// public for unit tests
public ICollection GetAllProperties( int resID )
{
ArrayList rsList = new ArrayList();
foreach( ITable table in _propTables )
{
rsList.Add( table.CreateModifiableResultSet( 0, resID ) );
}
return rsList;
}
internal IResultSet GetProperties( int resID, PropDataType propType )
{
ITable table = GetPropTable( propType );
return table.CreateResultSet( 0, resID );
}
/**
* Returns a result set containing all boolean properties of the specified resource.
*/
// public for unit tests
public IResultSet GetBoolProperties( int resID )
{
return _boolProps.CreateResultSet( 0, IntInternalizer.Intern( resID ) );
}
public IResultSet GetStringListProperties( int resID )
{
return _stringListProps.CreateResultSet( 0, IntInternalizer.Intern( resID ) );
}
public IResultSet GetStringListProperties( int resID, int propID )
{
return _stringListProps.CreateResultSet(
0, IntInternalizer.Intern( resID ), 1, IntInternalizer.Intern( propID ), true );
}
internal IBLOB GetBlobProperty( int resID, int propID )
{
using( IResultSet rs = _blobProps.CreateResultSet( 0, IntInternalizer.Intern( resID ), 1, IntInternalizer.Intern( propID ), true ) )
{
using( SafeRecordEnumerator enumerator = new SafeRecordEnumerator( rs, "MyPalStorage.GetBlobProperty" ) )
{
if ( !enumerator.MoveNext() )
return null;
return enumerator.Current.GetBLOBValue( 2 );
}
}
}
// -- link operations ------------------------------------------------
internal void SaveLink( int resID, int resID2, int type )
{
CheckOwnerThread();
lock( _links )
{
IRecord link = _links.NewRecord();
link.SetValue( 0, IntInternalizer.Intern( resID ) );
link.SetValue( 1, IntInternalizer.Intern( resID2 ) );
link.SetValue( 2, IntInternalizer.Intern( type ) );
SafeCommitRecord( link, "MyPalStorage.SaveLink" );
}
}
internal void DeleteLink( Resource res, Resource res2, int propType )
{
CheckOwnerThread();
using( IResultSet rs = _links.CreateResultSet( 0, IntInternalizer.Intern( res.Id ), 2, IntInternalizer.Intern( propType ), true ) )
{
using( SafeRecordEnumerator enumerator = new SafeRecordEnumerator( rs, "MyPalStorage.DeleteLink" ) )
{
while( enumerator.MoveNext() )
{
IRecord rec = enumerator.Current;
if ( rec.GetIntValue( 1 ) == res2.Id )
{
SafeDeleteRecord( rec, "MyPalStorage.DeleteLink" );
break;
}
}
}
}
OnLinkDeleted( res, res2, propType );
}
internal void DeleteAllLinks( int resID )
{
DeleteAllLinks( resID, 0 );
DeleteAllLinks( resID, 1 );
}
private void DeleteAllLinks( int resID, int columnIndex )
{
using( IResultSet rs = _links.CreateModifiableResultSet( columnIndex, IntInternalizer.Intern( resID ) ) )
{
DeleteAllRecords( rs, "MyPalStorage.DeleteAllLinks" );
}
}
internal IResultSet GetLinksFrom( int resId )
{
return _links.CreateResultSet( 0, IntInternalizer.Intern( resId ) );
}
internal IResultSet GetLinksTo( int resId )
{
return _links.CreateResultSet( 1, IntInternalizer.Intern( resId ) );
}
internal IResultSet GetLinksFrom( int resId, int propType )
{
return _links.CreateResultSet(
0, IntInternalizer.Intern( resId ), 2, IntInternalizer.Intern( propType ), true );
}
internal IResultSet GetLinksTo( int resId, int propType )
{
return _links.CreateResultSet(
1, IntInternalizer.Intern( resId ), 2, IntInternalizer.Intern( propType ), true );
}
internal IResultSet SelectLinksOfType( int propID )
{
return _links.CreateResultSet( 2, IntInternalizer.Intern( propID ) );
}
public bool LinkExists( int resID, int resID2, int type )
{
using( IResultSet rs = _links.CreateResultSet( 0, IntInternalizer.Intern( resID ), 2, IntInternalizer.Intern( type ), true ) )
{
foreach( IRecord rec in rs )
{
if ( rec.GetIntValue( 1 ) == resID2 )
{
return true;
}
}
}
return false;
}
/**
* Deletes all properties of the specified type from the database and
* cached resources.
*/
internal void DeletePropsOfType( int propType )
{
PropDataType dataType = PropTypes [propType].DataType;
IResultSet rs;
if ( dataType == PropDataType.LongString || dataType == PropDataType.Blob )
{
rs = GetPropTable( dataType ).CreateResultSet( 0 );
}
else
{
rs = SelectResourcesWithProp( propType );
}
List ids = new List();
using( rs )
{
using( SafeRecordEnumerator enumerator = new SafeRecordEnumerator( rs, "MyPalStorage.DeletePropsOfType()" ) )
{
while( enumerator.MoveNext() )
{
IRecord rec = enumerator.Current;
if ( rec.GetIntValue( 1 ) == propType )
{
ids.Add(rec.GetIntValue( 0 ));
/*
IResource res = GetResourceFromCache( id );
if ( res != null )
{
res.DeleteProp( propType );
}
else
{
SafeDeleteRecord( rec, "MyPalStorage.DeletePropsOfType()" );
}
*/
}
}
}
}
foreach(var id in ids)
{
IResource res = TryLoadResource(id);
if(res != null)
res.DeleteProp(propType);
}
}
/**
* Deletes all links of the specified type from the database and
* cached resources.
*/
internal void DeleteLinksOfType( int propType )
{
using( IResultSet rs = SelectLinksOfType( propType ) )
{
using( SafeRecordEnumerator enumerator = new SafeRecordEnumerator( rs, "MyPalStorage.DeleteLinksOfType" ) )
{
while( enumerator.MoveNext() )
{
IRecord rec = enumerator.Current;
int id1 = rec.GetIntValue( 0 );
int id2 = rec.GetIntValue( 1 );
IResource res1 = GetResourceFromCache( id1 );
IResource res2 = GetResourceFromCache( id2 );
if ( res1 != null || res2 != null )
{
if ( res1 == null )
res1 = LoadResource( id1 );
if ( res2 == null )
res2 = LoadResource( id2 );
res1.DeleteLink( propType, res2 );
}
else
{
SafeDeleteRecord( rec, "MyPalStorage.DeleteLinksOfType" );
}
}
}
}
}
public void RegisterLinkRestriction(string fromResourceType, int linkType,
string toResourceType, int minCount, int maxCount)
{
ResourceRestrictions.RegisterLinkRestriction(fromResourceType, linkType, toResourceType, minCount, maxCount);
}
public void RegisterLinkRestriction(string fromResourceType, PropId linkType, string toResourceType,
int minCount, int maxCount)
{
ResourceRestrictions.RegisterLinkRestriction(fromResourceType, linkType.Id, toResourceType,
minCount, maxCount);
}
public int GetMinLinkCountRestriction( string fromResourceType, int linkType )
{
return ResourceRestrictions.GetMinLinkCountRestriction( fromResourceType, linkType );
}
public int GetMaxLinkCountRestriction( string fromResourceType, int linkType )
{
return ResourceRestrictions.GetMaxLinkCountRestriction( fromResourceType, linkType );
}
public string GetLinkResourceTypeRestriction( string fromResourceType, int linkType )
{
return ResourceRestrictions.GetLinkResourceTypeRestriction( fromResourceType, linkType );
}
public void RegisterUniqueRestriction( string resourceType, int propId )
{
ResourceRestrictions.RegisterUniqueRestriction( resourceType, propId );
}
public void DeleteUniqueRestriction( string resourceType, int propId )
{
ResourceRestrictions.DeleteUniqueRestriction( resourceType, propId );
}
public void RegisterCustomRestriction( string resourceType, int propId, IResourceRestriction restriction )
{
ResourceRestrictions.RegisterCustomRestriction( resourceType, propId, restriction );
}
public void DeleteCustomRestriction( string resourceType, int propId )
{
ResourceRestrictions.DeleteCustomRestriction( resourceType, propId );
}
public void RegisterRestrictionOnDelete( string resourceType, IResourceRestriction restriction )
{
ResourceRestrictions.RegisterRestrictionOnDelete( resourceType, restriction );
}
public void DeleteRestrictionOnDelete( string resourceType )
{
ResourceRestrictions.DeleteRestrictionOnDelete( resourceType );
}
// -- resource select operations -------------------------------------------
public IResourceList ListFromIds( IntArrayList resourceIDs, bool live )
{
return new ResourceList( new PlainListPredicate( resourceIDs ), live );
}
public IResourceList ListFromIds( int[] resourceIDs, bool live )
{
return ListFromIds( new IntArrayList( resourceIDs ), live );
}
public IResourceList ListFromIds( ICollection resourceIDs, bool live )
{
IntArrayList list = resourceIDs as IntArrayList;
return ListFromIds( list ?? new IntArrayList( resourceIDs ), live );
}
public IResourceList FindResources( string resType, int propID, object propValue )
{
if ( propValue == null )
throw new ArgumentNullException( "propValue" );
return FindResourcesInRange( SelectionType.Normal, resType, propID, propValue, null );
}
public IResourceList FindResources( string resType, string propName, object propValue )
{
if ( propValue == null )
throw new ArgumentNullException( "propValue" );
return FindResourcesInRange( SelectionType.Normal, resType, GetPropId( propName ), propValue, null );
}
public IResourceList FindResources(string resType, PropId propId, T propValue)
{
return FindResources(resType, propId.Id, propValue);
}
public BusinessObjectList FindResources(ResourceTypeId resType, PropId propId, V propValue) where T : BusinessObject
{
return new BusinessObjectList(resType, FindResources(resType.Name, propId, propValue));
}
public IResourceList FindResourcesLive( string resType, int propID, object propValue )
{
if ( propValue == null )
throw new ArgumentNullException( "propValue" );
return FindResourcesInRange( SelectionType.Live, resType, propID, propValue, null );
}
public IResourceList FindResourcesLive( string resType, string propName, object propValue )
{
if ( propValue == null )
throw new ArgumentNullException( "propValue" );
return FindResourcesInRange( SelectionType.Live, resType, GetPropId( propName ), propValue, null );
}
public IResourceList FindResourcesLive(string resType, PropId propId, T propValue)
{
return FindResourcesLive(resType, propId.Id, propValue);
}
public IResourceList FindResources( SelectionType selType, string resType, int propID, object propValue )
{
if ( propValue == null )
throw new ArgumentNullException( "propValue" );
return FindResourcesInRange( selType, resType, propID, propValue, null );
}
public IResourceList FindResources( SelectionType selType, string resType, string propName, object propValue )
{
if ( propValue == null )
throw new ArgumentNullException( "propValue" );
return FindResourcesInRange( selType, resType, GetPropId( propName ), propValue, null );
}
public IResourceList FindResourcesInRange( string resType, int propID, object minValue, object maxValue )
{
return FindResourcesInRange( SelectionType.Normal, resType, propID, minValue, maxValue );
}
public IResourceList FindResourcesInRange( string resType, string propName, object minValue, object maxValue )
{
return FindResourcesInRange( SelectionType.Normal, resType, GetPropId( propName ), minValue, maxValue );
}
public IResourceList FindResourcesInRangeLive( string resType, int propID, object minValue, object maxValue )
{
return FindResourcesInRange( SelectionType.Live, resType, propID, minValue, maxValue );
}
public IResourceList FindResourcesInRangeLive( string resType, string propName, object minValue, object maxValue )
{
return FindResourcesInRange( SelectionType.Live, resType, GetPropId( propName ), minValue, maxValue );
}
public IResourceList FindResourcesInRange( SelectionType listType, string resType, string propName,
object minValue, object maxValue )
{
return FindResourcesInRange( listType, resType, GetPropId( propName ), minValue, maxValue );
}
///
/// Returns the list of resources for which the property with the specified name has a value in the specified range.
///
/// Specifies the update notification mode of a resource list.
/// The type of resources to return, or null if resources of any type should be returned.
/// The ID of the property for which the selection is done.
/// The minimum matching value of the property, must be non-null.
/// The maximum matching value of the property, may be null, works as exact search in this case.
/// The list of resources, or an empty resource list if no resources match the condition.
///
/// Range selection is only supported for int and date properties.
///
public IResourceList FindResourcesInRange( SelectionType listType, string resType, int propID,
object minValue, object maxValue )
{
bool isSnapshot = (listType == SelectionType.LiveSnapshot );
bool isLive = (listType == SelectionType.Live ||
listType == SelectionType.LiveSnapshot );
ResourceListPredicate pred = CreateSelectionPredicate( propID, minValue, maxValue, isSnapshot );
return IntersectPredicateWithType( pred, resType, isLive );
}
private ResourceListPredicate CreateSelectionPredicate( int propId, object minValue,
object maxValue, bool isSnapshot )
{
PropDataType propType = GetPropDataType( propId );
if ( propType == PropDataType.Bool )
{
bool boolValue = (bool) minValue;
if ( !boolValue )
{
String name = PropTypes[ propId ].Name;
throw new StorageException( "Selections by bool property are only supported for true value (propName=[" + name + "]" );
}
if ( maxValue != null)
{
throw new StorageException( "Range selections by bool property are not supported" );
}
return new ResourcesWithPropPredicate( propId, isSnapshot );
}
else if ( propType == PropDataType.Int || propType == PropDataType.String || propType == PropDataType.Date ||
propType == PropDataType.StringList )
{
CheckValueType( propId, propType, minValue );
if ( maxValue != null )
{
if ( propType == PropDataType.String || propType == PropDataType.StringList )
throw new StorageException( "Range selections on string and StringList properties are not supported" );
CheckValueType( propId, propType, maxValue );
if ( minValue != null && maxValue != null &&
((IComparable) minValue).CompareTo( maxValue ) > 0 )
{
return new PropValuePredicate( propId, maxValue, minValue, isSnapshot );
}
else
{
return new PropValuePredicate( propId, minValue, maxValue, isSnapshot );
}
}
else
return new PropValuePredicate( propId, minValue, null, isSnapshot );
}
else
{
throw new StorageException( "Selection on " + propType + " properties (" +
PropTypes [propId].Name + ") is not supported" );
}
}
public IResourceList FindResourcesWithProp( string resType, int propID )
{
return FindResourcesWithProp( SelectionType.Normal, resType, propID );
}
public IResourceList FindResourcesWithProp( string resType, string propName )
{
return FindResourcesWithProp( SelectionType.Normal, resType, GetPropId( propName ) );
}
public IResourceList FindResourcesWithProp(string resType, PropId propId)
{
return FindResourcesWithProp(resType, propId.Id);
}
public IResourceList FindResourcesWithPropLive( string resType, int propID )
{
return FindResourcesWithProp( SelectionType.Live, resType, propID );
}
public IResourceList FindResourcesWithPropLive( string resType, string propName )
{
return FindResourcesWithProp( SelectionType.Live, resType, GetPropId( propName ) );
}
public IResourceList FindResourcesWithPropLive(string resType, PropId propId)
{
return FindResourcesWithPropLive(resType, propId.Id);
}
public IResourceList FindResourcesWithProp( SelectionType selType, string resType, int propID )
{
bool isSnapshot = (selType == SelectionType.LiveSnapshot );
bool isLive = (selType == SelectionType.Live ||
selType == SelectionType.LiveSnapshot );
ResourceListPredicate pred;
PropDataType dataType = GetPropDataType( propID );
if ( dataType == PropDataType.LongString || dataType == PropDataType.Blob || dataType == PropDataType.Double )
{
throw new StorageException( "FindResourcesWithProp() is not supported for " + dataType + " properties (" +
PropTypes [propID].Name + ")" );
}
if ( dataType == PropDataType.Link )
{
pred = new ResourcesWithLinkPredicate( propID, isSnapshot );
}
else
{
pred = new ResourcesWithPropPredicate( propID, isSnapshot );
}
return IntersectPredicateWithType( pred, resType, isLive );
}
public IResourceList FindResourcesWithProp( SelectionType selType, string resType, string propName )
{
return FindResourcesWithProp( selType, resType, GetPropId( propName ) );
}
internal ResourceList IntersectPredicateWithType( ResourceListPredicate pred, string resType, bool live )
{
if ( resType != null )
{
ResourceListPredicate typePred = new ResourceTypePredicate( resType );
// NOTE: the order is important (the type predicate is faster to match, so we
// put it first)
pred = new IntersectionPredicate( typePred, pred );
}
return new ResourceList( pred, live );
}
public IResource FindUniqueResource( string resType, int propID, object propValue )
{
if ( propValue == null )
{
throw new ArgumentNullException( "propValue" );
}
object id;
_findUniqueResourceCacheLock.Enter();
try
{
id = _findUniqueResourceCache.TryKey( propValue );
}
finally
{
_findUniqueResourceCacheLock.Exit();
}
if( id != null )
{
IResource res = TryLoadResource( (int) id );
if( res != null && ( resType == null || res.Type == resType ) &&
res.HasProp( propID ) && propValue.Equals( res.GetProp( propID ) ) )
{
return res;
}
}
ResourceListPredicate pred = CreateSelectionPredicate( propID, propValue, null, false );
bool sortedById;
IntArrayList resIds = pred.GetMatchingResources( out sortedById );
if ( resIds.Count > 128 )
{
IntArrayList result = resIds;
if ( resType != null )
{
bool typeSortedById;
IntArrayList typeIds = new ResourceTypePredicate( resType ).GetMatchingResources( out typeSortedById );
if ( !typeSortedById )
{
typeIds.Sort();
}
if ( !sortedById )
{
resIds.Sort();
}
result.IntersectSorted( typeIds );
}
if ( result.Count > 1 )
{
if ( resType != null && ResourceRestrictions.UniqueRestrictionExists( resType, propID ) )
{
SetRepairRequired();
return LoadResource( result [0] );
}
throw new StorageException( "Multiple resources found where a unique resource was expected" );
}
if ( result.Count == 0 )
{
return null;
}
_findUniqueResourceCacheLock.Enter();
try
{
_findUniqueResourceCache.CacheObject( propValue, result [0] );
}
finally
{
_findUniqueResourceCacheLock.Exit();
}
return LoadResource( result [0] );
}
else
{
IResource result = null;
for( int i=0; i GetAllResources(ResourceTypeId resType) where T : BusinessObject
{
return new BusinessObjectList(resType, GetAllResources(resType.Name));
}
public IResourceList GetAllResourcesLive( string resType )
{
if ( resType == null )
throw new ArgumentNullException( "resType" );
return new ResourceList( new ResourceTypePredicate( resType ), true );
}
public IResourceList GetAllResources( string[] resTypes )
{
return GetMultiTypeResources( resTypes, false );
}
public IResourceList GetAllResourcesLive( string[] resTypes )
{
return GetMultiTypeResources( resTypes, true );
}
private IResourceList GetMultiTypeResources( string[] resTypes, bool live )
{
if ( resTypes == null )
throw new ArgumentNullException( "resTypes" );
if ( resTypes.Length == 0 )
{
return EmptyResourceList;
}
for( int i=0; i 100 ) percentage = 100;
_progressWindow.UpdateProgress( percentage, progress, null );
}
}
public void MarkHiddenResourceTypes( string[] loadedPluginNames )
{
MarkNotLoadedPlugins( "ResourceType", loadedPluginNames );
MarkNotLoadedPlugins( "PropType", loadedPluginNames );
}
internal void SetOwnerPlugin( IResource res, IPlugin ownerPlugin )
{
if ( ownerPlugin != null )
{
IStringList ownerPlugins = res.GetStringListProp( "OwnerPluginList" );
string ownerPluginName = ownerPlugin.GetType().FullName;
if ( ownerPlugins.IndexOf( ownerPluginName ) < 0 )
{
ownerPlugins.Add( ownerPluginName );
}
}
}
private void MarkNotLoadedPlugins( string resType, string[] loadedPluginNames )
{
foreach( IResource resTypeResource in FindResourcesWithProp( resType, "OwnerPluginList" ) )
{
IStringList ownerPlugins = resTypeResource.GetStringListProp( "OwnerPluginList" );
if ( ownerPlugins.Count == 0 )
{
return;
}
bool anyLoaded = false;
foreach( string ownerPlugin in ownerPlugins )
{
if ( Array.IndexOf( loadedPluginNames, ownerPlugin ) >= 0 )
{
anyLoaded = true;
break;
}
}
if ( !anyLoaded )
{
string name = resTypeResource.GetStringProp( "Name" );
if ( name == null ) // DB corruption recovery (OM-8930)
{
continue;
}
if ( resType == "ResourceType" && ResourceTypes.Exist( name ) )
{
((ResourceTypeItem)ResourceTypes [name]).SetOwnerPluginUnloaded();
}
else if ( resType == "PropType" && PropTypes.Exist( name ) )
{
((PropTypeItem)PropTypes [name]).SetOwnerPluginUnloaded();
}
}
}
}
public void RegisterDisplayNameProvider( IDisplayNameProvider provider )
{
Guard.NullArgument( provider, "provider" );
_displayNameProviders.Add( provider );
}
internal string CalcCustomDisplayName( IResource res )
{
foreach( IDisplayNameProvider provider in _displayNameProviders )
{
string displayName = provider.GetDisplayName( res );
if ( !string.IsNullOrEmpty(displayName) )
{
return displayName;
}
}
return "";
}
public class IndexRebuildException: Exception
{
public IndexRebuildException( string message, Exception innerException )
: base( message, innerException )
{
}
}
}
}