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
82 bits.put(0, 8, tag.getBytes());
83
84
85
86
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
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
136 AttributeTag tag = AttributeTag.fromBytes(parent, bytes);
137
138
139 int length = parser.getInt(8, 8);
140 int position = 16;
141 if(length == 0xFE) {
142
143 length = parser.getInt(16, 16);
144 position = 32;
145 } else if(length == 0xFE) {
146
147 length = parser.getInt(16, 24);
148 position = 40;
149 }
150
151
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
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 }