001: /*
002: *******************************************************************************
003: * Copyright (C) 1996-2004, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */
007: package com.ibm.icu.impl;
008:
009: import java.io.InputStream;
010: import java.io.DataInputStream;
011: import java.io.IOException;
012: import java.util.Arrays;
013:
014: public final class ICUBinary {
015: // public inner interface ------------------------------------------------
016:
017: /**
018: * Special interface for data authentication
019: */
020: public static interface Authenticate {
021: /**
022: * Method used in ICUBinary.readHeader() to provide data format
023: * authentication.
024: * @param version version of the current data
025: * @return true if dataformat is an acceptable version, false otherwise
026: */
027: public boolean isDataVersionAcceptable(byte version[]);
028: }
029:
030: // public methods --------------------------------------------------------
031:
032: /**
033: * <p>ICU data header reader method.
034: * Takes a ICU generated big-endian input stream, parse the ICU standard
035: * file header and authenticates them.</p>
036: * <p>Header format:
037: * <ul>
038: * <li> Header size (char)
039: * <li> Magic number 1 (byte)
040: * <li> Magic number 2 (byte)
041: * <li> Rest of the header size (char)
042: * <li> Reserved word (char)
043: * <li> Big endian indicator (byte)
044: * <li> Character set family indicator (byte)
045: * <li> Size of a char (byte) for c++ and c use
046: * <li> Reserved byte (byte)
047: * <li> Data format identifier (4 bytes), each ICU data has its own
048: * identifier to distinguish them. [0] major [1] minor
049: * [2] milli [3] micro
050: * <li> Data version (4 bytes), the change version of the ICU data
051: * [0] major [1] minor [2] milli [3] micro
052: * <li> Unicode version (4 bytes) this ICU is based on.
053: * </ul>
054: * </p>
055: * <p>
056: * Example of use:<br>
057: * <pre>
058: * try {
059: * FileInputStream input = new FileInputStream(filename);
060: * If (Utility.readICUDataHeader(input, dataformat, dataversion,
061: * unicode) {
062: * System.out.println("Verified file header, this is a ICU data file");
063: * }
064: * } catch (IOException e) {
065: * System.out.println("This is not a ICU data file");
066: * }
067: * </pre>
068: * </p>
069: * @param inputStream input stream that contains the ICU data header
070: * @param dataFormatIDExpected Data format expected. An array of 4 bytes
071: * information about the data format.
072: * E.g. data format ID 1.2.3.4. will became an array of
073: * {1, 2, 3, 4}
074: * @param authenticate user defined extra data authentication. This value
075: * can be null, if no extra authentication is needed.
076: * @exception IOException thrown if there is a read error or
077: * when header authentication fails.
078: * @draft 2.1
079: */
080: public static final byte[] readHeader(InputStream inputStream,
081: byte dataFormatIDExpected[], Authenticate authenticate)
082: throws IOException {
083: DataInputStream input = new DataInputStream(inputStream);
084: char headersize = input.readChar();
085: int readcount = 2;
086: //reading the header format
087: byte magic1 = input.readByte();
088: readcount++;
089: byte magic2 = input.readByte();
090: readcount++;
091: if (magic1 != MAGIC1 || magic2 != MAGIC2) {
092: throw new IOException(MAGIC_NUMBER_AUTHENTICATION_FAILED_);
093: }
094:
095: input.readChar(); // reading size
096: readcount += 2;
097: input.readChar(); // reading reserved word
098: readcount += 2;
099: byte bigendian = input.readByte();
100: readcount++;
101: byte charset = input.readByte();
102: readcount++;
103: byte charsize = input.readByte();
104: readcount++;
105: input.readByte(); // reading reserved byte
106: readcount++;
107:
108: byte dataFormatID[] = new byte[4];
109: input.readFully(dataFormatID);
110: readcount += 4;
111: byte dataVersion[] = new byte[4];
112: input.readFully(dataVersion);
113: readcount += 4;
114: byte unicodeVersion[] = new byte[4];
115: input.readFully(unicodeVersion);
116: readcount += 4;
117: if (headersize < readcount) {
118: throw new IOException("Internal Error: Header size error");
119: }
120: input.skipBytes(headersize - readcount);
121:
122: if (bigendian != BIG_ENDIAN_
123: || charset != CHAR_SET_
124: || charsize != CHAR_SIZE_
125: || !Arrays.equals(dataFormatIDExpected, dataFormatID)
126: || (authenticate != null && !authenticate
127: .isDataVersionAcceptable(dataVersion))) {
128: throw new IOException(HEADER_AUTHENTICATION_FAILED_);
129: }
130: return unicodeVersion;
131: }
132:
133: // private variables -------------------------------------------------
134:
135: /**
136: * Magic numbers to authenticate the data file
137: */
138: private static final byte MAGIC1 = (byte) 0xda;
139: private static final byte MAGIC2 = (byte) 0x27;
140:
141: /**
142: * File format authentication values
143: */
144: private static final byte BIG_ENDIAN_ = 1;
145: private static final byte CHAR_SET_ = 0;
146: private static final byte CHAR_SIZE_ = 2;
147:
148: /**
149: * Error messages
150: */
151: private static final String MAGIC_NUMBER_AUTHENTICATION_FAILED_ = "ICU data file error: Not an ICU data file";
152: private static final String HEADER_AUTHENTICATION_FAILED_ = "ICU data file error: Header authentication failed, please check if you have a valid ICU data file";
153: }
|