context: /*
   * Copyright (C) 2009, Google Inc.
   * Copyright (C) 2009, Henon <meinrad.recheis@gmail.com>
   *
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or
   * without modification, are permitted provided that the following
   * conditions are met:
   *
   * - Redistributions of source code must retain the above copyright
   *   notice, this list of conditions and the following disclaimer.
   *
   * - Redistributions in binary form must reproduce the above
   *   copyright notice, this list of conditions and the following
   *   disclaimer in the documentation and/or other materials provided
   *   with the distribution.
   *
   * - Neither the name of the Git Development Community nor the
   *   names of its contributors may be used to endorse or promote
   *   products derived from this software without specific prior
   *   written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  using System;
  using System.Collections.Generic;
  using System.IO;
  using GitSharp.Core.Util;
  using GitSharp.Core.Util.JavaHelper;
  
  namespace GitSharp.Core
  {
      /// <summary>
      /// Abstraction of arbitrary object storage.
      /// <para />
      /// An object database stores one or more Git objects, indexed by their unique
      /// <see cref="ObjectId"/>. Optionally an object database can reference one or more
      /// alternates; other <see cref="ObjectDatabase"/> instances that are searched in
      /// addition to the current database.
      /// <para />
      /// Databases are usually divided into two halves: a half that is considered to
      /// be fast to search, and a half that is considered to be slow to search. When
      /// alternates are present the fast half is fully searched (recursively through
      /// all alternates) before the slow half is considered.
      /// </summary>
      public abstract class ObjectDatabase : IDisposable
      {
          /// <summary>
          /// Constant indicating no alternate databases exist.
          /// </summary>
          protected static readonly ObjectDatabase[] NoAlternates = { };
  
          private readonly AtomicReference<ObjectDatabase[]> _alternates;
  
          /// <summary>
          /// Initialize a new database instance for access.
          /// </summary>
          protected ObjectDatabase()
          {
              _alternates = new AtomicReference<ObjectDatabase[]>();
          }
  
          /// <summary>
          /// Does this database exist yet?
          /// </summary>
          /// <returns>
          /// true if this database is already created; false if the caller
          /// should invoke <see cref="create"/> to create this database location.
          /// </returns>
          public virtual bool exists()
          {
              return true;
          }
  
          /// <summary>
          /// Initialize a new object database at this location.
          /// </summary>
          public virtual void create()
          {
              // Assume no action is required.
          }
  
          public virtual void Dispose()
          {
              close();
          }
  
          /// <summary>
          /// Close any resources held by this database and its active alternates.
          /// </summary>
          public void close()
          {
              closeSelf();
              closeAlternates();
  #if DEBUG
              GC.SuppressFinalize(this); // Disarm lock-release checker
  #endif
          }
  
  #if DEBUG
          // A debug mode warning if the type has not been disposed properly
          ~ObjectDatabase()
          {
              Console.Error.WriteLine(GetType().Name + " has not been properly disposed.");
          }
  #endif
          /// <summary>
          /// Close any resources held by this database only; ignoring alternates.
          /// <para />
          /// To fully close this database and its referenced alternates, the caller
          /// should instead invoke <see cref="close()"/>.
          /// </summary>
          public virtual void closeSelf()
          {
              // Assume no action is required.
          }
  
          /// <summary>
          /// Fully close all loaded alternates and clear the alternate list.
          /// </summary>
          public virtual void closeAlternates()
          {
              ObjectDatabase[] alt = _alternates.get();
              if (alt != null)
              {
                  _alternates.set(null);
                  closeAlternates(alt);
              }
          }
  
          /// <summary>
          /// Does the requested object exist in this database?
          /// <para />
          /// Alternates (if present) are searched automatically.
          /// </summary>
          /// <param name="objectId">identity of the object to test for existence of.</param>
          /// <returns>
          /// True if the specified object is stored in this database, or any
          /// of the alternate databases.
          /// </returns>
          public bool hasObject(AnyObjectId objectId)
          {
              return hasObjectImpl1(objectId) || hasObjectImpl2(objectId.Name);
          }
  
          private bool hasObjectImpl1(AnyObjectId objectId)
          {
              if (hasObject1(objectId))
              {
                  return true;
              }
              foreach (ObjectDatabase alt in getAlternates())
              {
                  if (alt.hasObjectImpl1(objectId))
                  {
                      return true;
                  }
              }
              return tryAgain1() && hasObject1(objectId);
          }
  
          private bool hasObjectImpl2(string objectId)
          {
              if (hasObject2(objectId))
              {
                  return true;
              }
              foreach (ObjectDatabase alt in getAlternates())
              {
                  if (alt.hasObjectImpl2(objectId))
                  {
                      return true;
                  }
              }
              return false;
          }
  
          /// <summary>
          /// Fast half of <see cref="hasObject(AnyObjectId)"/>.
          /// </summary>
          /// <param name="objectId">
          /// Identity of the object to test for existence of.
          /// </param>
          /// <returns>
          /// true if the specified object is stored in this database.
          /// </returns>
          public abstract bool hasObject1(AnyObjectId objectId);
  
          /// <summary>
          /// Slow half of <see cref="hasObject(AnyObjectId)"/>.
          /// </summary>
          /// <param name="objectName">
          /// Identity of the object to test for existence of.
          /// </param>
          /// <returns>
          /// true if the specified object is stored in this database.
          /// </returns>
          public virtual bool hasObject2(string objectName)
          {
              // Assume the search took place during hasObject1.
              return false;
          }
  
          /// <summary>
          /// Open an object from this database.
          /// <para />
          /// Alternates (if present) are searched automatically.
          /// </summary>
          /// <param name="curs">
          /// Temporary working space associated with the calling thread.
          /// </param>
          /// <param name="objectId">Identity of the object to open.</param>
          /// <returns>
          /// A <see cref="ObjectLoader"/> for accessing the data of the named
          /// object, or null if the object does not exist.
          /// </returns>
          public ObjectLoader openObject(WindowCursor curs, AnyObjectId objectId)
          {
              if (objectId == null) return null;
  
              ObjectLoader ldr = OpenObjectImpl1(curs, objectId);
              if (ldr != null)
              {
                  return ldr;
              }
  
              ldr = OpenObjectImpl2(curs, objectId.Name, objectId);
              if (ldr != null)
              {
                  return ldr;
              }
              return null;
          }
  
          private ObjectLoader OpenObjectImpl1(WindowCursor curs,
                   AnyObjectId objectId)
          {
  
gt:             ObjectLoader ldr = openObject1(curs, objectId);