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.xml;
23  
24  import java.io.*;
25  import java.net.URL;
26  
27  import javax.xml.parsers.DocumentBuilder;
28  import javax.xml.parsers.DocumentBuilderFactory;
29  import javax.xml.parsers.ParserConfigurationException;
30  import javax.xml.transform.*;
31  import javax.xml.transform.dom.DOMSource;
32  import javax.xml.transform.stream.StreamResult;
33  
34  import org.w3c.dom.CDATASection;
35  import org.w3c.dom.Document;
36  import org.w3c.dom.Element;
37  
38  import com.gcapmedia.dab.epg.*;
39  
40  /***
41   * Marshalls an EPG to/from XML
42   */
43  public class XmlMarshaller implements Marshaller {
44  
45  	/***
46  	 * Serial version
47  	 */
48  	private static final long serialVersionUID = 3823353646420716647L;
49  
50  	//private static final String EPG_NAMESPACE = "http://www.worlddab.org/schemas/epg";
51  	
52  	//private static final String XSI = "http://www.w3.org/2001/XMLSchema-instance";
53  	
54  	//private static final String EPG_SCHEMA_LOCATION = "http://www.worlddab.org/schemas/epg/epgSchedule_13.xsd";
55  	
56  	/***
57  	 * @see com.gcapmedia.dab.epg.Marshaller#marshall(com.gcapmedia.dab.epg.Epg)
58  	 */
59  	public byte[] marshall(Epg epg) throws MarshallException {
60  		
61  		byte[] bytes = null;
62  				
63          // build the document
64          Document doc = buildDocument(epg);
65          
66          // write the document to the byte array
67          try {
68              // Prepare the DOM document for writing
69              Source source = new DOMSource(doc);
70                 
71              // write the document to a byte array
72      		StringWriter writer = new StringWriter();
73      		TransformerFactory factory = TransformerFactory.newInstance();
74      		factory.setAttribute("indent-number", new Integer(4));
75      		Transformer transformer = factory.newTransformer();
76      		StreamResult result = new StreamResult(writer);
77      		transformer.setOutputProperty(OutputKeys.INDENT, "yes");
78      		transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
79      		transformer.transform(source, result);
80      		String markup = writer.getBuffer().toString();
81              bytes = markup.getBytes();
82              
83          } catch (Exception e) {
84          	throw new MarshallException("Error marshalling XML data", e);
85          }
86  		
87          return bytes;
88  	}
89  	
90  	/***
91  	 * Marshalls an EPG to an XML string adherent to the ISO/IEC 10646 character
92  	 * set and using UTF-8 encoding.
93  	 * @throws UnsupportedEncodingException 
94  	 * @throws MarshallException 
95  	 */
96  	public String marshallToXmlString(Epg epg) throws UnsupportedEncodingException, MarshallException {
97  		return new String(marshall(epg), "ISO-8859-2");
98  	}
99  
100 	/***
101 	 * @see com.gcapmedia.dab.epg.Marshaller#unmarshall(byte[])
102 	 */
103 	public Epg unmarshall(byte[] bytes) {
104 		// TODO Auto-generated method stub
105 		return null;
106 	}
107 	
108 	/***
109 	 * Unmarshall a document located at the specified URL. This should use the
110 	 * ISO/IEC 10646 character set and be encoded in UTF-8.
111 	 * @throws IOException 
112 	 */
113 	public Epg unmarshallFromUrl(URL url) throws IOException {
114 		BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
115 		String tmp = null;
116 		StringBuilder buffer = new StringBuilder();
117 		while((tmp = reader.readLine()) != null) {
118 			buffer.append(tmp);
119 		}
120 		return unmarshall(buffer.toString().getBytes());
121 	}
122 	
123 	/***
124 	 * Unmarshall an EPG from a file. This should use the
125 	 * ISO/IEC 10646 character set and be encoded in UTF-8.
126 	 * @throws IOException 
127 	 */
128 	public Epg unmarshallFromFile(File file) throws IOException {
129 		return unmarshallFromUrl(file.toURL());
130 	}
131 
132 	/***
133 	 * @param epg
134 	 * @return
135 	 */
136 	private Document buildDocument(Epg epg) {
137 		
138 		// create the document
139 		DocumentBuilder builder = null;
140 		try {
141 			builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
142 		} catch (ParserConfigurationException e1) {
143 			// TODO Auto-generated catch block
144 			e1.printStackTrace();
145 		}
146 	    Document doc = builder.newDocument();
147 	    
148 	    
149 	    // EPG tag
150 	    Element epgElement = doc.createElement("epg");
151 	    doc.appendChild(epgElement);
152 	    epgElement.setAttribute("system", epg.getSystemType().name());
153 	    
154 	    // build a schedule
155 	    if(epg.getSchedule() != null) {
156 	    	Schedule schedule = epg.getSchedule();
157 	    	Element scheduleElement = buildSchedule(schedule, epgElement, doc);
158 		    epgElement.appendChild(scheduleElement);
159 	    }
160 	    
161 	    return doc;
162 	}
163 
164 	/***
165 	 * builds the <schedule> element
166 	 */
167 	private Element buildSchedule(Schedule schedule, Element parent, Document doc) {
168 		Element scheduleElement = doc.createElement("schedule");
169 	    if(schedule.getVersion() > 0) {
170 	    	scheduleElement.setAttribute("version", "" + schedule.getVersion());
171 	    }
172 	    if(schedule.getCreated() != null) {
173 	    	scheduleElement.setAttribute("creationTime", schedule.getCreated().toString());
174 	    }
175 	    if(schedule.getOriginator() != null) {
176 	    	scheduleElement.setAttribute("originator", schedule.getOriginator());
177 	    }
178 	    
179 	    // schedule scope
180 	    if(schedule.getScope() != null) {
181 	    	Scope scope = schedule.getScope();
182 	    	Element scopeElement = buildScope(scope, scheduleElement, doc);
183 	    	scheduleElement.appendChild(scopeElement);
184 	    }
185 	    
186 	    // programmes
187 	    for(Programme programme : schedule.getProgrammes()) {
188 	    	Element programmeElement = buildProgramme(programme, scheduleElement, doc);
189 	    	scheduleElement.appendChild(programmeElement);
190 	    }
191 	    
192 	    return scheduleElement;
193 	}
194 
195 	/***
196 	 * builds a <programme> element
197 	 */
198 	private Element buildProgramme(Programme programme, Element scheduleElement, Document doc) {
199 		
200 		// programme element
201 		Element programmeElement = doc.createElement("programme");
202 		programmeElement.setAttribute("shortId", "" + programme.getShortId());
203 		if(programme.getId() != null) {
204 			programmeElement.setAttribute("id", programme.getId().toString());
205 		}
206 		if(programme.getRecommendation() != null && programme.getRecommendation() != Recommendation.NO) {
207 			programmeElement.setAttribute("recommendation", programme.getRecommendation().value());
208 		}
209 		if(programme.getBroadcastFlag() != null && programme.getBroadcastFlag() != BroadcastFlag.ON_AIR) {
210 			programmeElement.setAttribute("broadcast", programme.getBroadcastFlag().value());
211 		}
212 		
213 		// name groups
214 		NameGroup names = programme.getNames();
215 		for(ShortName shortName : names.getShortNames()) {
216 			Element shortNameElement = doc.createElement("shortName");
217 			shortNameElement.setAttribute("xml:lang", shortName.getLanguage());
218 			shortNameElement.setTextContent(shortName.getText());
219 			programmeElement.appendChild(shortNameElement);
220 		}
221 		for(MediumName mediumName : names.getMediumNames()) {
222 			Element mediumNameElement = doc.createElement("mediumName");
223 			mediumNameElement.setAttribute("xml:lang", mediumName.getLanguage());
224 			mediumNameElement.setTextContent(mediumName.getText());
225 			programmeElement.appendChild(mediumNameElement);
226 		}
227 		for(LongName longName : names.getLongNames()) {
228 			Element longNameElement = doc.createElement("longName");
229 			longNameElement.setAttribute("xml:lang", longName.getLanguage());
230 			longNameElement.setTextContent(longName.getText());
231 			programmeElement.appendChild(longNameElement);
232 		}
233 		
234 		// programme locations
235 		for(Location location : programme.getLocations()) {
236 			Element locationElement = buildLocation(location, programmeElement, doc);
237 			programmeElement.appendChild(locationElement);
238 		}
239 		
240 		// media descriptions
241 		MediaGroup media = programme.getMedia();
242 		Element mediaElement = buildMedia(media, doc);
243 		programmeElement.appendChild(mediaElement);
244 		
245 		// genres
246 		for(Genre genre : programme.getGenres()) {
247 			Element genreElement = buildGenre(genre, programmeElement, doc);
248 			programmeElement.appendChild(genreElement);
249 		}
250 		
251 		// memberships
252 		for(Membership membership : programme.getMemberships()) {
253 			Element membershipElement = buildMembership(membership, programmeElement, doc);
254 			programmeElement.appendChild(membershipElement);
255 		}
256 		
257 		// links
258 		for(Link link : programme.getLinks()) {
259 			Element linkElement = buildLink(link, programmeElement, doc);
260 			programmeElement.appendChild(linkElement);
261 		}
262 		
263 		// programme events
264 		for(ProgrammeEvent event : programme.getEvents()) {
265 			Element eventElement = buildEvent(event, programmeElement, doc);
266 			programmeElement.appendChild(eventElement);
267 		}
268 		
269 		return programmeElement;
270 	}
271 
272 	/***
273 	 * builds the <programmeEvent> element
274 	 */
275 	private Element buildEvent(ProgrammeEvent event, Element programmeElement, Document doc) {
276 		Element eventElement = doc.createElement("programmeEvent");
277 		
278 		eventElement.setAttribute("shortId", "" + event.getShortId());
279 		if(event.getId() != null) {
280 			eventElement.setAttribute("id", event.getId().toString());
281 		}
282 		if(event.getRecommendation() != null) {
283 			eventElement.setAttribute("recommendation", event.getRecommendation().value());
284 		}
285 		
286 		// name groups
287 		NameGroup names = event.getNames();
288 		for(ShortName shortName : names.getShortNames()) {
289 			Element shortNameElement = doc.createElement("shortName");
290 			shortNameElement.setAttribute("xml:lang", shortName.getLanguage());
291 			shortNameElement.setTextContent(shortName.getText());
292 			eventElement.appendChild(shortNameElement);
293 		}
294 		for(MediumName mediumName : names.getMediumNames()) {
295 			Element mediumNameElement = doc.createElement("mediumName");
296 			mediumNameElement.setAttribute("xml:lang", mediumName.getLanguage());
297 			mediumNameElement.setTextContent(mediumName.getText());
298 			eventElement.appendChild(mediumNameElement);
299 		}
300 		for(LongName longName : names.getLongNames()) {
301 			Element longNameElement = doc.createElement("longName");
302 			longNameElement.setAttribute("xml:lang", longName.getLanguage());
303 			longNameElement.setTextContent(longName.getText());
304 			eventElement.appendChild(longNameElement);
305 		}
306 		
307 		// programme locations
308 		for(Location location : event.getLocations()) {
309 			Element locationElement = buildLocation(location, programmeElement, doc);
310 			eventElement.appendChild(locationElement);
311 		}
312 		
313 		// media descriptions
314 		MediaGroup media = event.getMedia();
315 		Element mediaElement = buildMedia(media, doc);
316 		eventElement.appendChild(mediaElement);
317 		
318 		
319 		// genres
320 		for(Genre genre : event.getGenres()) {
321 			Element genreElement = buildGenre(genre, programmeElement, doc);
322 			eventElement.appendChild(genreElement);
323 		}
324 		
325 		// memberships
326 		for(Membership membership : event.getMemberships()) {
327 			Element membershipElement = buildMembership(membership, programmeElement, doc);
328 			eventElement.appendChild(membershipElement);
329 		}
330 		
331 		// links
332 		for(Link link : event.getLinks()) {
333 			Element linkElement = buildLink(link, programmeElement, doc);
334 			eventElement.appendChild(linkElement);
335 		}
336 		
337 		return eventElement;
338 	}
339 
340 	/***
341 	 * builds the <link> element
342 	 */
343 	private Element buildLink(Link link, Element programmeElement, Document doc) {
344 		Element linkElement = doc.createElement("link");
345 		linkElement.setAttribute("url", link.getUrl().toString());
346 		linkElement.setAttribute("description", link.getDescription());
347 		return linkElement;
348 	}
349 
350 	/***
351 	 * builds the <memberOf> element
352 	 */
353 	private Element buildMembership(Membership membership, Element programmeElement, Document doc) {
354 		Element memberOfElement = doc.createElement("memberOf");
355 		memberOfElement.setAttribute("shortId", "" + membership.getShortId());
356 		if(membership.getCrid() != null) {
357 			memberOfElement.setAttribute("id", membership.getCrid().toString());
358 		}
359 		if(membership.getIndex() > 0) {
360 			memberOfElement.setAttribute("shortId", "" + membership.getIndex());
361 		}
362 		return memberOfElement;
363 	}
364 
365 	/***
366 	 * builds the <genre> element
367 	 */
368 	private Element buildGenre(Genre genre, Element programmeElement, Document doc) {
369 		Element genreElement = doc.createElement("genre");
370 		genreElement.setAttribute("href", genre.toString());
371 		if(genre.getType() != null && genre.getType() != Genre.Type.MAIN) {
372 			genreElement.setAttribute("type", genre.getType().toString());
373 		}
374 		if(genre.getDefinition() != null) {
375 			Element nameElement = doc.createElement("doc");
376 			CDATASection data = doc.createCDATASection(genre.getDefinition());
377 			nameElement.appendChild(data);
378 			genreElement.appendChild(nameElement);
379 		}
380 		return genreElement;
381 	}
382 
383 	/***
384 	 * builds the <mediaDescription> element
385 	 */
386 	private Element buildMedia(MediaGroup media, Document doc) {
387 		
388 		Element mediaElement = doc.createElement("mediaDescription");
389 		
390 		// short descriptions
391 		for(ShortDescription shortDescription : media.getShortDescriptions()) {
392 			Element shortDescriptionElement = doc.createElement("shortDescription");
393 			shortDescriptionElement.setAttribute("xml:lang", shortDescription.getLanguage());
394 			CDATASection data = doc.createCDATASection(shortDescription.getText());
395 			shortDescriptionElement.appendChild(data);
396 			mediaElement.appendChild(shortDescriptionElement);
397 		}
398 		
399 		// long descriptions
400 		for(LongDescription longDescription : media.getLongDescriptions()) {
401 			Element longDescriptionElement = doc.createElement("longDescription");
402 			longDescriptionElement.setAttribute("xml:lang", longDescription.getLanguage());
403 			CDATASection data = doc.createCDATASection(longDescription.getText());
404 			longDescriptionElement.appendChild(data);
405 			mediaElement.appendChild(longDescriptionElement);
406 		}
407 		
408 		// multimedia
409 		for(Multimedia multimedia : media.getMultimedia()) {
410 			Element multimediaElement = doc.createElement("multimedia");
411 			multimediaElement.setAttribute("url", multimedia.getUrl().toString());
412 			if(multimedia.getType() != null) {
413 				multimediaElement.setAttribute("type", multimedia.getType().toString());
414 			}
415 			if(multimedia.getMimeType() != null) {
416 				multimediaElement.setAttribute("mimeValue", multimedia.getMimeType());
417 			}
418 			if(multimedia.getHeight() > 0) {
419 				multimediaElement.setAttribute("height", "" + multimedia.getHeight());
420 			}
421 			if(multimedia.getWidth() > 0) {
422 				multimediaElement.setAttribute("width", "" + multimedia.getWidth());
423 			}
424 			mediaElement.appendChild(multimediaElement);
425 		}
426 		
427 		return mediaElement;
428 	}
429 
430 	/***
431 	 * builds the <location> element
432 	 */
433 	private Element buildLocation(Location location, Element parent, Document doc) {
434 		Element locationElement = doc.createElement("location");
435 		
436 		// times
437 		for(Time time : location.getTimes()) {
438 			Element timeElement = doc.createElement("time");
439 			timeElement.setAttribute("time", time.getBilledTime().toString());
440 			timeElement.setAttribute("duration", time.getBilledDuration().toString());
441 			if(time.getActualTime() != null) {
442 				timeElement.setAttribute("actualTime", time.getActualTime().toString());
443 			}
444 			if(time.getActualDuration() != null) {
445 				timeElement.setAttribute("actualDuration", time.getActualDuration().toString());
446 			}
447 			locationElement.appendChild(timeElement);
448 		}
449 		
450 		// relative times
451 		for(RelativeTime time : location.getRelativeTimes()) {
452 			Element timeElement = doc.createElement("relativeTime");
453 			timeElement.setAttribute("time", time.getBilledTime().toString());
454 			timeElement.setAttribute("duration", time.getBilledDuration().toString());
455 			if(time.getActualTime() != null) {
456 				timeElement.setAttribute("actualTime", time.getActualTime().toString());
457 			}
458 			if(time.getActualDuration() != null) {
459 				timeElement.setAttribute("actualDuration", time.getActualDuration().toString());
460 			}
461 			locationElement.appendChild(timeElement);
462 		}
463 		
464 		// bearers
465 		for(Bearer bearer : location.getBearers()) {
466 			Element bearerElement = doc.createElement("bearer");
467 			bearerElement.setAttribute("id", bearer.getId().toString());
468 			locationElement.appendChild(bearerElement);
469 		}
470 		
471 		return locationElement;
472 	}
473 
474 	/***
475 	 * builds the <scope> element
476 	 */
477 	private Element buildScope(Scope scope, Element parent, Document doc) {
478 		Element scopeElement = doc.createElement("scope");
479 		scopeElement.setAttribute("startTime", scope.getStartTime().toString());
480 		scopeElement.setAttribute("stopTime", scope.getStopTime().toString());
481 		for(ContentId id : scope.getServices()) {
482 			Element serviceElement = doc.createElement("serviceScope");
483 			serviceElement.setAttribute("id", id.toString());
484 			scopeElement.appendChild(serviceElement);
485 		}
486 		return scopeElement;
487 	}
488 
489 }