View Javadoc
1   /*
2    * Copyright (C) 2013 4th Line GmbH, Switzerland
3    *
4    * The contents of this file are subject to the terms of either the GNU
5    * Lesser General Public License Version 2 or later ("LGPL") or the
6    * Common Development and Distribution License Version 1 or later
7    * ("CDDL") (collectively, the "License"). You may not use this file
8    * except in compliance with the License. See LICENSE.txt for more
9    * information.
10   *
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14   */
15  
16  package org.fourthline.cling.binding.xml;
17  
18  import org.fourthline.cling.binding.staging.MutableAction;
19  import org.fourthline.cling.binding.staging.MutableActionArgument;
20  import org.fourthline.cling.binding.staging.MutableAllowedValueRange;
21  import org.fourthline.cling.binding.staging.MutableService;
22  import org.fourthline.cling.binding.staging.MutableStateVariable;
23  import org.fourthline.cling.model.ValidationException;
24  import org.fourthline.cling.model.meta.ActionArgument;
25  import org.fourthline.cling.model.meta.Service;
26  import org.fourthline.cling.model.meta.StateVariableEventDetails;
27  import org.fourthline.cling.model.types.CustomDatatype;
28  import org.fourthline.cling.model.types.Datatype;
29  import org.seamless.xml.SAXParser;
30  import org.xml.sax.Attributes;
31  import org.xml.sax.InputSource;
32  import org.xml.sax.SAXException;
33  
34  import java.io.StringReader;
35  import java.util.ArrayList;
36  import java.util.List;
37  import java.util.Locale;
38  import java.util.logging.Logger;
39  
40  import static org.fourthline.cling.binding.xml.Descriptor.Service.ATTRIBUTE;
41  import static org.fourthline.cling.binding.xml.Descriptor.Service.ELEMENT;
42  
43  /**
44   * Implementation based on JAXP SAX.
45   *
46   * @author Christian Bauer
47   */
48  public class UDA10ServiceDescriptorBinderSAXImpl extends UDA10ServiceDescriptorBinderImpl {
49  
50      private static Logger log = Logger.getLogger(ServiceDescriptorBinder.class.getName());
51  
52      @Override
53      public <S extends Service> S describe(S undescribedService, String descriptorXml) throws DescriptorBindingException, ValidationException {
54  
55          if (descriptorXml == null || descriptorXml.length() == 0) {
56              throw new DescriptorBindingException("Null or empty descriptor");
57          }
58  
59          try {
60              log.fine("Reading service from XML descriptor");
61  
62              SAXParser parser = new SAXParser();
63  
64              MutableService descriptor = new MutableService();
65  
66              hydrateBasic(descriptor, undescribedService);
67  
68              new RootHandler(descriptor, parser);
69  
70              parser.parse(
71                      new InputSource(
72                              // TODO: UPNP VIOLATION: Virgin Media Superhub sends trailing spaces/newlines after last XML element, need to trim()
73                              new StringReader(descriptorXml.trim())
74                      )
75              );
76  
77              // Build the immutable descriptor graph
78              return (S)descriptor.build(undescribedService.getDevice());
79  
80          } catch (ValidationException ex) {
81              throw ex;
82          } catch (Exception ex) {
83              throw new DescriptorBindingException("Could not parse service descriptor: " + ex.toString(), ex);
84          }
85      }
86  
87      protected static class RootHandler extends ServiceDescriptorHandler<MutableService> {
88  
89          public RootHandler(MutableService instance, SAXParser parser) {
90              super(instance, parser);
91          }
92  
93          @Override
94          public void startElement(ELEMENT element, Attributes attributes) throws SAXException {
95  
96              /*
97              if (element.equals(SpecVersionHandler.EL)) {
98                  MutableUDAVersion udaVersion = new MutableUDAVersion();
99                  getInstance().udaVersion = udaVersion;
100                 new SpecVersionHandler(udaVersion, this);
101             }
102             */
103 
104             if (element.equals(ActionListHandler.EL)) {
105                 List<MutableAction> actions = new ArrayList<>();
106                 getInstance().actions = actions;
107                 new ActionListHandler(actions, this);
108             }
109 
110             if (element.equals(StateVariableListHandler.EL)) {
111                 List<MutableStateVariable> stateVariables = new ArrayList<>();
112                 getInstance().stateVariables = stateVariables;
113                 new StateVariableListHandler(stateVariables, this);
114             }
115 
116         }
117     }
118 
119     /*
120     protected static class SpecVersionHandler extends ServiceDescriptorHandler<MutableUDAVersion> {
121 
122         public static final ELEMENT EL = ELEMENT.specVersion;
123 
124         public SpecVersionHandler(MutableUDAVersion instance, ServiceDescriptorHandler parent) {
125             super(instance, parent);
126         }
127 
128         @Override
129         public void endElement(ELEMENT element) throws SAXException {
130             switch (element) {
131                 case major:
132                     getInstance().major = Integer.valueOf(getCharacters());
133                     break;
134                 case minor:
135                     getInstance().minor = Integer.valueOf(getCharacters());
136                     break;
137             }
138         }
139 
140         @Override
141         public boolean isLastElement(ELEMENT element) {
142             return element.equals(EL);
143         }
144     }
145     */
146 
147     protected static class ActionListHandler extends ServiceDescriptorHandler<List<MutableAction>> {
148 
149         public static final ELEMENT EL = ELEMENT.actionList;
150 
151         public ActionListHandler(List<MutableAction> instance, ServiceDescriptorHandler parent) {
152             super(instance, parent);
153         }
154 
155         @Override
156         public void startElement(ELEMENT element, Attributes attributes) throws SAXException {
157             if (element.equals(ActionHandler.EL)) {
158                 MutableAction action = new MutableAction();
159                 getInstance().add(action);
160                 new ActionHandler(action, this);
161             }
162         }
163 
164         @Override
165         public boolean isLastElement(ELEMENT element) {
166             return element.equals(EL);
167         }
168     }
169 
170     protected static class ActionHandler extends ServiceDescriptorHandler<MutableAction> {
171 
172         public static final ELEMENT EL = ELEMENT.action;
173 
174         public ActionHandler(MutableAction instance, ServiceDescriptorHandler parent) {
175             super(instance, parent);
176         }
177 
178         @Override
179         public void startElement(ELEMENT element, Attributes attributes) throws SAXException {
180             if (element.equals(ActionArgumentListHandler.EL)) {
181                 List<MutableActionArgument> arguments = new ArrayList<>();
182                 getInstance().arguments = arguments;
183                 new ActionArgumentListHandler(arguments, this);
184             }
185         }
186 
187         @Override
188         public void endElement(ELEMENT element) throws SAXException {
189             switch (element) {
190                 case name:
191                     getInstance().name = getCharacters();
192                     break;
193             }
194         }
195 
196         @Override
197         public boolean isLastElement(ELEMENT element) {
198             return element.equals(EL);
199         }
200     }
201 
202     protected static class ActionArgumentListHandler extends ServiceDescriptorHandler<List<MutableActionArgument>> {
203 
204         public static final ELEMENT EL = ELEMENT.argumentList;
205 
206         public ActionArgumentListHandler(List<MutableActionArgument> instance, ServiceDescriptorHandler parent) {
207             super(instance, parent);
208         }
209 
210         @Override
211         public void startElement(ELEMENT element, Attributes attributes) throws SAXException {
212             if (element.equals(ActionArgumentHandler.EL)) {
213                 MutableActionArgument argument = new MutableActionArgument();
214                 getInstance().add(argument);
215                 new ActionArgumentHandler(argument, this);
216             }
217         }
218 
219         @Override
220         public boolean isLastElement(ELEMENT element) {
221             return element.equals(EL);
222         }
223     }
224 
225     protected static class ActionArgumentHandler extends ServiceDescriptorHandler<MutableActionArgument> {
226 
227         public static final ELEMENT EL = ELEMENT.argument;
228 
229         public ActionArgumentHandler(MutableActionArgument instance, ServiceDescriptorHandler parent) {
230             super(instance, parent);
231         }
232 
233         @Override
234         public void endElement(ELEMENT element) throws SAXException {
235             switch (element) {
236                 case name:
237                     getInstance().name = getCharacters();
238                     break;
239                 case direction:
240                     String directionString = getCharacters();
241                     try {
242                         getInstance().direction = ActionArgument.Direction.valueOf(directionString.toUpperCase(Locale.ROOT));
243                     } catch (IllegalArgumentException ex) {
244                         // TODO: UPNP VIOLATION: Pelco SpectraIV-IP uses illegal value INOUT
245                         log.warning("UPnP specification violation: Invalid action argument direction, assuming 'IN': " + directionString);
246                         getInstance().direction = ActionArgument.Direction.IN;
247                     }
248                     break;
249                 case relatedStateVariable:
250                     getInstance().relatedStateVariable = getCharacters();
251                     break;
252                 case retval:
253                     getInstance().retval = true;
254                     break;
255             }
256         }
257 
258         @Override
259         public boolean isLastElement(ELEMENT element) {
260             return element.equals(EL);
261         }
262     }
263 
264     protected static class StateVariableListHandler extends ServiceDescriptorHandler<List<MutableStateVariable>> {
265 
266         public static final ELEMENT EL = ELEMENT.serviceStateTable;
267 
268         public StateVariableListHandler(List<MutableStateVariable> instance, ServiceDescriptorHandler parent) {
269             super(instance, parent);
270         }
271 
272         @Override
273         public void startElement(ELEMENT element, Attributes attributes) throws SAXException {
274             if (element.equals(StateVariableHandler.EL)) {
275                 MutableStateVariable stateVariable = new MutableStateVariable();
276 
277                 String sendEventsAttributeValue = attributes.getValue(ATTRIBUTE.sendEvents.toString());
278                 stateVariable.eventDetails = new StateVariableEventDetails(
279                         sendEventsAttributeValue != null && sendEventsAttributeValue.toUpperCase(Locale.ROOT).equals("YES")
280                 );
281 
282                 getInstance().add(stateVariable);
283                 new StateVariableHandler(stateVariable, this);
284             }
285         }
286 
287         @Override
288         public boolean isLastElement(ELEMENT element) {
289             return element.equals(EL);
290         }
291     }
292 
293     protected static class StateVariableHandler extends ServiceDescriptorHandler<MutableStateVariable> {
294 
295         public static final ELEMENT EL = ELEMENT.stateVariable;
296 
297         public StateVariableHandler(MutableStateVariable instance, ServiceDescriptorHandler parent) {
298             super(instance, parent);
299         }
300 
301         @Override
302         public void startElement(ELEMENT element, Attributes attributes) throws SAXException {
303             if (element.equals(AllowedValueListHandler.EL)) {
304                 List<String> allowedValues = new ArrayList<>();
305                 getInstance().allowedValues = allowedValues;
306                 new AllowedValueListHandler(allowedValues, this);
307             }
308 
309             if (element.equals(AllowedValueRangeHandler.EL)) {
310                 MutableAllowedValueRange allowedValueRange = new MutableAllowedValueRange();
311                 getInstance().allowedValueRange = allowedValueRange;
312                 new AllowedValueRangeHandler(allowedValueRange, this);
313             }
314         }
315 
316         @Override
317         public void endElement(ELEMENT element) throws SAXException {
318             switch (element) {
319                 case name:
320                     getInstance().name = getCharacters();
321                     break;
322                 case dataType:
323                     String dtName = getCharacters();
324                     Datatype.Builtin builtin = Datatype.Builtin.getByDescriptorName(dtName);
325                     getInstance().dataType = builtin != null ? builtin.getDatatype() : new CustomDatatype(dtName);
326                     break;
327                 case defaultValue:
328                     getInstance().defaultValue = getCharacters();
329                     break;
330             }
331         }
332 
333         @Override
334         public boolean isLastElement(ELEMENT element) {
335             return element.equals(EL);
336         }
337     }
338 
339     protected static class AllowedValueListHandler extends ServiceDescriptorHandler<List<String>> {
340 
341         public static final ELEMENT EL = ELEMENT.allowedValueList;
342 
343         public AllowedValueListHandler(List<String> instance, ServiceDescriptorHandler parent) {
344             super(instance, parent);
345         }
346 
347         @Override
348         public void endElement(ELEMENT element) throws SAXException {
349             switch (element) {
350                 case allowedValue:
351                     getInstance().add(getCharacters());
352                     break;
353             }
354         }
355 
356         @Override
357         public boolean isLastElement(ELEMENT element) {
358             return element.equals(EL);
359         }
360     }
361 
362     protected static class AllowedValueRangeHandler extends ServiceDescriptorHandler<MutableAllowedValueRange> {
363 
364         public static final ELEMENT EL = ELEMENT.allowedValueRange;
365 
366         public AllowedValueRangeHandler(MutableAllowedValueRange instance, ServiceDescriptorHandler parent) {
367             super(instance, parent);
368         }
369 
370         @Override
371         public void endElement(ELEMENT element) throws SAXException {
372             try {
373                 switch (element) {
374                     case minimum:
375                         getInstance().minimum = Long.valueOf(getCharacters());
376                         break;
377                     case maximum:
378                         getInstance().maximum = Long.valueOf(getCharacters());
379                         break;
380                     case step:
381                         getInstance().step = Long.valueOf(getCharacters());
382                         break;
383                 }
384             } catch (Exception ex) {
385                 // Ignore
386             }
387         }
388 
389         @Override
390         public boolean isLastElement(ELEMENT element) {
391             return element.equals(EL);
392         }
393     }
394 
395     protected static class ServiceDescriptorHandler<I> extends SAXParser.Handler<I> {
396 
397         public ServiceDescriptorHandler(I instance) {
398             super(instance);
399         }
400 
401         public ServiceDescriptorHandler(I instance, SAXParser parser) {
402             super(instance, parser);
403         }
404 
405         public ServiceDescriptorHandler(I instance, ServiceDescriptorHandler parent) {
406             super(instance, parent);
407         }
408 
409         public ServiceDescriptorHandler(I instance, SAXParser parser, ServiceDescriptorHandler parent) {
410             super(instance, parser, parent);
411         }
412 
413         @Override
414         public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
415             super.startElement(uri, localName, qName, attributes);
416             ELEMENT el = ELEMENT.valueOrNullOf(localName);
417             if (el == null) return;
418             startElement(el, attributes);
419         }
420 
421         @Override
422         public void endElement(String uri, String localName, String qName) throws SAXException {
423             super.endElement(uri, localName, qName);
424             ELEMENT el = ELEMENT.valueOrNullOf(localName);
425             if (el == null) return;
426             endElement(el);
427         }
428 
429         @Override
430         protected boolean isLastElement(String uri, String localName, String qName) {
431             ELEMENT el = ELEMENT.valueOrNullOf(localName);
432             return el != null && isLastElement(el);
433         }
434 
435         public void startElement(ELEMENT element, Attributes attributes) throws SAXException {
436 
437         }
438 
439         public void endElement(ELEMENT element) throws SAXException {
440 
441         }
442 
443         public boolean isLastElement(ELEMENT element) {
444             return false;
445         }
446     }
447 
448 }