/FileExtraction/Java_unseen/dlitz_resin/modules/resin/src/com/caucho/env/repository/FileRepository.java
/*
 * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source 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, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.env.repository;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

import com.caucho.env.git.GitCommit;
import com.caucho.env.git.GitObjectStream;
import com.caucho.env.git.GitSystem;
import com.caucho.env.git.GitTree;
import com.caucho.env.git.GitType;
import com.caucho.util.L10N;
import com.caucho.vfs.Path;
import com.caucho.vfs.Vfs;
import com.caucho.vfs.WriteStream;

public class FileRepository extends AbstractRepository
{
  private static final L10N L = new L10N(FileRepository.class);

  private GitSystem _git;
  
  private AtomicReference<String> _rootHash = new AtomicReference<String>();
  
  public FileRepository()
  {
    this(GitSystem.getCurrent());
  }

  public FileRepository(GitSystem git)
  {
    _git = git;
    
    if (_git == null)
      throw new IllegalStateException(L.l("{0} is required for {1}",
                                          GitSystem.class.getSimpleName(),
                                          getClass().getSimpleName()));
  }

  /**
   * Updates the repository
   */
  @Override
  public void checkForUpdate()
  {
    update(getRepositoryRootHash(), false);
  }

  //
  // tag metadata management
  //

  /**
   * Adds a tag
   *
   * @param tagName the symbolic tag for the repository
   * @param contentHash the sha1 hash of the tag's content
   * @param commitMessage user's message for the commit
   * @param commitMetaData additional commit metadata
   */
  @Override
  public boolean putTag(String tagName,
                        String contentHash,
                        Map<String,String> commitMetaData)
  {
    RepositoryTagMap tagMap;

    do {
      tagMap = addTagData(tagName, contentHash, commitMetaData);
    } while (! setTagMap(tagMap));
    
    return true;
  }

  /**
   * Removes a tag
   *
   * @param tagName the symbolic tag for the repository
   * @param commitMessage user's message for the commit
   * @param commitMetaData additional metadata
   */
  @Override
  public boolean removeTag(String tagName,
                           Map<String,String> commitMetaData)
  {
    RepositoryTagMap tagMap;

    do {
      tagMap = removeTagData(tagName, commitMetaData);
    } while (! setTagMap(tagMap));

    return true;
  }

  /**
   * Returns the hash stored in the git tag
   */
  @Override
  public String getRepositoryRootHash()
  {
    String rootHash = _rootHash.get();
    
    if (rootHash != null && rootHash.length() > 6)
      return rootHash;
    
    String value = _git.getTag(getRepositoryTag());
    
    _rootHash.compareAndSet(null, value);
    
    value = _rootHash.get();
    
    if (value != null && value.length() > 6)
      return value;
    else
      return null;
  }
  
  /**
   * Sets the hash stored in the git tag
   */
  @Override
  public void setRepositoryRootHash(String sha1)
  {
    if (sha1 != null) {
      _rootHash.set(sha1);

      _git.writeTag(getRepositoryTag(), sha1);
    }
  }

  /**
   * Returns true if the file exists.
   */
  @Override
  public boolean exists(String hash)
  {
    if (hash == null)
      throw new NullPointerException();
    
    return _git.contains(hash);
  }

  /**
   * Returns true if the file is a blob.
   */
  @Override
  public GitType getType(String sha1)
  {
    try {
      return _git.contains(sha1) ? _git.objectType(sha1) : null;
    } catch (IOException e) {
      throw new RepositoryException(e);
    }
  }

  /**
   * Adds a path to the repository.  If the path is a directory or a
   * jar scheme, adds the contents recursively.
   */
  @Override
  public String addBlob(InputStream is)
  {
    try {
      return _git.writeInputStream(is);
    } catch (IOException e) {
      throw new RepositoryException(e);
    }
  }

  /**
   * Adds a path to the repository.  If the path is a directory or a
   * jar scheme, adds the contents recursively.
   */
  @Override
  public String addBlob(InputStream is, long length)
  {
    try {
      return _git.writeInputStream(is, length);
    } catch (IOException e) {
      throw new RepositoryException(e);
    }
  }

  /**
   * Opens a stream to a blob
   */
  @Override
  public InputStream openBlob(String sha1)
    throws IOException
  {
    return _git.openBlob(sha1);
  }

  /**
   * Opens a stream to the raw git file.
   */
  @Override
  public InputStream openRawGitFile(String sha1)
    throws IOException
  {
    return _git.openRawGitFile(sha1);
  }

  /**
   * Reads a git tree from the repository
   */
  @Override
  public GitTree readTree(String sha1)
    throws IOException
  {
    return _git.parseTree(sha1);
  }

  /**
   * Adds a git tree to the repository
   */
  @Override
  public String addTree(GitTree tree)
    throws IOException
  {
    return _git.writeTree(tree);
  }

  /**
   * Reads a git commit from the repository
   */
  @Override
  public GitCommit readCommit(String sha1)
    throws IOException
  {
    return _git.parseCommit(sha1);
  }

  /**
   * Adds a git commit to the repository
   */
  @Override
  public String addCommit(GitCommit commit)
    throws IOException
  {
    return _git.writeCommit(commit);
  }

  /**
   * Writes the git file from the StreamInput
   *
   * @param sha1 the file hash
   * @param is the raw contents for the new file
   */
  @Override
  public void writeRawGitFile(String sha1, InputStream is)
    throws IOException
  {
    _git.writeRawGitFile(sha1, is);
  }

  /**
   * Writes the git file from the StreamInput
   *
   * @param sha1 the file hash
   * @param is the raw contents for the new file
   */
  @Override
  public void validateRawGitFile(String sha1)
  {
    _git.validateRawGitFile(sha1);
  }

  /**
   * Writes the contents to a stream.
   */
  @Override
  public void writeBlobToStream(String blobHash, OutputStream os)
  {
    GitObjectStream is = null;
    
    try {
      is = _git.open(blobHash);
    
      if (is.getType() != GitType.BLOB)
        throw new RepositoryException(L.l("'{0}' is an unexpected type, expected 'blob'",
                                                is.getType()));

      WriteStream out = null;

      if (os instanceof WriteStream)
        out = (WriteStream) os;
      else
        out = Vfs.openWrite(os);

      try {
        out.writeStream(is.getInputStream());
      } finally {
        if (out != null && out != os)
          out.close();
      }
    } catch (IOException e) {
      throw new RepositoryException(e);
    } finally {
      is.close();
    }
  }

  /**
   * Expands the repository to the filesystem.
   */
  @Override
  public void expandToPath(String contentHash, Path path)
  {
    try {
      _git.expandToPath(path, contentHash);
    } catch (IOException e) {
      throw new RepositoryException(e);
    }
  }
}