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.Foundation;
  using Db4objects.Db4o.Internal;
  using Db4objects.Db4o.Reflect;
  using Db4objects.Db4o.Reflect.Core;
  using Db4objects.Db4o.Reflect.Generic;
  using Sharpen;
  
  namespace Db4objects.Db4o.Qlin
  {
      /// <summary>creates prototype objects for classes.</summary>
      /// <remarks>
      ///     creates prototype objects for classes. Each field on prototype objects is set
      ///     to a newly created object or primitive that can be identified either by it's
      ///     identity or by an int ID that is generated by the system. Creation of fields
      ///     is recursed to the depth specified in the constructor.<br />
      ///     <br />
      ///     Allows analyzing expressions called on prototype objects to find the
      ///     underlying field that delivers the return value of the expression. Passed
      ///     expressions should not have side effects on objects, otherwise the
      ///     "prototype world" will no longer work.<br />
      ///     <br />
      ///     We plan to supply an ImmutableFieldClassLoader to instrument the code to
      ///     throw on every modification. This ClassLoader could also supply information
      ///     about all the method calls involved.<br />
      ///     <br />
      ///     For now our approach only works if expressions are directly backed by a
      ///     single field.<br />
      ///     <br />
      ///     We were inspired for this approach when we saw that Thomas Mueller managed to
      ///     map expressions to fields for his JaQu query interface, Kudos!
      ///     http://www.h2database.com/html/jaqu.html<br />
      ///     <br />
      ///     We took the idea a bit further and made it work for all primitives except for
      ///     boolean and we plan to also get deeper expressions, collections and
      ///     interfaces working nicely.
      /// </remarks>
      public class Prototypes
      {
          private static Hashtable4 _integerConverters;
          private static readonly string StringIdentifier = "QLinIdentity";
          private readonly bool _ignoreTransient;
          private readonly Hashtable4 _prototypes = new Hashtable4();
          private readonly int _recursionDepth;
          private readonly IReflector _reflector;
  
          public Prototypes(IReflector reflector, int recursionDepth, bool ignoreTransient)
          {
              _reflector = reflector;
              _recursionDepth = recursionDepth;
              _ignoreTransient = ignoreTransient;
          }
  
          public Prototypes() : this(DefaultReflector(), 5, false)
          {
          }
  
          /// <summary>returns a prototype object for a specific class.</summary>
          /// <remarks>returns a prototype object for a specific class.</remarks>
          public virtual object PrototypeForClass(Type clazz)
          {
              if (clazz == null)
              {
                  throw new PrototypesException("Class can not be null");
              }
              var claxx = _reflector.ForClass(clazz);
              if (claxx == null)
              {
                  throw new PrototypesException("Not found in the reflector: " + clazz);
              }
              var className = claxx.GetName();
              var prototype = (Prototype) _prototypes.Get(className);
              if (prototype != null)
              {
                  return prototype.Object();
              }
              prototype = new Prototype(this, claxx);
              _prototypes.Put(className, prototype);
              return prototype.Object();
          }
  
          /// <summary>
          ///     analyzes the passed expression and tries to find the path to the
          ///     backing field that is accessed.
          /// </summary>
          /// <remarks>
          ///     analyzes the passed expression and tries to find the path to the
          ///     backing field that is accessed.
          /// </remarks>
          public virtual IEnumerator BackingFieldPath(Type clazz, object expression)
          {
              return BackingFieldPath(_reflector.ForClass(clazz), expression);
          }
  
          /// <summary>
          ///     analyzes the passed expression and tries to find the path to the
          ///     backing field that is accessed.
          /// </summary>
          /// <remarks>
          ///     analyzes the passed expression and tries to find the path to the
          ///     backing field that is accessed.
          /// </remarks>
          public virtual IEnumerator BackingFieldPath(IReflectClass claxx, object expression
              )
          {
              return BackingFieldPath(claxx.GetName(), expression);
          }
  
          /// <summary>
          ///     analyzes the passed expression and tries to find the path to the
          ///     backing field that is accessed.
          /// </summary>
          /// <remarks>
          ///     analyzes the passed expression and tries to find the path to the
          ///     backing field that is accessed.
          /// </remarks>
          public virtual IEnumerator BackingFieldPath(string className, object expression)
          {
              var prototype = (Prototype) _prototypes.Get(className);
              if (prototype == null)
              {
                  return null;
              }
              return prototype.BackingFieldPath(_reflector, expression);
          }
  
          private static IntegerConverter IntegerConverterforClassName(IReflector
              reflector, string className)
          {
              if (_integerConverters == null)
              {
                  _integerConverters = new Hashtable4();
                  IntegerConverter[] converters =
                  {
                      new
                          _IntegerConverter_211(),
                      new _IntegerConverter_215(), new _IntegerConverter_219(
                          ),
                      new _IntegerConverter_223(), new _IntegerConverter_227(), new _IntegerConverter_231
                          (),
                      new _IntegerConverter_235(), new _IntegerConverter_239()
                  };
                  for (var converterIndex = 0; converterIndex < converters.Length; ++converterIndex)
                  {
                      var converter = converters[converterIndex];
                      _integerConverters.Put(converter.PrimitiveName(), converter);
                      if (!converter.PrimitiveName().Equals(converter.WrapperName(reflector)))
                      {
                          _integerConverters.Put(converter.WrapperName(reflector), converter);
                      }
                  }
              }
              return (IntegerConverter) _integerConverters.Get(className);
          }
  
          // Strings get prepended the following, so we can also use strings 
          // without restrictions in queries.
          public virtual IReflector Reflector()
          {
              return _reflector;
          }
  
          // We could always use this, but we want to make users of this class
          // aware that they have control over the reflector and that it is
          // important.
          public static IReflector DefaultReflector()
          {
              return new GenericReflector(Platform4.ReflectorForType(typeof (Prototypes)));
          }
  
          private static bool TrySetField(IReflectField field, object onObject, object value
              )
          {
              try
              {
                  field.Set(onObject, value);
              }
              catch
              {
                  return false;
              }
              return true;
          }
  
          private class Prototype
          {
              private readonly Prototypes _enclosing;
              private readonly IdentityHashtable4 _fieldsByIdentity = new IdentityHashtable4();
              private readonly Hashtable4 _fieldsByIntId = new Hashtable4();
              private readonly object _object;
              private int intIdGenerator;
  
              public Prototype(Prototypes _enclosing, IReflectClass claxx)
              {
                  this._enclosing = _enclosing;
                  _object = claxx.NewInstance();
                  if (_object == null)
                  {
                      throw new PrototypesException("Prototype could not be created for class " + claxx
                          .GetName());
                  }
                  Analyze(_object, claxx, this._enclosing._recursionDepth, null);
              }
  
              private void Analyze(object @object, IReflectClass claxx, int depth, List4 parentPath
                  )
              {
                  if (depth < 0)
                  {
                      return;
                  }
                  ReflectorUtils.ForEachField(claxx, new _IProcedure4_130(this, parentPath, claxx,
                      @object, depth));
              }
  
              public virtual object Object()
              {
                  return _object;
              }
  
              public virtual IEnumerator BackingFieldPath(IReflector reflector, object expression
                  )
              {
                  if (expression == null)
                  {
                      return null;
                  }
                  var claxx = reflector.ForObject(expression);
                  if (claxx == null)
                  {
                      return null;
                  }
                  var converter = IntegerConverterforClassName(reflector
                      , claxx.GetName());
                  if (converter != null)
                  {
                      var entry = (Pair) _fieldsByIntId.Get(converter.ToInteger(expression));
                      if (entry == null)
                      {
                          return null;
                      }
                      if (entry.first.Equals(expression))
                      {
  
gt:                         return AsIterator((List4) entry.second);