View Javadoc

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     	// pad to an even number of bytes
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 }