1/*
2 * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 *
25 * THIS FILE WAS MODIFIED BY SUN MICROSYSTEMS, INC.
26 */
27
28package com.sun.xml.internal.fastinfoset.algorithm;
29
30
31import java.io.EOFException;
32import java.io.IOException;
33import java.io.InputStream;
34import java.io.OutputStream;
35import java.nio.CharBuffer;
36import java.util.ArrayList;
37import java.util.List;
38
39import com.sun.xml.internal.org.jvnet.fastinfoset.EncodingAlgorithmException;
40import com.sun.xml.internal.fastinfoset.CommonResourceBundle;
41
42
43/**
44 *
45 * An encoder for handling boolean values.  Suppports the builtin BOOLEAN encoder.
46 *
47 * @author Alan Hudson
48 * @author Paul Sandoz
49 *
50 */
51public class BooleanEncodingAlgorithm extends BuiltInEncodingAlgorithm {
52
53    /** Table for setting a particular bit of a byte */
54    private static final int[] BIT_TABLE = {
55        1 << 7,
56        1 << 6,
57        1 << 5,
58        1 << 4,
59        1 << 3,
60        1 << 2,
61        1 << 1,
62        1 << 0};
63
64    public int getPrimtiveLengthFromOctetLength(int octetLength) throws EncodingAlgorithmException {
65        // Cannot determine the number of boolean values from just the octet length
66        throw new UnsupportedOperationException();
67    }
68
69    public int getOctetLengthFromPrimitiveLength(int primitiveLength) {
70        if (primitiveLength < 5) {
71            return 1;
72        } else {
73            final int div = primitiveLength / 8;
74            return (div == 0) ? 2 : 1 + div;
75        }
76    }
77
78    public final Object decodeFromBytes(byte[] b, int start, int length) throws EncodingAlgorithmException {
79        final int blength = getPrimtiveLengthFromOctetLength(length, b[start]);
80        boolean[] data = new boolean[blength];
81
82        decodeFromBytesToBooleanArray(data, 0, blength, b, start, length);
83        return data;
84    }
85
86    public final Object decodeFromInputStream(InputStream s) throws IOException {
87        final List booleanList = new ArrayList();
88
89        int value = s.read();
90        if (value == -1) {
91            throw new EOFException();
92        }
93        final int unusedBits = (value >> 4) & 0xFF;
94
95        int bitPosition = 4;
96        int bitPositionEnd = 8;
97        int valueNext = 0;
98        do {
99            valueNext = s.read();
100            if (valueNext == -1) {
101                bitPositionEnd -= unusedBits;
102            }
103
104            while(bitPosition < bitPositionEnd) {
105                booleanList.add(
106                        Boolean.valueOf((value & BIT_TABLE[bitPosition++]) > 0));
107            }
108
109            value = valueNext;
110        } while (value != -1);
111
112        return generateArrayFromList(booleanList);
113    }
114
115    public void encodeToOutputStream(Object data, OutputStream s) throws IOException {
116        if (!(data instanceof boolean[])) {
117            throw new IllegalArgumentException(CommonResourceBundle.getInstance().getString("message.dataNotBoolean"));
118        }
119
120        boolean array[] = (boolean[])data;
121        final int alength = array.length;
122
123        final int mod = (alength + 4) % 8;
124        final int unusedBits = (mod == 0) ? 0 : 8 - mod;
125
126        int bitPosition = 4;
127        int value = unusedBits << 4;
128        int astart = 0;
129        while (astart < alength) {
130            if (array[astart++]) {
131                value |= BIT_TABLE[bitPosition];
132            }
133
134            if (++bitPosition == 8) {
135                s.write(value);
136                bitPosition = value = 0;
137            }
138        }
139
140        if (bitPosition != 8) {
141            s.write(value);
142        }
143    }
144
145    public final Object convertFromCharacters(char[] ch, int start, int length) {
146        if (length == 0) {
147            return new boolean[0];
148        }
149
150        final CharBuffer cb = CharBuffer.wrap(ch, start, length);
151        final List booleanList = new ArrayList();
152
153        matchWhiteSpaceDelimnatedWords(cb,
154            new WordListener() {
155                public void word(int start, int end) {
156                    if (cb.charAt(start) == 't') {
157                        booleanList.add(Boolean.TRUE);
158                    } else {
159                        booleanList.add(Boolean.FALSE);
160                    }
161                }
162            }
163        );
164
165        return generateArrayFromList(booleanList);
166    }
167
168    public final void convertToCharacters(Object data, StringBuffer s) {
169        if (data == null) {
170            return;
171        }
172
173        final boolean[] value = (boolean[]) data;
174        if (value.length == 0) {
175            return;
176        }
177
178        // Insure conservately as all false
179        s.ensureCapacity(value.length * 5);
180
181        final int end = value.length - 1;
182        for (int i = 0; i <= end; i++) {
183            if (value[i]) {
184                s.append("true");
185            } else {
186                s.append("false");
187            }
188            if (i != end) {
189                s.append(' ');
190            }
191        }
192    }
193
194    public int getPrimtiveLengthFromOctetLength(int octetLength, int firstOctet) throws EncodingAlgorithmException {
195        final int unusedBits = (firstOctet >> 4) & 0xFF;
196        if (octetLength == 1) {
197           if (unusedBits > 3) {
198               throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.unusedBits4"));
199           }
200           return 4 - unusedBits;
201        } else {
202           if (unusedBits > 7) {
203               throw new EncodingAlgorithmException(CommonResourceBundle.getInstance().getString("message.unusedBits8"));
204           }
205           return octetLength * 8 - 4 - unusedBits;
206        }
207    }
208
209    public final void decodeFromBytesToBooleanArray(boolean[] bdata, int bstart, int blength, byte[] b, int start, int length) {
210        int value = b[start++] & 0xFF;
211        int bitPosition = 4;
212        final int bend = bstart + blength;
213        while (bstart < bend) {
214            if (bitPosition == 8) {
215                value = b[start++] & 0xFF;
216                bitPosition = 0;
217            }
218
219            bdata[bstart++] = (value & BIT_TABLE[bitPosition++]) > 0;
220        }
221    }
222
223    public void encodeToBytes(Object array, int astart, int alength, byte[] b, int start) {
224        if (!(array instanceof boolean[])) {
225            throw new IllegalArgumentException(CommonResourceBundle.getInstance().getString("message.dataNotBoolean"));
226        }
227
228        encodeToBytesFromBooleanArray((boolean[])array, astart, alength, b, start);
229    }
230
231    public void encodeToBytesFromBooleanArray(boolean[] array, int astart, int alength, byte[] b, int start) {
232        final int mod = (alength + 4) % 8;
233        final int unusedBits = (mod == 0) ? 0 : 8 - mod;
234
235        int bitPosition = 4;
236        int value = unusedBits << 4;
237        final int aend = astart + alength;
238        while (astart < aend) {
239            if (array[astart++]) {
240                value |= BIT_TABLE[bitPosition];
241            }
242
243            if (++bitPosition == 8) {
244                b[start++] = (byte)value;
245                bitPosition = value = 0;
246            }
247        }
248
249        if (bitPosition > 0) {
250            b[start] = (byte)value;
251        }
252    }
253
254
255    /**
256     *
257     * Generate a boolean array from a list of Booleans.
258     *
259     * @param array The array
260     *
261     */
262    private boolean[] generateArrayFromList(List array) {
263        boolean[] bdata = new boolean[array.size()];
264        for (int i = 0; i < bdata.length; i++) {
265            bdata[i] = ((Boolean)array.get(i)).booleanValue();
266        }
267
268        return bdata;
269    }
270
271}
272