/*******************************************************************************
 * Copyright (c) 2002 - 2006 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package com.ibm.wala.util.shrike;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import com.ibm.wala.classLoader.ModuleEntry;
import com.ibm.wala.shrikeCT.ClassReader;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.ref.CacheReference;

/**
 * A soft handle to a Shrike class reader
 * 
 * TODO: implement more effective caching than just soft references TODO: push
 * weakness up the chain the InputStream, etc ... TODO: reduce reliance on
 * reader throughout the analysis packages
 */
public class ShrikeClassReaderHandle {

  private final static boolean DEBUG = false;
  /**
   * The module entry that defines the class file
   */
  private final ModuleEntry entry;

  private Object reader;

  /**
   * The number of times we hydrate the reader
   */
  private int hydrateCount = 0;

  public ShrikeClassReaderHandle(ModuleEntry entry) {
    if (entry == null) {
      throw new IllegalArgumentException("null entry");
    }
    this.entry = entry;
  }

  /**
   * @return an instance of the class reader ... create one if necessary
   * @throw InvalidClassFileException iff Shrike fails to read the class file
   *        correctly.
   */
  public ClassReader get() throws InvalidClassFileException {
    ClassReader result = (ClassReader) CacheReference.get(reader);
    if (result == null) {
      hydrateCount++;
      if (DEBUG) {
        if (hydrateCount > 1) {
          System.err.println(("Hydrate " + entry + " " + hydrateCount));
          try {
            throw new Exception();
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }
      ByteArrayOutputStream S = new ByteArrayOutputStream();
      try {
        InputStream s = entry.getInputStream();
        readBytes(s, S);
        s.close();
      } catch (IOException e) {
        e.printStackTrace();
        Assertions.UNREACHABLE();
      }
      result = new ClassReader(S.toByteArray());
      reader = CacheReference.make(result);
    }
    return result;
  }

  /**
   * Read is into bytes
   * @throws IOException
   */
  private void readBytes(InputStream is, ByteArrayOutputStream bytes) throws IOException {
    int n = 0;
    byte[] buffer = new byte[1024];
    while (n > -1) {
      n = is.read(buffer, 0, 1024);
      if (n > -1) {
        bytes.write(buffer, 0, n);
      }
    }
  }

  public String getFileName() {
    return entry.getName();
  }

  /**
   * Force the reference to be cleared/collected
   */
  public void clear() {
    reader = null;
  }

  public ModuleEntry getModuleEntry() {
    return entry;
  }}
