// -*- mode: Java; file-coding-system: *euc-japan*; -*-
// $Id: ByteArrayEncodedData.java,v 1.2 1999/09/22 07:27:07 maeda Exp $
// Copyright (C) 1999 MAEDA Atusi

package jp.ac.tohoku.is.archi.mpi2.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import mpi.Datatype;
import mpi.MPI;

/**
 * byteの配列を用いて送受信を行なう{@link EncodedData}の実装.
 *
 * この実装を用いるためには，受信時に{@link #prepare(ObjectDecoder)}を用いず，
 * {@link #allocate}を用いてバッファを割り付けることが必要である．
 */
final class ByteArrayEncodedData implements EncodedData {
  private byte[] buffer = null;
  public Datatype type() {
    return MPI.BYTE;
  }
  public Object getArray() {
    if (buffer == null) {
      throw new RuntimeException("EncodedData not finished yet.");
    }
    return buffer;
  }
  public int length() {
    return buffer.length;
  }
  final class ByteArrayEncoder implements Encoder {
    private ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    private ObjectOutputStream stream;
    ByteArrayEncoder() {
      try {
	stream = new ObjectOutputStream(bytes);
      } catch (IOException e) {
	new RuntimeException("Exception while initializing ByteArrayEncoder\n" +
			     "Nested exception is: " + e);
      }
    }
    public void prepare(Encodable data) {
      // Do nothing.
    }
    public void finishPrepare() {
      // Do nothing.
    }
    public void allocate(Encodable data, int nelem) {
      // Do nothing.
    }
    public void encode(Encodable data) {
      try {
	stream.writeObject(data);
      } catch (IOException e) {
	throw new RuntimeException("Exception while encoding.\n" +
				   "Nested exception is: " + e);
      }
    }
    public void finishEncode() {
      try {
	stream.close();
	buffer = bytes.toByteArray();
      } catch (IOException e) {
	throw new RuntimeException("Exception while encoding.\n" +
				   "Nested exception is: " + e);
      }
    }
  }
  public Encoder getEncoder() {
    return new ByteArrayEncoder();
  }
  final class ByteArrayDecoder implements Decoder {
    private ObjectInputStream stream;
    public void prepare(ObjectDecoder od) {
      // この方法では処理できない.
      throw new RuntimeException("ByteArrayDecoder cannot be prepared.  Use allocate(int) instead.");
    }
    public void finishPrepare() {
      // この方法では処理できない.
      throw new RuntimeException("ByteArrayDecoder cannot be prepared.  Use allocate(int) instead.");
    }
    public void allocate(ObjectDecoder od, int nelem) {
      // この方法では処理できない.
      throw new RuntimeException("ByteArrayDecoder cannot be prepared.  Use allocate(int) instead.");
    }
    public void allocate(int size) {
      buffer = new byte[size];
      try {
	stream = new ObjectInputStream(new ByteArrayInputStream(buffer));
      } catch (IOException e) {
	throw new RuntimeException("Exception while encoding.\n" +
				   "Nested exception is: " + e);
      }
    }
    public Object decode(ObjectDecoder od) {
      try {
	return stream.readObject();
      } catch (Exception e) {
	throw new RuntimeException("Exception while decoding.\n" +
				   "Nested exception is: " + e);
      }
    }
  }
  public Decoder getDecoder() {
    return new ByteArrayDecoder();
  }
}
