View Javadoc

1   /* This file is part of COPAL (COntext Provisioning for All).
2    *
3    * COPAL is a part of SM4All (Smart hoMes for All) project.
4    *
5    * COPAL is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU Lesser General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * COPAL is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public License
16   * along with COPAL. If not, see <http://www.gnu.org/licenses/>.
17   */
18  package at.ac.tuwien.infosys.sm4all.copal.api.event.xml;
19  
20  import java.net.URI;
21  import java.net.URL;
22  import java.util.concurrent.atomic.AtomicReference;
23  import javax.xml.validation.Schema;
24  import org.w3c.dom.Element;
25  import at.ac.tuwien.infosys.sm4all.copal.api.event.DefaultAction;
26  import at.ac.tuwien.infosys.sm4all.copal.api.event.XMLContextEventType;
27  import at.ac.tuwien.infosys.sm4all.copal.api.security.Authorization;
28  import at.ac.tuwien.infosys.sm4all.copal.api.util.Attribute;
29  import at.ac.tuwien.infosys.sm4all.copal.api.util.FailedUnmarshallingException;
30  import at.ac.tuwien.infosys.sm4all.copal.api.util.MissingFieldException;
31  import at.ac.tuwien.infosys.sm4all.copal.api.util.Unmarshaller;
32  import at.ac.tuwien.infosys.sm4all.copal.api.xml.BaseUnmarshallerBuilder;
33  import at.ac.tuwien.infosys.sm4all.copal.api.xml.ElementUnmarshaller;
34  import at.ac.tuwien.infosys.sm4all.copal.api.xml.ListUnmarshaller;
35  import at.ac.tuwien.infosys.sm4all.copal.api.xml.Optional;
36  import at.ac.tuwien.infosys.sm4all.copal.api.xml.RemoveStrategy;
37  import at.ac.tuwien.infosys.sm4all.copal.api.xml.StringAttribute;
38  
39  /**
40   * Unmarshals and marshals a {@link XMLContextEventType} from/into an
41   * {@link Element}.
42   * 
43   * @author sanjin
44   */
45  public class XMLContextEventTypeUnmarshaller implements
46          Unmarshaller<XMLContextEventType> {
47  
48      /**
49       * The local name of child {@link Element}s used in the
50       * {@link ListUnmarshaller.Builder} that is returned by the
51       * {@link #getListBuilder()}.
52       */
53      public static final String EVENT_TYPE_ELEMENT = ContextEventTypeUnmarshaller.EVENT_TYPE_ELEMENT;
54      /**
55       * The name of attribute that holds marshaled name.
56       */
57      public static final String NAME_ATTRIBUTE = ContextEventTypeUnmarshaller.NAME_ATTRIBUTE;
58      /**
59       * The name of attribute that holds marshaled time-to-live.
60       */
61      public static final String TTL_ATTRIBUTE = ContextEventTypeUnmarshaller.TTL_ATTRIBUTE;
62      /**
63       * The name of attribute that holds marshaled priority.
64       */
65      public static final String PRIORITY_ATTRIBUTE = ContextEventTypeUnmarshaller.PRIORITY_ATTRIBUTE;
66      /**
67       * The name of attribute that holds marshaled root element name.
68       */
69      public static final String ROOT_ELEMENT_NAME_ATTRIBUTE = "rootElement";
70      /**
71       * The name of attribute that holds marshaled namespace {@link URI}.
72       */
73      public static final String NAMESPACE_URI_ATTRIBUTE = "namespace";
74      /**
75       * The local name of child {@link Element} that holds marshaled
76       * {@link Attribute}s.
77       */
78      public static final String ATTRIBUTES_ELEMENT = ContextEventTypeUnmarshaller.ATTRIBUTES_ELEMENT;
79      /**
80       * The local name of child {@link Element} that holds marshaled
81       * {@link Authorization}s.
82       */
83      public static final String AUTHORIZATIONS_ELEMENT = ContextEventTypeUnmarshaller.AUTHORIZATIONS_ELEMENT;
84      /**
85       * The local name of child {@link Element} that holds marshaled
86       * {@link DefaultAction}s.
87       */
88      public static final String ACTIONS_ELEMENT = ContextEventTypeUnmarshaller.ACTIONS_ELEMENT;
89      /**
90       * The local name of child {@link Element} that holds marshaled
91       * {@link Schema} {@link URL}.
92       */
93      public static final String SCHEMA_ELEMENT = SchemaURLUnmarshaller.SCHEMA_ELEMENT;
94  
95      private static final ListUnmarshaller.Builder<XMLContextEventType> LIST_BUILDER = new ListUnmarshaller.Builder<XMLContextEventType>().withChildName(
96              EVENT_TYPE_ELEMENT).withBuilder(new Builder());
97  
98      private final ClassLoader classLoader;
99      private final Element element;
100     private final ContextEventTypeUnmarshaller eventTypeUnmarshaller;
101     private final Unmarshaller<String> rootElementName;
102     private final Unmarshaller<String> namespaceURI;
103     private final Unmarshaller<URL> schemaURL;
104 
105     /**
106      * Creates instance of {@link XMLContextEventType} {@link Unmarshaller}
107      * which uses specified {@link Element} for unmarshalling and/or
108      * marshalling.
109      * 
110      * @param element the {@link Element} used for unmarshalling and
111      *        marshalling.
112      * @throws NullPointerException if specified {@link Element} is
113      *         <code>null</code>.
114      */
115     public XMLContextEventTypeUnmarshaller(final Element element) {
116         this(null, element);
117     }
118 
119     /**
120      * Creates instance of {@link XMLContextEventType} {@link Unmarshaller}
121      * which uses specified {@link Element} for unmarshalling and/or
122      * marshalling. The specified {@link ClassLoader} is used for resolving
123      * {@link Schema} {@link URL} that is in the classpath.
124      * 
125      * @param classLoader the {@link ClassLoader} used for resolving
126      *        {@link Schema} {@link URL} that is in the classpath.
127      * @param element the {@link Element} used for unmarshalling and
128      *        marshalling.
129      * @throws NullPointerException if specified {@link Element} is
130      *         <code>null</code>.
131      */
132     public XMLContextEventTypeUnmarshaller(final ClassLoader classLoader,
133             final Element element) {
134         super();
135 
136         if (null == element) {
137             throw new NullPointerException("XML DOM element cannot be null.");
138         }
139 
140         this.classLoader = classLoader;
141         this.element = element;
142         this.eventTypeUnmarshaller = new ContextEventTypeUnmarshaller(element);
143         this.rootElementName = new Optional<String>(new StringAttribute(
144                 ROOT_ELEMENT_NAME_ATTRIBUTE, element));
145         this.namespaceURI = new Optional<String>(new StringAttribute(
146                 NAMESPACE_URI_ATTRIBUTE, element));
147         SchemaURLUnmarshaller.Builder builder = new SchemaURLUnmarshaller.Builder();
148         if (null != classLoader) {
149             builder = builder.withClassLoader(classLoader);
150         }
151         this.schemaURL = new ElementUnmarshaller<URL>(element, SCHEMA_ELEMENT,
152                 builder, RemoveStrategy.RemoveElement);
153     }
154 
155     /**
156      * Returns the {@link ClassLoader} used for resolving {@link Schema}
157      * {@link URL} that is in the classpath.
158      * 
159      * @return the {@link ClassLoader} used for resolving {@link Schema}
160      *         {@link URL} that is in the classpath.
161      */
162     public ClassLoader getClassLoader() {
163         return this.classLoader;
164     }
165 
166     /**
167      * Returns the {@link Element} used for unmarshalling and marshalling.
168      * 
169      * @return the {@link Element} used for unmarshalling and marshalling.
170      */
171     public Element getElement() {
172         return this.element;
173     }
174 
175     /**
176      * Unmarshals an {@link XMLContextEventType} from the {@link Element}.
177      * 
178      * @return the unmarshaled {@link XMLContextEventType}.
179      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
180      */
181     @Override
182     public XMLContextEventType unmarshal() throws FailedUnmarshallingException {
183         final XMLContextEventType result = new XMLContextEventType(
184                 unmarshalName(), unmarshalRootElementName());
185 
186         result.setTTL(unmarshalTTL());
187         result.setPriority(unmarshalPriority());
188         result.setNamespaceURI(unmarshalNamespaceURI());
189         for (final Attribute attribute : unmarshalAttributes()) {
190             result.add(attribute);
191         }
192         for (final Authorization authorization : unmarshalAuthorizations()) {
193             result.add(authorization);
194         }
195         result.append(unmarshalActions());
196         result.setSchemaURL(unmarshalSchemaURL());
197 
198         return result;
199     }
200 
201     /**
202      * Unmarshals name of {@link XMLContextEventType} from the {@link Element}.
203      * 
204      * @return the name of marshaled {@link XMLContextEventType}.
205      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
206      */
207     public String unmarshalName() throws FailedUnmarshallingException {
208         return this.eventTypeUnmarshaller.unmarshalName();
209     }
210 
211     /**
212      * Unmarshals time-to-live of {@link XMLContextEventType} from the
213      * {@link Element}. If the time-to-live value is missing then the
214      * {@link XMLContextEventType#TTL_DEFAULT} is returned.
215      * 
216      * @return the time-to-live of marshaled {@link XMLContextEventType}.
217      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
218      */
219     public long unmarshalTTL() throws FailedUnmarshallingException {
220         return this.eventTypeUnmarshaller.unmarshalTTL();
221     }
222 
223     /**
224      * Unmarshals priority of {@link XMLContextEventType} from the
225      * {@link Element}. If the priority value is missing then the
226      * {@link XMLContextEventType#PRIORITY_DEFAULT} is returned.
227      * 
228      * @return the priority of marshaled {@link XMLContextEventType}.
229      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
230      */
231     public int unmarshalPriority() throws FailedUnmarshallingException {
232         return this.eventTypeUnmarshaller.unmarshalPriority();
233     }
234 
235     /**
236      * Unmarshals root element name of {@link XMLContextEventType} from the
237      * {@link Element}. If the root element name value is missing then the
238      * result of the {@link #unmarshalName()} method is returned.
239      * 
240      * @return the root element name of marshaled {@link XMLContextEventType}.
241      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
242      */
243     public String unmarshalRootElementName()
244             throws FailedUnmarshallingException {
245         String result = this.rootElementName.unmarshal();
246 
247         if (null == result) {
248             result = unmarshalName();
249         }
250 
251         return result;
252     }
253 
254     /**
255      * Unmarshals namespace {@link URI} of {@link XMLContextEventType} from the
256      * {@link Element}. If the namespace {@link URI} value is missing then
257      * <code>null</code> value is returned.
258      * 
259      * @return the namespace {@link URI} of marshaled
260      *         {@link XMLContextEventType}.
261      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
262      */
263     public URI unmarshalNamespaceURI() throws FailedUnmarshallingException {
264         final URI result;
265 
266         @SuppressWarnings("hiding")
267         final String namespaceURI = this.namespaceURI.unmarshal();
268         if (null == namespaceURI) {
269             result = null;
270         } else {
271             result = URI.create(namespaceURI);
272         }
273 
274         return result;
275     }
276 
277     /**
278      * Unmarshals {@link Attribute}s of {@link XMLContextEventType} from the
279      * {@link Element}.
280      * 
281      * @return the {@link Attribute}s of marshaled {@link XMLContextEventType}.
282      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
283      */
284     public Attribute[] unmarshalAttributes()
285             throws FailedUnmarshallingException {
286         return this.eventTypeUnmarshaller.unmarshalAttributes();
287     }
288 
289     /**
290      * Unmarshals {@link Authorization}s of {@link XMLContextEventType} from the
291      * {@link Element}.
292      * 
293      * @return the {@link Authorization}s of marshaled
294      *         {@link XMLContextEventType}.
295      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
296      */
297     public Authorization[] unmarshalAuthorizations()
298             throws FailedUnmarshallingException {
299         return this.eventTypeUnmarshaller.unmarshalAuthorizations();
300     }
301 
302     /**
303      * Unmarshals {@link DefaultAction}s of {@link XMLContextEventType} from the
304      * {@link Element}.
305      * 
306      * @return the {@link DefaultAction}s of marshaled
307      *         {@link XMLContextEventType}.
308      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
309      */
310     public DefaultAction[] unmarshalActions()
311             throws FailedUnmarshallingException {
312         return this.eventTypeUnmarshaller.unmarshalActions();
313     }
314 
315     /**
316      * Unmarshals {@link Schema} {@link URL} of {@link XMLContextEventType} from
317      * the {@link Element}. If the {@link Schema} {@link URL} value is missing
318      * then <code>null</code> value is returned.
319      * 
320      * @return the {@link Schema} {@link URL} of marshaled
321      *         {@link XMLContextEventType}.
322      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
323      */
324     public URL unmarshalSchemaURL() throws FailedUnmarshallingException {
325         URL result;
326 
327         try {
328             result = this.schemaURL.unmarshal();
329         } catch (final MissingFieldException ex) {
330             if (SCHEMA_ELEMENT.equals(ex.getFieldName())) {
331                 result = null;
332             } else {
333                 throw ex;
334             }
335         }
336 
337         return result;
338     }
339 
340     /**
341      * Marshals specified {@link XMLContextEventType} into the {@link Element}.
342      * 
343      * @param eventType the {@link XMLContextEventType}.
344      * @throws NullPointerException if specified {@link XMLContextEventType} is
345      *         <code>null</code>.
346      */
347     @Override
348     public void marshal(final XMLContextEventType eventType) {
349         if (null == eventType) {
350             throw new NullPointerException("Event type cannot be null.");
351         }
352 
353         this.schemaURL.remove();
354         this.eventTypeUnmarshaller.marshal(eventType);
355 
356         @SuppressWarnings("hiding")
357         final String rootElementName = eventType.getRootElementName();
358         if (eventType.getName().equals(rootElementName)) {
359             this.rootElementName.remove();
360         } else {
361             this.rootElementName.marshal(rootElementName);
362         }
363         if (eventType.hasNamespace()) {
364             this.namespaceURI.marshal(eventType.getNamespaceURI().toString());
365         } else {
366             this.namespaceURI.remove();
367         }
368         if (eventType.hasSchema()) {
369             this.schemaURL.marshal(eventType.getSchemaURL());
370         }
371     }
372 
373     /**
374      * Removes any marshaled {@link XMLContextEventType} from the
375      * {@link Element}.
376      */
377     @Override
378     public void remove() {
379         this.eventTypeUnmarshaller.remove();
380 
381         this.rootElementName.remove();
382         this.namespaceURI.remove();
383         this.schemaURL.remove();
384     }
385 
386     /**
387      * Creates instance of {@link ListUnmarshaller.Builder} for
388      * {@link XMLContextEventType}s. The returned
389      * {@link ListUnmarshaller.Builder} does not have the parent {@link Element}
390      * set and caller should set it before building the {@link ListUnmarshaller}
391      * for {@link XMLContextEventType}s. The name for child {@link Element}s is
392      * set to {@link ContextEventTypeUnmarshaller#EVENT_TYPE_ELEMENT}.
393      * 
394      * @return the {@link ListUnmarshaller.Builder} for
395      *         {@link XMLContextEventType}s.
396      */
397     public static ListUnmarshaller.Builder<XMLContextEventType> getListBuilder() {
398         return LIST_BUILDER;
399     }
400 
401     /**
402      * Creates instance of {@link ListUnmarshaller.Builder} for
403      * {@link XMLContextEventType}s. The {@link Schema} {@link URL}s in
404      * classpath will be resolved with specified {@link ClassLoader}. The
405      * returned {@link ListUnmarshaller.Builder} does not have the parent
406      * {@link Element} set and caller should set it before building the
407      * {@link ListUnmarshaller} for {@link XMLContextEventType}s. The name for
408      * child {@link Element}s is set to
409      * {@link ContextEventTypeUnmarshaller#EVENT_TYPE_ELEMENT}.
410      * 
411      * @param classLoader the {@link ClassLoader} used for resolving
412      *        {@link Schema} {@link URL}s that are in the classpath.
413      * @return the {@link ListUnmarshaller.Builder} for
414      *         {@link XMLContextEventType}s.
415      */
416     public static ListUnmarshaller.Builder<XMLContextEventType> getListBuilder(
417             final ClassLoader classLoader) {
418         return new ListUnmarshaller.Builder<XMLContextEventType>().withChildName(
419                 EVENT_TYPE_ELEMENT).withBuilder(
420                 new Builder().withClassLoader(classLoader));
421     }
422 
423     /**
424      * Builder of {@link XMLContextEventTypeUnmarshaller}.
425      * 
426      * @author sanjin
427      */
428     public static class Builder extends
429             BaseUnmarshallerBuilder<XMLContextEventType> {
430 
431         private final AtomicReference<ClassLoader> classLoaderRef = new AtomicReference<ClassLoader>();
432 
433         /**
434          * Create uninitialized instance of
435          * {@link XMLContextEventTypeUnmarshaller.Builder}.
436          */
437         public Builder() {
438             super();
439         }
440 
441         /**
442          * Clone-constructor.
443          * 
444          * @param builder the cloned
445          *        {@link XMLContextEventTypeUnmarshaller.Builder}.
446          */
447         private Builder(final Builder builder) {
448             super(builder);
449 
450             this.classLoaderRef.set(builder.classLoaderRef.get());
451         }
452 
453         /**
454          * Returns the {@link ClassLoader}.
455          * 
456          * @return the {@link ClassLoader}.
457          */
458         public ClassLoader getClassLoader() {
459             return this.classLoaderRef.get();
460         }
461 
462         /**
463          * Create instance of {@link XMLContextEventTypeUnmarshaller.Builder}
464          * that will build {@link XMLContextEventTypeUnmarshaller}s that will
465          * use specified {@link ClassLoader} to resolve {@link Schema}
466          * {@link URL}s that are in the classpath.
467          * 
468          * @param classLoader the {@link ClassLoader}.
469          * @return an {@link XMLContextEventTypeUnmarshaller.Builder}.
470          */
471         public Builder withClassLoader(final ClassLoader classLoader) {
472             final Builder result = copy();
473 
474             result.classLoaderRef.set(classLoader);
475 
476             return result;
477         }
478 
479         /**
480          * Create instance of {@link XMLContextEventTypeUnmarshaller}.
481          * 
482          * @return a {@link XMLContextEventTypeUnmarshaller}.
483          */
484         @Override
485         public XMLContextEventTypeUnmarshaller build() {
486             return new XMLContextEventTypeUnmarshaller(getClassLoader(),
487                     getElement());
488         }
489 
490         @Override
491         protected Builder copy() {
492             return new Builder(this);
493         }
494     }
495 }