blob: cdbfb597c03e886813151a0d740a81a6e4e675b8 [file] [log] [blame]
/*
* Copyright 2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jdo.impl.fostore;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import org.apache.jdo.util.I18NHelper;
/**
* Extend ByteArrayOutputStream so that we can get ahold of the byte array
* and current position, and can make sure we have enough space to write an
* object. We also allow getting and changing the current position. Also,
* implement DataOutput so that we can write easily to this output.
*
* @author Dave Bristor
*/
class FOStoreOutput implements DataOutput {
// Once closed, no more writing allowed.
private boolean closed = false;
private final LocalByteArrayOutputStream stream;
private final DataOutputStream dos;
/** I18N support. */
private static final I18NHelper msg = I18NHelper.getInstance(I18N.NAME);
FOStoreOutput() {
stream = new LocalByteArrayOutputStream();
dos = new DataOutputStream(stream);
}
/** Close the stream. The stream can no longer be written.
*/
public void close() {
closed = true;
}
/** Reset the stream. Discard the current contents, and reset the count
* to 0 and the current position to 0. The current buffer is retained.
*/
public void reset() {
stream.reset();
}
// In a simpler world, FOStoreOutput would extend ByteArrayOutputStream
// and implement DataOutput. However. The creators of the early Java
// classes wisely noted that, in ByteArrayOutputStream, the write methods
// could not throw IOException, after all, there's no I/O happening. Of
// course, with DataOutput, you never know what's behind the scenes, so
// it's write methods *do* throw IOException. Since we need the
// functionality of both, and these are in conflice, we cannot both
// extend ByteArrayOutputStream *and* implement DataOutput. So we
// implement DataOutput and delegate to this extension.
//
class LocalByteArrayOutputStream extends ByteArrayOutputStream {
LocalByteArrayOutputStream() {
super();
}
byte[] getBuf() {
return buf;
}
int getCurrentPosition() {
return count;
}
void seek(int pos) {
if (pos < 0 || pos > buf.length) {
throw new FOStoreFatalInternalException(
this.getClass(), "seek", // NOI18N
msg.msg("ERR_InvalidSeekPos", // NOI18N
new Integer(pos), new Integer(buf.length)));
}
this.count = pos;
}
}
/**
* Provides no-copy access to the buffer.
* @return The byte array representing this stream. <em>Not a copy.</em>
*/
byte[] getBuf() {
return stream.getBuf();
}
//
// A common need of RequestHandlers is to write a nonsense number, which
// is later filled in with a count or length (etc.) appropriate to the
// reply's acutal data. These help do that. Use them instead of
// getPos/setPos, if/when you can.
//
// This is easy to do by hand, but I've burned myself too many times
// by being imprecise when doing it that way!
//
/**
* Write a nonsense int value at the current position, and return that
* position for later use with endStash
* @return Position in this output for later use in writing a 'real'
* value.
* @see #endStash
*/
int beginStash() throws IOException {
int rc = getPos();
writeInt(0xbadbad10);
return rc;
}
/**
* Write the given value at the given position, and reset the position to
* what it was before the write occurred.
* @param value Value to be written
* @param pos Position in this output at which value is to be written
* @see #beginStash
*/
void endStash(int value, int pos) throws IOException {
int savedPos = getPos();
setPos(pos);
writeInt(value);
setPos(savedPos);
}
/**
* Provides the stream's current writing position.
* @return The current writing position of the stream.
*/
int getPos() {
return stream.getCurrentPosition();
}
/**
* Allows for setting the current writing position.
* @param pos Position at which future write operations will take
* place.
*/
void setPos(int pos) throws IOException {
stream.seek(pos);
}
//
// Implement DataOutput by forwarding onto our private DataOutputStream
//
public void write(byte[] b) throws IOException {
assertNotClosed();
dos.write(b);
}
public void write(int b) throws IOException {
assertNotClosed();
dos.write(b);
}
public void write(byte[] b, int off, int len) throws IOException {
assertNotClosed();
dos.write(b, off, len);
}
public void writeBoolean(boolean v) throws IOException {
assertNotClosed();
dos.writeBoolean(v);
}
public void writeByte(int v) throws IOException {
assertNotClosed();
dos.writeByte(v);
}
public void writeBytes(String s) throws IOException {
assertNotClosed();
dos.writeBytes(s);
}
public void writeChar(int v) throws IOException {
assertNotClosed();
dos.writeChar(v);
}
public void writeChars(String s) throws IOException {
assertNotClosed();
dos.writeChars(s);
}
public void writeDouble(double v) throws IOException {
assertNotClosed();
dos.writeDouble(v);
}
public void writeFloat(float v) throws IOException {
assertNotClosed();
dos.writeFloat(v);
}
public void writeInt(int v) throws IOException {
assertNotClosed();
dos.writeInt(v);
}
public void writeLong(long v) throws IOException {
assertNotClosed();
dos.writeLong(v);
}
public void writeShort(int v) throws IOException {
assertNotClosed();
dos.writeShort(v);
}
public void writeUTF(String str) throws IOException {
assertNotClosed();
dos.writeUTF(str);
}
//
// Private implementation methods
//
private void assertNotClosed() {
if (closed) {
throw new FOStoreFatalInternalException(
getClass(), "assertNotClosed", // NOI18N
msg.msg("ERR_Closed")); // NOI18N
}
}
}