context: /* This file is part of the db4o object database http://www.db4o.com
  
  Copyright (C) 2004 - 2011  Versant Corporation http://www.versant.com
  
  db4o is free software; you can redistribute it and/or modify it under
  the terms of version 3 of the GNU General Public License as published
  by the Free Software Foundation.
  
  db4o is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  for more details.
  
  You should have received a copy of the GNU General Public License along
  with this program.  If not, see http://www.gnu.org/licenses/. */
  
  using System;
  using System.Collections;
  using Db4objects.Db4o.Config;
  using Db4objects.Db4o.Ext;
  using Db4objects.Db4o.Foundation;
  using Db4objects.Db4o.Foundation.IO;
  using Db4objects.Db4o.Internal;
  using Db4objects.Db4o.Query;
  using Sharpen;
  
  namespace Db4objects.Db4o.Tests.Common.Reflect.Custom
  {
      /// <summary>
      ///     Custom class information is stored to db4o itself as
      ///     a CustomClassRepository singleton.
      /// </summary>
      /// <remarks>
      ///     Custom class information is stored to db4o itself as
      ///     a CustomClassRepository singleton.
      /// </remarks>
      public class Db4oPersistenceProvider : IPersistenceProvider
      {
          public virtual void CreateEntryClass(PersistenceContext context, string className
              , string[] fieldNames, string[] fieldTypes)
          {
              LogMethodCall("createEntryClass", context, className);
              var repository = Repository(context);
              repository.DefineClass(className, fieldNames, fieldTypes);
              UpdateMetadata(context, repository);
          }
  
          public virtual void CreateIndex(PersistenceContext context, string className, string
              fieldName)
          {
              MarkIndexedField(context, className, fieldName, true);
          }
  
          public virtual void DropIndex(PersistenceContext context, string className, string
              fieldName)
          {
              MarkIndexedField(context, className, fieldName, false);
          }
  
          public virtual int Delete(PersistenceContext context, string className, object uid
              )
          {
              // TODO Auto-generated method stub
              return 0;
          }
  
          public virtual void DropEntryClass(PersistenceContext context, string className)
          {
          }
  
          // TODO Auto-generated method stub
          public virtual void InitContext(PersistenceContext context)
          {
              LogMethodCall("initContext", context);
              var metadata = OpenMetadata(context.Url());
              try
              {
                  var repository = InitializeClassRepository(metadata);
                  var reflector = new CustomReflector(repository);
                  var data = OpenData(reflector, context.Url());
                  context.SetProviderContext(new MyContext(repository, metadata
                      , data));
              }
              catch (Exception e)
              {
                  Runtime.PrintStackTrace(e);
                  // make sure metadata container is not left open
                  // in case something goes wrong with the setup
                  CloseIgnoringExceptions(metadata);
                  // cant use exception chaining here because the
                  // test must run in jdk 1.1
                  throw new Db4oException(e);
              }
          }
  
          public virtual void Insert(PersistenceContext context, PersistentEntry entry)
          {
              LogMethodCall("insert", context, entry);
              // clone the entry because clients are allowed to reuse
              // entry objects
              DataContainer(context).Store(Clone(entry));
          }
  
          public virtual IEnumerator Select(PersistenceContext context, PersistentEntryTemplate
              template)
          {
              LogMethodCall("select", context, template);
              var query = QueryFromTemplate(context, template);
              return new ObjectSetIterator(query.Execute());
          }
  
          public virtual void Update(PersistenceContext context, PersistentEntry entry)
          {
              var existing = SelectByUid(context, entry.className, entry.uid);
              existing.fieldValues = entry.fieldValues;
              DataContainer(context).Store(existing);
          }
  
          public virtual void CloseContext(PersistenceContext context)
          {
              LogMethodCall("closeContext", context);
              var customContext = My(context);
              if (null != customContext)
              {
                  CloseIgnoringExceptions(customContext.metadata);
                  CloseIgnoringExceptions(customContext.data);
                  context.SetProviderContext(null);
              }
          }
  
          public virtual void Purge(string url)
          {
              File4.Delete(url);
              File4.Delete(MetadataFile(url));
          }
  
          private void MarkIndexedField(PersistenceContext context, string className, string
              fieldName, bool indexed)
          {
              var field = CustomClass(context, className).CustomField(fieldName);
              field.Indexed(indexed);
              UpdateMetadata(context, field);
              Restart(context);
          }
  
          private void Restart(PersistenceContext context)
          {
              CloseContext(context);
              InitContext(context);
          }
  
          private void CloseIgnoringExceptions(IObjectContainer container)
          {
              try
              {
                  container.Close();
              }
              catch (Exception e)
              {
                  Runtime.PrintStackTrace(e);
              }
          }
  
          private PersistentEntry SelectByUid(PersistenceContext context, string className,
              object uid)
          {
              var query = NewQuery(context, className);
              query.Descend("uid").Constrain(uid);
              return (PersistentEntry) query.Execute().Next();
          }
  
          private void AddClassConstraint(PersistenceContext context, IQuery query, string
              className)
          {
              query.Constrain(CustomClass(context, className));
          }
  
          private CustomClass CustomClass(PersistenceContext context, string className)
          {
              return Repository(context).ForName(className);
          }
  
          private IConstraint AddFieldConstraint(IQuery query, PersistentEntryTemplate template
              , int index)
          {
              return query.Descend(template.fieldNames[index]).Constrain(template.fieldValues[index
                  ]);
          }
  
          private void AddFieldConstraints(IQuery query, PersistentEntryTemplate template)
          {
              if (template.fieldNames.Length == 0)
              {
                  return;
              }
              var c = AddFieldConstraint(query, template, 0);
              for (var i = 1; i < template.fieldNames.Length; ++i)
              {
                  c = c.And(AddFieldConstraint(query, template, i));
              }
          }
  
          private PersistentEntry Clone(PersistentEntry entry)
          {
              return new PersistentEntry(entry.className, entry.uid, entry.fieldValues);
          }
  
          private MyContext My(PersistenceContext context)
          {
              return ((MyContext) context.GetProviderContext());
          }
  
          private IConfiguration DataConfiguration(CustomReflector reflector)
          {
              var config = Db4oFactory.NewConfiguration();
              config.ReflectWith(reflector);
              ConfigureCustomClasses(config, reflector);
              return config;
          }
  
          private void ConfigureCustomClasses(IConfiguration config, CustomReflector reflector
              )
          {
              var classes = reflector.CustomClasses();
              while (classes.MoveNext())
              {
                  var cc = (CustomClass) classes.Current;
                  ConfigureFields(config, cc);
              }
          }
  
          private void ConfigureFields(IConfiguration config, CustomClass cc)
          {
              var fields = cc.CustomFields();
              while (fields.MoveNext())
              {
                  var field = (CustomField) fields.Current;
                  config.ObjectClass(cc).ObjectField(field.GetName()).Indexed(field.Indexed());
              }
          }
  
          public virtual IObjectContainer DataContainer(PersistenceContext context)
          {
              return My(context).data;
          }
  
          private CustomClassRepository InitializeClassRepository(IObjectContainer container
              )
          {
              var repository = QueryClassRepository(container);
              if (repository == null)
              {
                  Log("Initializing new class repository.");
                  repository = new CustomClassRepository();
                  Store(container, repository);
              }
              else
              {
                  Log("Found existing class repository: " + repository);
              }
              return repository;
          }
  
          private IConfiguration MetaConfiguration()
          {
              var config = Db4oFactory.NewConfiguration();
              config.ExceptionsOnNotStorable(true);
              // the following line is only necessary for the tests to run
              // in OSGi environment
              config.ReflectWith(Platform4.ReflectorForType(typeof (CustomClassRepository)));
              Cascade(config, typeof (CustomClassRepository));
              Cascade(config, typeof (Hashtable4));
              Cascade(config, typeof (CustomClass));
              // FIXME: [TA] this is necessary because the behavior
              // on .net differs with regards to cascade activation
              // remove the following two lines and run the test
              // on .net to see it fail
              Cascade(config, typeof (CustomField));
              Cascade(config, typeof (CustomUidField));
              return config;
          }
  
          private void Cascade(IConfiguration config, Type klass)
          {
              config.ObjectClass(klass).CascadeOnUpdate(true);
              config.ObjectClass(klass).CascadeOnActivate(true);
          }
  
          private IObjectContainer MetadataContainer(PersistenceContext context)
          {
              return My(context).metadata;
          }
  
          private string MetadataFile(string fname)
          {
              return fname + ".metadata";
          }
  
          private IObjectContainer OpenData(CustomReflector reflector, string fname)
          {
              return Db4oFactory.OpenFile(DataConfiguration(reflector), fname);
          }
  
          private IObjectContainer OpenMetadata(string fname)
          {
              return Db4oFactory.OpenFile(MetaConfiguration(), MetadataFile(fname));
          }
  
          private CustomClassRepository QueryClassRepository(IObjectContainer container)
          {
              var found = container.Query(typeof (CustomClassRepository));
              if (!found.HasNext())
              {
                  return null;
              }
              return (CustomClassRepository) found.Next();
          }
  
          private IQuery QueryFromTemplate(PersistenceContext context, PersistentEntryTemplate
              template)
          {
  
gt:             var query = NewQuery(context, template.className);