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.type;
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.xml.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.Append;
33  import at.ac.tuwien.infosys.sm4all.copal.api.xml.BaseUnmarshallerBuilder;
34  import at.ac.tuwien.infosys.sm4all.copal.api.xml.ElementUnmarshaller;
35  import at.ac.tuwien.infosys.sm4all.copal.api.xml.InsertAfter;
36  import at.ac.tuwien.infosys.sm4all.copal.api.xml.ListUnmarshaller;
37  import at.ac.tuwien.infosys.sm4all.copal.api.xml.Optional;
38  import at.ac.tuwien.infosys.sm4all.copal.api.xml.StringAttribute;
39  
40  /**
41   * Unmarshalls and marshalls a {@link XMLContextEventType} from/into an
42   * {@link Element}.
43   * 
44   * @author sanjin
45   */
46  public class XMLContextEventTypeUnmarshaller implements
47          Unmarshaller<XMLContextEventType> {
48  
49      /**
50       * The local name of child {@link Element}s used in the
51       * {@link ListUnmarshaller.Builder} that is returned by the
52       * {@link #getListBuilder()}.
53       */
54      public static final String EVENT_TYPE_ELEMENT = ContextEventTypeUnmarshaller.EVENT_TYPE_ELEMENT;
55      /**
56       * The name of attribute that holds marshalled name.
57       */
58      public static final String NAME_ATTRIBUTE = ContextEventTypeUnmarshaller.NAME_ATTRIBUTE;
59      /**
60       * The name of attribute that holds marshalled time-to-live.
61       */
62      public static final String TTL_ATTRIBUTE = ContextEventTypeUnmarshaller.TTL_ATTRIBUTE;
63      /**
64       * The name of attribute that holds marshalled priority.
65       */
66      public static final String PRIORITY_ATTRIBUTE = ContextEventTypeUnmarshaller.PRIORITY_ATTRIBUTE;
67      /**
68       * The name of attribute that holds marshalled root element name.
69       */
70      public static final String ROOT_ELEMENT_NAME_ATTRIBUTE = "rootElement";
71      /**
72       * The name of attribute that holds marshalled namespace {@link URI}.
73       */
74      public static final String NAMESPACE_URI_ATTRIBUTE = "namespace";
75      /**
76       * The local name of child {@link Element} that holds marshalled
77       * {@link Attribute}s.
78       */
79      public static final String ATTRIBUTES_ELEMENT = ContextEventTypeUnmarshaller.ATTRIBUTES_ELEMENT;
80      /**
81       * The local name of child {@link Element} that holds marshalled
82       * {@link Authorization}s.
83       */
84      public static final String AUTHORIZATIONS_ELEMENT = ContextEventTypeUnmarshaller.AUTHORIZATIONS_ELEMENT;
85      /**
86       * The local name of child {@link Element} that holds marshalled
87       * {@link DefaultAction}s.
88       */
89      public static final String ACTIONS_ELEMENT = ContextEventTypeUnmarshaller.ACTIONS_ELEMENT;
90      /**
91       * The local name of child {@link Element} that holds marshalled
92       * {@link Schema} {@link URL}.
93       */
94      public static final String SCHEMA_ELEMENT = SchemaURLUnmarshaller.SCHEMA_ELEMENT;
95  
96      private static final ListUnmarshaller.Builder<XMLContextEventType> LIST_BUILDER = new ListUnmarshaller.Builder<XMLContextEventType>().withChildName(
97              EVENT_TYPE_ELEMENT).withBuilder(new Builder()).withStrategy(
98              new Append());
99  
100     private final ClassLoader classLoader;
101     private final Element element;
102     private final ContextEventTypeUnmarshaller eventTypeUnmarshaller;
103     private final Unmarshaller<String> rootElementName;
104     private final Unmarshaller<String> namespaceURI;
105     private final Unmarshaller<URL> schemaURL;
106 
107     /**
108      * Creates instance of {@link XMLContextEventType} {@link Unmarshaller}
109      * which uses specified {@link Element} for unmarshalling and/or
110      * marshalling.
111      * 
112      * @param element the {@link Element} used for unmarshalling and
113      *        marshalling.
114      * @throws NullPointerException if specified {@link Element} is
115      *         <code>null</code>.
116      */
117     public XMLContextEventTypeUnmarshaller(final Element element) {
118         this(null, element);
119     }
120 
121     /**
122      * Creates instance of {@link XMLContextEventType} {@link Unmarshaller}
123      * which uses specified {@link Element} for unmarshalling and/or
124      * marshalling. The specified {@link ClassLoader} is used for resolving
125      * {@link Schema} {@link URL} that is in the classpath.
126      * 
127      * @param classLoader the {@link ClassLoader} used for resolving
128      *        {@link Schema} {@link URL} that is in the classpath.
129      * @param element the {@link Element} used for unmarshalling and
130      *        marshalling.
131      * @throws NullPointerException if specified {@link Element} is
132      *         <code>null</code>.
133      */
134     public XMLContextEventTypeUnmarshaller(final ClassLoader classLoader,
135             final Element element) {
136         super();
137 
138         if (element == null)
139             throw new NullPointerException("XML DOM element cannot be null.");
140 
141         this.classLoader = classLoader;
142         this.element = element;
143         this.eventTypeUnmarshaller = new ContextEventTypeUnmarshaller(element);
144         this.rootElementName = new Optional<String>(new StringAttribute(
145                 ROOT_ELEMENT_NAME_ATTRIBUTE, element));
146         this.namespaceURI = new Optional<String>(new StringAttribute(
147                 NAMESPACE_URI_ATTRIBUTE, element));
148         SchemaURLUnmarshaller.Builder builder = new SchemaURLUnmarshaller.Builder();
149         if (classLoader != null)
150             builder = builder.withClassLoader(classLoader);
151         this.schemaURL = new ElementUnmarshaller<URL>(element, SCHEMA_ELEMENT,
152                 builder, new InsertAfter(ACTIONS_ELEMENT, new InsertAfter(
153                         AUTHORIZATIONS_ELEMENT, new InsertAfter(
154                                 ATTRIBUTES_ELEMENT, new Append()))));
155     }
156 
157     /**
158      * Returns the {@link ClassLoader} used for resolving {@link Schema}
159      * {@link URL} that is in the classpath.
160      * 
161      * @return the {@link ClassLoader} used for resolving {@link Schema}
162      *         {@link URL} that is in the classpath.
163      */
164     public ClassLoader getClassLoader() {
165         return this.classLoader;
166     }
167 
168     /**
169      * Returns the {@link Element} used for unmarshalling and marshalling.
170      * 
171      * @return the {@link Element} used for unmarshalling and marshalling.
172      */
173     public Element getElement() {
174         return this.element;
175     }
176 
177     /**
178      * Unmarshalls an {@link XMLContextEventType} from the {@link Element}.
179      * 
180      * @return the unmarshalled {@link XMLContextEventType}.
181      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
182      */
183     @Override
184     public XMLContextEventType unmarshal() throws FailedUnmarshallingException {
185         final XMLContextEventType result = new XMLContextEventType(
186                 unmarshalName(), unmarshalRootElementName());
187 
188         result.setTTL(unmarshalTTL());
189         result.setPriority(unmarshalPriority());
190         result.setNamespaceURI(unmarshalNamespaceURI());
191         for (final Attribute attribute : unmarshalAttributes())
192             result.setAttribute(attribute.getName(), attribute.getValue());
193         for (final Authorization authorization : unmarshalAuthorizations())
194             result.addAuthorization(authorization);
195         result.appendActions(unmarshalActions());
196         result.setSchemaURL(unmarshalSchemaURL());
197 
198         return result;
199     }
200 
201     /**
202      * Unmarshalls name of {@link XMLContextEventType} from the {@link Element}.
203      * 
204      * @return the name of marshalled {@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      * Unmarshalls 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 marshalled {@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      * Unmarshalls 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 marshalled {@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      * Unmarshalls 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 marshalled {@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 (result == null)
248             result = unmarshalName();
249 
250         return result;
251     }
252 
253     /**
254      * Unmarshalls namespace {@link URI} of {@link XMLContextEventType} from the
255      * {@link Element}. If the namespace {@link URI} value is missing then
256      * <code>null</code> value is returned.
257      * 
258      * @return the namespace {@link URI} of marshalled
259      *         {@link XMLContextEventType}.
260      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
261      */
262     @SuppressWarnings("hiding")
263     public URI unmarshalNamespaceURI() throws FailedUnmarshallingException {
264         final URI result;
265 
266         final String namespaceURI = this.namespaceURI.unmarshal();
267         if (namespaceURI == null)
268             result = null;
269         else
270             result = URI.create(namespaceURI);
271 
272         return result;
273     }
274 
275     /**
276      * Unmarshalls {@link Attribute}s of {@link XMLContextEventType} from the
277      * {@link Element}.
278      * 
279      * @return the {@link Attribute}s of marshalled {@link XMLContextEventType}.
280      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
281      */
282     public Attribute[] unmarshalAttributes()
283             throws FailedUnmarshallingException {
284         return this.eventTypeUnmarshaller.unmarshalAttributes();
285     }
286 
287     /**
288      * Unmarshalls {@link Authorization}s of {@link XMLContextEventType} from
289      * the {@link Element}.
290      * 
291      * @return the {@link Authorization}s of marshalled
292      *         {@link XMLContextEventType}.
293      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
294      */
295     public Authorization[] unmarshalAuthorizations()
296             throws FailedUnmarshallingException {
297         return this.eventTypeUnmarshaller.unmarshalAuthorizations();
298     }
299 
300     /**
301      * Unmarshalls {@link DefaultAction}s of {@link XMLContextEventType} from
302      * the {@link Element}.
303      * 
304      * @return the {@link DefaultAction}s of marshalled
305      *         {@link XMLContextEventType}.
306      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
307      */
308     public DefaultAction[] unmarshalActions()
309             throws FailedUnmarshallingException {
310         return this.eventTypeUnmarshaller.unmarshalActions();
311     }
312 
313     /**
314      * Unmarshalls {@link Schema} {@link URL} of {@link XMLContextEventType}
315      * from the {@link Element}. If the {@link Schema} {@link URL} value is
316      * missing then <code>null</code> value is returned.
317      * 
318      * @return the {@link Schema} {@link URL} of marshalled
319      *         {@link XMLContextEventType}.
320      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
321      */
322     public URL unmarshalSchemaURL() throws FailedUnmarshallingException {
323         URL result;
324 
325         try {
326             result = this.schemaURL.unmarshal();
327         } catch (final MissingFieldException ex) {
328             if (SCHEMA_ELEMENT.equals(ex.getFieldName()))
329                 result = null;
330             else
331                 throw ex;
332         }
333 
334         return result;
335     }
336 
337     /**
338      * Marshalls specified {@link XMLContextEventType} into the {@link Element}.
339      * 
340      * @param eventType the {@link XMLContextEventType}.
341      * @throws NullPointerException if specified {@link XMLContextEventType} is
342      *         <code>null</code>.
343      */
344     @SuppressWarnings("hiding")
345     @Override
346     public void marshal(final XMLContextEventType eventType) {
347         if (eventType == null)
348             throw new NullPointerException("Event type cannot be null.");
349 
350         this.schemaURL.remove();
351         this.eventTypeUnmarshaller.marshal(eventType);
352 
353         final String rootElementName = eventType.getRootElementName();
354         if (eventType.getName().equals(rootElementName))
355             this.rootElementName.remove();
356         else
357             this.rootElementName.marshal(rootElementName);
358         if (eventType.hasNamespace())
359             this.namespaceURI.marshal(eventType.getNamespaceURI().toString());
360         else
361             this.namespaceURI.remove();
362         if (eventType.hasSchema())
363             this.schemaURL.marshal(eventType.getSchemaURL());
364     }
365 
366     /**
367      * Removes any marshalled {@link XMLContextEventType} from the
368      * {@link Element}.
369      */
370     @Override
371     public void remove() {
372         this.eventTypeUnmarshaller.remove();
373 
374         this.rootElementName.remove();
375         this.namespaceURI.remove();
376         this.schemaURL.remove();
377     }
378 
379     /**
380      * Creates instance of {@link ListUnmarshaller.Builder} for
381      * {@link XMLContextEventType}s. The returned
382      * {@link ListUnmarshaller.Builder} does not have the parent {@link Element}
383      * set and caller should set it before building the {@link ListUnmarshaller}
384      * for {@link XMLContextEventType}s. The name for child {@link Element}s is
385      * set to {@link ContextEventTypeUnmarshaller#EVENT_TYPE_ELEMENT} and the
386      * strategy is set to {@link Append}.
387      * 
388      * @return the {@link ListUnmarshaller.Builder} for
389      *         {@link XMLContextEventType}s.
390      */
391     public static ListUnmarshaller.Builder<XMLContextEventType> getListBuilder() {
392         return LIST_BUILDER;
393     }
394 
395     /**
396      * Creates instance of {@link ListUnmarshaller.Builder} for
397      * {@link XMLContextEventType}s. The {@link Schema} {@link URL}s in
398      * classpath will be resolved with specified {@link ClassLoader}. The
399      * returned {@link ListUnmarshaller.Builder} does not have the parent
400      * {@link Element} set and caller should set it before building the
401      * {@link ListUnmarshaller} for {@link XMLContextEventType}s. The name for
402      * child {@link Element}s is set to
403      * {@link ContextEventTypeUnmarshaller#EVENT_TYPE_ELEMENT} and the strategy
404      * is set to {@link Append}.
405      * 
406      * @param classLoader the {@link ClassLoader} used for resolving
407      *        {@link Schema} {@link URL}s that are in the classpath.
408      * @return the {@link ListUnmarshaller.Builder} for
409      *         {@link XMLContextEventType}s.
410      */
411     public static ListUnmarshaller.Builder<XMLContextEventType> getListBuilder(
412             final ClassLoader classLoader) {
413         return new ListUnmarshaller.Builder<XMLContextEventType>().withChildName(
414                 EVENT_TYPE_ELEMENT).withBuilder(
415                 new Builder().withClassLoader(classLoader)).withStrategy(
416                 new Append());
417     }
418 
419     /**
420      * Builder of {@link XMLContextEventTypeUnmarshaller}.
421      * 
422      * @author sanjin
423      */
424     public static class Builder extends
425             BaseUnmarshallerBuilder<XMLContextEventType> {
426 
427         private final AtomicReference<ClassLoader> classLoaderRef = new AtomicReference<ClassLoader>();
428 
429         /**
430          * Create uninitialized instance of
431          * {@link XMLContextEventTypeUnmarshaller.Builder}.
432          */
433         public Builder() {
434             super();
435         }
436 
437         /**
438          * Clone-constructor.
439          * 
440          * @param builder the cloned
441          *        {@link XMLContextEventTypeUnmarshaller.Builder}.
442          */
443         private Builder(final Builder builder) {
444             super(builder);
445 
446             this.classLoaderRef.set(builder.classLoaderRef.get());
447         }
448 
449         /**
450          * Returns the {@link ClassLoader}.
451          * 
452          * @return the {@link ClassLoader}.
453          */
454         public ClassLoader getClassLoader() {
455             return this.classLoaderRef.get();
456         }
457 
458         /**
459          * Create instance of {@link XMLContextEventTypeUnmarshaller.Builder}
460          * that will build {@link XMLContextEventTypeUnmarshaller}s that will
461          * use specified {@link ClassLoader} to resolve {@link Schema}
462          * {@link URL}s that are in the classpath.
463          * 
464          * @param classLoader the {@link ClassLoader}.
465          * @return an {@link XMLContextEventTypeUnmarshaller.Builder}.
466          */
467         public Builder withClassLoader(final ClassLoader classLoader) {
468             final Builder result = clone();
469 
470             result.classLoaderRef.set(classLoader);
471 
472             return result;
473         }
474 
475         /**
476          * Create instance of {@link XMLContextEventTypeUnmarshaller}.
477          * 
478          * @return a {@link XMLContextEventTypeUnmarshaller}.
479          */
480         @Override
481         public XMLContextEventTypeUnmarshaller build() {
482             return new XMLContextEventTypeUnmarshaller(getClassLoader(),
483                     getElement());
484         }
485 
486         @Override
487         protected Builder clone() {
488             return new Builder(this);
489         }
490     }
491 }