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  /***
25   * Attribute binary encoding
26   */
27  public class Attribute implements Encodable {
28  
29  	/***
30  	 * Element tag uniquely identifies the element.
31  	 */
32  	private AttributeTag tag;
33  		
34  	/***
35  	 * Contains either a string (4.5.1) or an enumerated data value
36  	 * (4.6) or a common data type (4.7)
37  	 */
38  	private Encodable value;
39  	
40  	/***
41  	 * Create a new attribute
42  	 * @param tag Attribute tag
43  	 * @param data Attribute value
44  	 */
45  	public Attribute(AttributeTag tag, Encodable value) {
46  		if(tag == null) {
47  			throw new IllegalArgumentException("Must specify a tag for this attribute");
48  		}
49  		this.tag = tag;
50  		if(value == null) {
51  			throw new IllegalArgumentException("Must specify a value for this attribute: " + this.getTag());
52  		}
53  		this.value = value;
54  	}
55  	
56  	/***
57  	 * @return Returns the attribute tag
58  	 */
59  	public AttributeTag getTag() {
60  		return tag;
61  	}
62  	
63  	/***
64  	 * @return Returns the attribute value
65  	 */
66  	public Encodable getValue() {
67  		return value;
68  	}
69  
70  	/***
71  	 * @see com.gcapmedia.dab.epg.binary.Encodable#getBytes()
72  	 */
73  	public byte[] getBytes() {
74  		if(value == null) {
75  			throw new IllegalStateException("Bytes requested when value is null");
76  		}
77  		
78  		
79  		BitBuilder bits = new BitBuilder(8);
80  		
81  		// b0-b7: attribute tag
82  		bits.put(0, 8, tag.getBytes());
83  		
84  		// b8-15: attribute data length (0-253 bytes)
85  		// b16-31: extended attribute length (256-65536 bytes)
86  		// b16-39: extended attribute length (65537-16777216 bytes)
87  		byte[] databytes = value.getBytes();
88  		int datalength = databytes.length;
89  		if(datalength <= 253) {
90  			bits.setSize(16 + datalength * 8);
91  			bits.put(8, 8, datalength);
92  		} else if(datalength >= 1<<8 && datalength <= 1<<16) {
93  			bits.setSize(32 + datalength * 8);
94  			bits.put(8, 8, 0xFE);
95  			bits.put(16, 16, datalength);
96  		} else if(datalength > 1<<16 && datalength <= 1<<24) { 
97  			bits.setSize(40 + datalength * 8);
98  			bits.put(8, 8, 0xFF);
99  			bits.put(16, 24, datalength);
100 		} else {
101 			throw new IndexOutOfBoundsException("Attribute data length exceeds the maximum allowed by the extended attribute length (24bits): " + datalength + " > " + (1<<24));
102 		}
103 		
104 		// b16/32/40-(16/32/40 + bytes*8): attribute data bytes
105 		bits.put(bits.position(), databytes.length * 8, databytes);
106 		
107 		return bits.toByteArray();
108 	}
109 
110 	/***
111 	 * @see com.gcapmedia.dab.epg.binary.Encodable#getLength()
112 	 */
113 	public int getLength() {
114 		return getBytes().length;
115 	}
116 	
117 	/***
118 	 * Parse an object from its byte array representation
119 	 * @param parent Tag parent
120 	 * @param bytes Byte array representation
121 	 */
122 	public static Attribute fromBytes(ElementTag parent, byte[] bytes) {
123 		return fromBytes(parent, bytes, null);
124 	}
125 	
126 	/***
127 	 * Parse an object from its byte array representation
128 	 * @param parent Tag parent
129 	 * @param bytes Byte array representation
130 	 */
131 	public static Attribute fromBytes(ElementTag parent, byte[] bytes, TokenTable tokens) {
132 		
133 		BitParser parser = new BitParser(bytes);
134 		
135 		// b0-7: attribute tag
136 		AttributeTag tag = AttributeTag.fromBytes(parent, bytes);
137 		
138 		// b8-15: attribute length
139 		int length = parser.getInt(8, 8);
140 		int position = 16;
141 		if(length == 0xFE) {
142 			// b16-31: extended attribute length (16bits)
143 			length = parser.getInt(16, 16);
144 			position = 32;
145 		} else if(length == 0xFE) {
146 			// b16-39: extended attribute length (24bits)
147 			length = parser.getInt(16, 24);
148 			position = 40;
149 		}
150 		
151 		// b16/32/40: attribute value data
152 		Encodable encodable = null;
153 		byte[] data = parser.getByteArray(position, bytes.length * 8 - position);
154 		if(tag == AttributeTag.epgSystem ||
155 		   tag == AttributeTag.genreType ||
156 		   tag == AttributeTag.programmeBroadcast ||
157 		   tag == AttributeTag.programmeRecommendation) {
158 		    encodable = EnumType.fromByte(tag, data[0]);
159 		} else if(tag == AttributeTag.scopeStartTime || 
160 				  tag == AttributeTag.scopeStopTime ||
161 				  tag == AttributeTag.timeActualTime ||
162 				  tag == AttributeTag.timeTime ||
163 				  tag == AttributeTag.scheduleCreationTime) {	
164 			encodable = TimepointType.fromBytes(data);
165 		} else if(tag == AttributeTag.timeActualDuration ||
166 				  tag == AttributeTag.timeDuration) { 
167 			encodable = DurationType.fromBytes(data);
168 		} else if(tag == AttributeTag.shortNameLang ||
169 				  tag == AttributeTag.shortDescriptionLang ||
170 				  tag == AttributeTag.mediumNameLang ||
171 				  tag == AttributeTag.longNameLang ||
172 				  tag == AttributeTag.longDescriptionLang) {
173 			encodable = StringType.fromBytes(data);
174 		} else if(tag == AttributeTag.serviceScopeId ||
175 				  tag == AttributeTag.bearerId) {
176 			encodable = ContentIdType.fromBytes(data);
177 		} else if(tag == AttributeTag.programmeShortId) {
178 			encodable = ShortCridType.fromBytes(data);
179 		} else if(tag == AttributeTag.programmeVersion ||
180 				  tag == AttributeTag.programmeEventVersion ||
181 				  tag == AttributeTag.serviceInformationVersion ||
182 				  tag == AttributeTag.ensembleVersion ||
183 				  tag == AttributeTag.serviceVersion ||
184 				  tag == AttributeTag.programmeGroupsVersion ||
185 				  tag == AttributeTag.programmeGroupVersion ||
186 				  tag == AttributeTag.scheduleVersion) {
187 			encodable = VersionType.fromBytes(data);
188 		} else if(tag == AttributeTag.genreHref) {
189 			encodable = GenreType.fromBytes(data);
190 		} else {
191 			throw new IllegalArgumentException("Deconstruction of Attribute Tag " + tag + " has not yet been implemented");
192 			//encodable = StringType.fromBytes(data);
193 		}
194 		
195 		Attribute attribute = null;
196 		try {
197 			attribute = new Attribute(tag, encodable);
198 		} catch(Exception e) {
199 			throw new IllegalArgumentException("Error creating attribute from data: " + BitBuilder.printByteArray(data) + " [" + e.getMessage() + "]");
200 		}
201 		return attribute;
202 	}
203 	
204 	/***
205 	 * @see java.lang.Object#equals(java.lang.Object)
206 	 */
207 	@Override
208 	public boolean equals(Object obj) {
209 		if(!(obj instanceof Attribute)) {
210 			return false;
211 		}
212 		Attribute that = (Attribute)obj;		
213 		return this.tag == that.tag && this.value.equals(that.value);
214 	}
215 
216 	/***
217 	 * @see java.lang.Object#toString()
218 	 */
219 	public String toString() {
220 		return tag + "=" + value;
221 	}
222 }