001: /*
002: * Copyright 1994-2004 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package java.io;
027:
028: /**
029: * A data output stream lets an application write primitive Java data
030: * types to an output stream in a portable way. An application can
031: * then use a data input stream to read the data back in.
032: *
033: * @author unascribed
034: * @version 1.50, 05/05/07
035: * @see java.io.DataInputStream
036: * @since JDK1.0
037: */
038: public class DataOutputStream extends FilterOutputStream implements
039: DataOutput {
040: /**
041: * The number of bytes written to the data output stream so far.
042: * If this counter overflows, it will be wrapped to Integer.MAX_VALUE.
043: */
044: protected int written;
045:
046: /**
047: * bytearr is initialized on demand by writeUTF
048: */
049: private byte[] bytearr = null;
050:
051: /**
052: * Creates a new data output stream to write data to the specified
053: * underlying output stream. The counter <code>written</code> is
054: * set to zero.
055: *
056: * @param out the underlying output stream, to be saved for later
057: * use.
058: * @see java.io.FilterOutputStream#out
059: */
060: public DataOutputStream(OutputStream out) {
061: super (out);
062: }
063:
064: /**
065: * Increases the written counter by the specified value
066: * until it reaches Integer.MAX_VALUE.
067: */
068: private void incCount(int value) {
069: int temp = written + value;
070: if (temp < 0) {
071: temp = Integer.MAX_VALUE;
072: }
073: written = temp;
074: }
075:
076: /**
077: * Writes the specified byte (the low eight bits of the argument
078: * <code>b</code>) to the underlying output stream. If no exception
079: * is thrown, the counter <code>written</code> is incremented by
080: * <code>1</code>.
081: * <p>
082: * Implements the <code>write</code> method of <code>OutputStream</code>.
083: *
084: * @param b the <code>byte</code> to be written.
085: * @exception IOException if an I/O error occurs.
086: * @see java.io.FilterOutputStream#out
087: */
088: public synchronized void write(int b) throws IOException {
089: out.write(b);
090: incCount(1);
091: }
092:
093: /**
094: * Writes <code>len</code> bytes from the specified byte array
095: * starting at offset <code>off</code> to the underlying output stream.
096: * If no exception is thrown, the counter <code>written</code> is
097: * incremented by <code>len</code>.
098: *
099: * @param b the data.
100: * @param off the start offset in the data.
101: * @param len the number of bytes to write.
102: * @exception IOException if an I/O error occurs.
103: * @see java.io.FilterOutputStream#out
104: */
105: public synchronized void write(byte b[], int off, int len)
106: throws IOException {
107: out.write(b, off, len);
108: incCount(len);
109: }
110:
111: /**
112: * Flushes this data output stream. This forces any buffered output
113: * bytes to be written out to the stream.
114: * <p>
115: * The <code>flush</code> method of <code>DataOutputStream</code>
116: * calls the <code>flush</code> method of its underlying output stream.
117: *
118: * @exception IOException if an I/O error occurs.
119: * @see java.io.FilterOutputStream#out
120: * @see java.io.OutputStream#flush()
121: */
122: public void flush() throws IOException {
123: out.flush();
124: }
125:
126: /**
127: * Writes a <code>boolean</code> to the underlying output stream as
128: * a 1-byte value. The value <code>true</code> is written out as the
129: * value <code>(byte)1</code>; the value <code>false</code> is
130: * written out as the value <code>(byte)0</code>. If no exception is
131: * thrown, the counter <code>written</code> is incremented by
132: * <code>1</code>.
133: *
134: * @param v a <code>boolean</code> value to be written.
135: * @exception IOException if an I/O error occurs.
136: * @see java.io.FilterOutputStream#out
137: */
138: public final void writeBoolean(boolean v) throws IOException {
139: out.write(v ? 1 : 0);
140: incCount(1);
141: }
142:
143: /**
144: * Writes out a <code>byte</code> to the underlying output stream as
145: * a 1-byte value. If no exception is thrown, the counter
146: * <code>written</code> is incremented by <code>1</code>.
147: *
148: * @param v a <code>byte</code> value to be written.
149: * @exception IOException if an I/O error occurs.
150: * @see java.io.FilterOutputStream#out
151: */
152: public final void writeByte(int v) throws IOException {
153: out.write(v);
154: incCount(1);
155: }
156:
157: /**
158: * Writes a <code>short</code> to the underlying output stream as two
159: * bytes, high byte first. If no exception is thrown, the counter
160: * <code>written</code> is incremented by <code>2</code>.
161: *
162: * @param v a <code>short</code> to be written.
163: * @exception IOException if an I/O error occurs.
164: * @see java.io.FilterOutputStream#out
165: */
166: public final void writeShort(int v) throws IOException {
167: out.write((v >>> 8) & 0xFF);
168: out.write((v >>> 0) & 0xFF);
169: incCount(2);
170: }
171:
172: /**
173: * Writes a <code>char</code> to the underlying output stream as a
174: * 2-byte value, high byte first. If no exception is thrown, the
175: * counter <code>written</code> is incremented by <code>2</code>.
176: *
177: * @param v a <code>char</code> value to be written.
178: * @exception IOException if an I/O error occurs.
179: * @see java.io.FilterOutputStream#out
180: */
181: public final void writeChar(int v) throws IOException {
182: out.write((v >>> 8) & 0xFF);
183: out.write((v >>> 0) & 0xFF);
184: incCount(2);
185: }
186:
187: /**
188: * Writes an <code>int</code> to the underlying output stream as four
189: * bytes, high byte first. If no exception is thrown, the counter
190: * <code>written</code> is incremented by <code>4</code>.
191: *
192: * @param v an <code>int</code> to be written.
193: * @exception IOException if an I/O error occurs.
194: * @see java.io.FilterOutputStream#out
195: */
196: public final void writeInt(int v) throws IOException {
197: out.write((v >>> 24) & 0xFF);
198: out.write((v >>> 16) & 0xFF);
199: out.write((v >>> 8) & 0xFF);
200: out.write((v >>> 0) & 0xFF);
201: incCount(4);
202: }
203:
204: private byte writeBuffer[] = new byte[8];
205:
206: /**
207: * Writes a <code>long</code> to the underlying output stream as eight
208: * bytes, high byte first. In no exception is thrown, the counter
209: * <code>written</code> is incremented by <code>8</code>.
210: *
211: * @param v a <code>long</code> to be written.
212: * @exception IOException if an I/O error occurs.
213: * @see java.io.FilterOutputStream#out
214: */
215: public final void writeLong(long v) throws IOException {
216: writeBuffer[0] = (byte) (v >>> 56);
217: writeBuffer[1] = (byte) (v >>> 48);
218: writeBuffer[2] = (byte) (v >>> 40);
219: writeBuffer[3] = (byte) (v >>> 32);
220: writeBuffer[4] = (byte) (v >>> 24);
221: writeBuffer[5] = (byte) (v >>> 16);
222: writeBuffer[6] = (byte) (v >>> 8);
223: writeBuffer[7] = (byte) (v >>> 0);
224: out.write(writeBuffer, 0, 8);
225: incCount(8);
226: }
227:
228: /**
229: * Converts the float argument to an <code>int</code> using the
230: * <code>floatToIntBits</code> method in class <code>Float</code>,
231: * and then writes that <code>int</code> value to the underlying
232: * output stream as a 4-byte quantity, high byte first. If no
233: * exception is thrown, the counter <code>written</code> is
234: * incremented by <code>4</code>.
235: *
236: * @param v a <code>float</code> value to be written.
237: * @exception IOException if an I/O error occurs.
238: * @see java.io.FilterOutputStream#out
239: * @see java.lang.Float#floatToIntBits(float)
240: */
241: public final void writeFloat(float v) throws IOException {
242: writeInt(Float.floatToIntBits(v));
243: }
244:
245: /**
246: * Converts the double argument to a <code>long</code> using the
247: * <code>doubleToLongBits</code> method in class <code>Double</code>,
248: * and then writes that <code>long</code> value to the underlying
249: * output stream as an 8-byte quantity, high byte first. If no
250: * exception is thrown, the counter <code>written</code> is
251: * incremented by <code>8</code>.
252: *
253: * @param v a <code>double</code> value to be written.
254: * @exception IOException if an I/O error occurs.
255: * @see java.io.FilterOutputStream#out
256: * @see java.lang.Double#doubleToLongBits(double)
257: */
258: public final void writeDouble(double v) throws IOException {
259: writeLong(Double.doubleToLongBits(v));
260: }
261:
262: /**
263: * Writes out the string to the underlying output stream as a
264: * sequence of bytes. Each character in the string is written out, in
265: * sequence, by discarding its high eight bits. If no exception is
266: * thrown, the counter <code>written</code> is incremented by the
267: * length of <code>s</code>.
268: *
269: * @param s a string of bytes to be written.
270: * @exception IOException if an I/O error occurs.
271: * @see java.io.FilterOutputStream#out
272: */
273: public final void writeBytes(String s) throws IOException {
274: int len = s.length();
275: for (int i = 0; i < len; i++) {
276: out.write((byte) s.charAt(i));
277: }
278: incCount(len);
279: }
280:
281: /**
282: * Writes a string to the underlying output stream as a sequence of
283: * characters. Each character is written to the data output stream as
284: * if by the <code>writeChar</code> method. If no exception is
285: * thrown, the counter <code>written</code> is incremented by twice
286: * the length of <code>s</code>.
287: *
288: * @param s a <code>String</code> value to be written.
289: * @exception IOException if an I/O error occurs.
290: * @see java.io.DataOutputStream#writeChar(int)
291: * @see java.io.FilterOutputStream#out
292: */
293: public final void writeChars(String s) throws IOException {
294: int len = s.length();
295: for (int i = 0; i < len; i++) {
296: int v = s.charAt(i);
297: out.write((v >>> 8) & 0xFF);
298: out.write((v >>> 0) & 0xFF);
299: }
300: incCount(len * 2);
301: }
302:
303: /**
304: * Writes a string to the underlying output stream using
305: * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
306: * encoding in a machine-independent manner.
307: * <p>
308: * First, two bytes are written to the output stream as if by the
309: * <code>writeShort</code> method giving the number of bytes to
310: * follow. This value is the number of bytes actually written out,
311: * not the length of the string. Following the length, each character
312: * of the string is output, in sequence, using the modified UTF-8 encoding
313: * for the character. If no exception is thrown, the counter
314: * <code>written</code> is incremented by the total number of
315: * bytes written to the output stream. This will be at least two
316: * plus the length of <code>str</code>, and at most two plus
317: * thrice the length of <code>str</code>.
318: *
319: * @param str a string to be written.
320: * @exception IOException if an I/O error occurs.
321: */
322: public final void writeUTF(String str) throws IOException {
323: writeUTF(str, this );
324: }
325:
326: /**
327: * Writes a string to the specified DataOutput using
328: * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
329: * encoding in a machine-independent manner.
330: * <p>
331: * First, two bytes are written to out as if by the <code>writeShort</code>
332: * method giving the number of bytes to follow. This value is the number of
333: * bytes actually written out, not the length of the string. Following the
334: * length, each character of the string is output, in sequence, using the
335: * modified UTF-8 encoding for the character. If no exception is thrown, the
336: * counter <code>written</code> is incremented by the total number of
337: * bytes written to the output stream. This will be at least two
338: * plus the length of <code>str</code>, and at most two plus
339: * thrice the length of <code>str</code>.
340: *
341: * @param str a string to be written.
342: * @param out destination to write to
343: * @return The number of bytes written out.
344: * @exception IOException if an I/O error occurs.
345: */
346: static int writeUTF(String str, DataOutput out) throws IOException {
347: int strlen = str.length();
348: int utflen = 0;
349: int c, count = 0;
350:
351: /* use charAt instead of copying String to char array */
352: for (int i = 0; i < strlen; i++) {
353: c = str.charAt(i);
354: if ((c >= 0x0001) && (c <= 0x007F)) {
355: utflen++;
356: } else if (c > 0x07FF) {
357: utflen += 3;
358: } else {
359: utflen += 2;
360: }
361: }
362:
363: if (utflen > 65535)
364: throw new UTFDataFormatException(
365: "encoded string too long: " + utflen + " bytes");
366:
367: byte[] bytearr = null;
368: if (out instanceof DataOutputStream) {
369: DataOutputStream dos = (DataOutputStream) out;
370: if (dos.bytearr == null
371: || (dos.bytearr.length < (utflen + 2)))
372: dos.bytearr = new byte[(utflen * 2) + 2];
373: bytearr = dos.bytearr;
374: } else {
375: bytearr = new byte[utflen + 2];
376: }
377:
378: bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
379: bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);
380:
381: int i = 0;
382: for (i = 0; i < strlen; i++) {
383: c = str.charAt(i);
384: if (!((c >= 0x0001) && (c <= 0x007F)))
385: break;
386: bytearr[count++] = (byte) c;
387: }
388:
389: for (; i < strlen; i++) {
390: c = str.charAt(i);
391: if ((c >= 0x0001) && (c <= 0x007F)) {
392: bytearr[count++] = (byte) c;
393:
394: } else if (c > 0x07FF) {
395: bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
396: bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F));
397: bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
398: } else {
399: bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
400: bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
401: }
402: }
403: out.write(bytearr, 0, utflen + 2);
404: return utflen + 2;
405: }
406:
407: /**
408: * Returns the current value of the counter <code>written</code>,
409: * the number of bytes written to this data output stream so far.
410: * If the counter overflows, it will be wrapped to Integer.MAX_VALUE.
411: *
412: * @return the value of the <code>written</code> field.
413: * @see java.io.DataOutputStream#written
414: */
415: public final int size() {
416: return written;
417: }
418: }
|