1 /***
2 * Java DAB EPG API - Serialize/Deserialize To/From POJOs to XML/Binary as per
3 * ETSI specifications TS 102 818 (XML Specification for DAB EPG) and TS 102
4 * 371 (Transportation and Binary Encoding Specification for EPG).
5 *
6 * Copyright (C) 2007 GCap Media PLC
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22 package com.gcapmedia.dab.epg.binary;
23
24 import org.apache.commons.lang.StringUtils;
25
26 /***
27 *
28 */
29 public class BitBuilder {
30
31 /***
32 * Delegate bitset
33 */
34 private StringBuilder bits;
35
36 /***
37 * Size of the entire construct
38 */
39 private int size;
40
41 /***
42 * Position is marked as after the position of last insert
43 */
44 private int position;
45
46 /***
47 * Create a new bithelper
48 * @param size
49 */
50 public BitBuilder(int size) {
51 if(size < 1) {
52 throw new IllegalArgumentException("Size must be greater than zero");
53 }
54 this.size = size;
55 bits = new StringBuilder(size);
56 bits.replace(0, size - 1, StringUtils.repeat("0", size));
57 }
58
59 /***
60 * Set a new size for the result
61 * @param size
62 */
63 public void setSize(int size) {
64 this.size = size;
65 if(bits.length() > size) {
66 bits.setLength(size);
67 bits.trimToSize();
68 } else if(bits.length() < size) {
69 bits.replace(0, size - 1, StringUtils.rightPad(bits.toString(), size, '0'));
70 }
71 }
72
73 /***
74 * Returns the size of the construct
75 * @return the size of the construct
76 */
77 public int size() {
78 return size;
79 }
80
81 /***
82 * Returns the value of the bit with the specified index
83 * @param index the bit index
84 * @return the value of the bit with the specified index
85 */
86 public boolean get(int index) {
87 return bits.charAt(index) == '1';
88 }
89
90 /***
91 *
92 * @param index
93 * @param length
94 * @param value
95 */
96 public BitBuilder put(int index, int length, long value) {
97 if(index + length - 1 > size) {
98 throw new IndexOutOfBoundsException("Inserted value out of range: " + index + " + " + length + " > " + size);
99 }
100 String binary = StringUtils.leftPad(Long.toBinaryString(value), length, '0');
101 if(binary.length() > length) {
102 throw new IndexOutOfBoundsException("Resultant binary exceeds the desired length: " + binary.length() + " > " + length + " : " + binary + " [value=" + value + "]");
103 }
104 bits.replace(index, index + length, binary);
105 position = index + length;
106 return this;
107 }
108
109 public BitBuilder put(int index, byte bite) {
110 return put(index, 8, bite & 0xFF);
111 }
112
113 public BitBuilder put(int index, boolean flag) {
114 if(index >= size) {
115 throw new IndexOutOfBoundsException("Requested index is beyond the bounds of the construct: " + index + " >= " + size);
116 }
117 bits.replace(index, index + 1, flag ? "1" : "0");
118 position = index + 1;
119 return this;
120 }
121
122 public BitBuilder put(int index, int length, byte[] bytes) {
123 if(index + length - 1 > size) {
124 throw new IndexOutOfBoundsException("Inserted value out of range: " + index + " + " + length + " > " + size);
125 }
126 if(index + bytes.length * 8 - 1 > size) {
127 throw new IndexOutOfBoundsException("Inserted byte length out of range: " + index + " + " + (bytes.length * 8) + " > " + size);
128 }
129 if(bytes.length * 8 < length) {
130 throw new IllegalArgumentException("Byte length exceeds the desired insert length: " + (bytes.length * 8) + " > " + length);
131 }
132 int count = 0;
133 for(byte bite : bytes) {
134 String binary = StringUtils.leftPad(Integer.toBinaryString(bite & 0xFF), 8, '0');
135 bits.replace(index + count, index + count + 8, binary);
136 count += 8;
137 }
138 position = index + length;
139 return this;
140 }
141
142 /***
143 * @return Returns the contents of the builder as a byte array
144 */
145 public byte[] toByteArray() {
146
147
148 String binary = bits.toString();
149
150 if(binary.length() % 8 > 0) {
151 int rounded = binary.length() + (8 - size % 8);
152 binary = StringUtils.leftPad(binary, rounded, '0');
153 }
154
155 byte[] bytes = new byte[size / 8];
156 for(int i = 0; i < size; i += 8) {
157 int num = Integer.parseInt(binary.substring(i, i + 8), 2);
158 bytes[i / 8] = (byte)num;
159 }
160
161 return bytes;
162 }
163
164 /***
165 * Returns a byte array as printable output
166 * @param bytes Byte array to print
167 */
168 public static String printByteArray(byte[] bytes) {
169 StringBuilder buf = new StringBuilder();
170 for(byte bite : bytes) {
171 buf.append(StringUtils.leftPad(Integer.toHexString((int)bite & 0xFF), 2, '0') + " ");
172 }
173 return buf.toString();
174 }
175
176 /***
177 * @return Returns the position marker as the index after the last inserted
178 * position
179 */
180 public int position() {
181 return position;
182 }
183
184 /***
185 * @see java.lang.Object#toString()
186 */
187 public String toString() {
188 return "BitBuilder [" + size() + "]: " + bits.toString();
189 }
190
191 }