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.util.Arrays;
21  import java.util.Calendar;
22  import java.util.Date;
23  import java.util.List;
24  import java.util.Map.Entry;
25  import org.w3c.dom.Document;
26  import org.w3c.dom.Element;
27  import org.w3c.dom.Node;
28  import at.ac.tuwien.infosys.sm4all.copal.api.event.ContextEvent;
29  import at.ac.tuwien.infosys.sm4all.copal.api.event.ContextEventType;
30  import at.ac.tuwien.infosys.sm4all.copal.api.event.xml.type.AuthorizationUnmarshaller;
31  import at.ac.tuwien.infosys.sm4all.copal.api.security.Authorization;
32  import at.ac.tuwien.infosys.sm4all.copal.api.util.Attribute;
33  import at.ac.tuwien.infosys.sm4all.copal.api.util.Constants;
34  import at.ac.tuwien.infosys.sm4all.copal.api.util.Unmarshaller;
35  import at.ac.tuwien.infosys.sm4all.copal.api.xml.Append;
36  import at.ac.tuwien.infosys.sm4all.copal.api.xml.ElementUnmarshaller;
37  import at.ac.tuwien.infosys.sm4all.copal.api.xml.InsertAfter;
38  import at.ac.tuwien.infosys.sm4all.copal.api.xml.InsertBefore;
39  import at.ac.tuwien.infosys.sm4all.copal.api.xml.UnmarshallerBuilder;
40  import static at.ac.tuwien.infosys.sm4all.copal.api.event.xml.type.ContextEventTypeUnmarshaller.ACTIONS_ELEMENT;
41  import static at.ac.tuwien.infosys.sm4all.copal.api.event.xml.type.ContextEventTypeUnmarshaller.ATTRIBUTES_ELEMENT;
42  import static at.ac.tuwien.infosys.sm4all.copal.api.event.xml.type.ContextEventTypeUnmarshaller.AUTHORIZATIONS_ELEMENT;
43  
44  /**
45   * Class which must be used to wrap a {@link Document} event so COPAL can inject
46   * {@link Element}s needed for processing of the event. Each
47   * {@link XMLContextEvent} also has a {@link XMLContextEventType} associated
48   * with it to be able to easily distinguish {@link XMLContextEvent}s of same
49   * type.
50   * 
51   * @author sanjin
52   */
53  public class XMLContextEvent extends ContextEvent {
54  
55      /**
56       * all COPAL {@link Element}s that are injected and/or can be retrieved from
57       * a generated {@link Document}.
58       */
59      public static final XMLElement<?>[] XML_ELEMENTS = { EventType.INSTANCE,
60              SourceID.INSTANCE, TimeStamp.INSTANCE, Actions.INSTANCE,
61              Attributes.INSTANCE, TimeToLive.INSTANCE, Priority.INSTANCE,
62              CurrentActionName.INSTANCE, ActionsFinished.INSTANCE };
63  
64      private static final UnmarshallerBuilder<List<Authorization>> AUTHORIZATIONS_BUILDER = new ElementUnmarshaller.Builder<List<Authorization>>().withChildName(
65              AUTHORIZATIONS_ELEMENT).withBuilder(
66              AuthorizationUnmarshaller.getListBuilder()).withStrategy(
67              new InsertBefore(ACTIONS_ELEMENT, new InsertAfter(
68                      ATTRIBUTES_ELEMENT, new Append())));
69  
70      private final Document document;
71      private final Unmarshaller<List<Authorization>> authorizations;
72      private Element actionsElement;
73      private Element attributesElement;
74      private Element ttlElement;
75      private Element priorityElement;
76      private boolean elementsInjected = false;
77  
78      /**
79       * Create instance of the {@link XMLContextEvent} with specified
80       * {@link XMLContextEventType} and {@link Document}. The specified
81       * {@link Document} is deep cloned, thus ensuring that any later changes on
82       * this instance will not be reflected in {@link Document} saved in the
83       * {@link XMLContextEvent}.
84       * 
85       * @param eventType the {@link XMLContextEventType} of this
86       *        {@link XMLContextEvent}.
87       * @param document the event {@link Document}.
88       * @throws MalformedDocumentException if specified {@link Document} is
89       *         malformed {@link XMLContextEvent}.
90       * @throws NullPointerException if specified {@link Document} is
91       *         <code>null</code>.
92       */
93      public XMLContextEvent(final XMLContextEventType eventType,
94              final Document document) throws MalformedDocumentException {
95          super(eventType, SourceID.INSTANCE.retrieve(document),
96                  TimeStamp.INSTANCE.retrieve(document));
97  
98          this.document = (Document) document.cloneNode(true);
99  
100         this.actionsElement = Actions.INSTANCE.getElement(this.document);
101         this.attributesElement = Attributes.INSTANCE.getElement(this.document);
102         this.ttlElement = TimeToLive.INSTANCE.getElement(this.document);
103         this.priorityElement = Priority.INSTANCE.getElement(this.document);
104 
105         this.authorizations = AUTHORIZATIONS_BUILDER.withElement(
106                 this.document.getDocumentElement()).build();
107 
108         setTTL(TimeToLive.INSTANCE.retrieve(this.document));
109         setPriority(Priority.INSTANCE.retrieve(this.document));
110 
111         for (final Attribute attribute : getAttributes())
112             setAttribute(attribute.getName(), null);
113         for (final Entry<String, String> attribute : Attributes.INSTANCE.retrieve(
114                 this.document).entrySet())
115             setAttribute(attribute.getKey(), attribute.getValue());
116 
117         for (final Authorization authorization : getAuthorizations())
118             removeAuthorization(authorization.getMethod());
119         for (final Authorization authorization : this.authorizations.unmarshal())
120             addAuthorization(authorization);
121 
122         setActions(Actions.INSTANCE.retrieve(this.document));
123 
124         this.elementsInjected = true;
125     }
126 
127     /**
128      * Create instance of {@link XMLContextEvent} with specified
129      * {@link XMLContextEventType}, source ID and {@link Document}. The
130      * specified {@link Document} is deep cloned, thus ensuring that any later
131      * changes on this instance will not be reflected in {@link Document} saved
132      * in the {@link XMLContextEvent}.
133      * 
134      * @param eventType the {@link XMLContextEventType} of this
135      *        {@link XMLContextEvent}.
136      * @param sourceID the source ID of the {@link XMLContextEvent}.
137      * @param document the event {@link Document}.
138      * @throws MalformedDocumentException if specified {@link Document} is
139      *         malformed for specified {@link ContextEventType}.
140      * @throws NullPointerException if specified {@link XMLContextEventType},
141      *         source ID, or {@link Document} is <code>null</code>.
142      * @throws IllegalArgumentException if specified source ID is an empty or
143      *         blank string.
144      */
145     public XMLContextEvent(final XMLContextEventType eventType,
146             final String sourceID, final Document document)
147             throws MalformedDocumentException {
148         this(eventType, sourceID, Calendar.getInstance().getTime(), document);
149     }
150 
151     /**
152      * Create instance of {@link XMLContextEvent} with specified
153      * {@link XMLContextEventType}, source ID, time stamp and {@link Document}.
154      * The given {@link Document} is deep cloned, thus ensuring that any later
155      * changes on this instance will not be reflected in {@link Document} saved
156      * in the {@link XMLContextEvent}.
157      * 
158      * @param eventType the {@link XMLContextEventType} of the
159      *        {@link XMLContextEvent}.
160      * @param sourceID the source ID of the {@link XMLContextEvent}.
161      * @param timeStamp the time stamp for the {@link XMLContextEvent}.
162      * @param document the event as XML DOM document.
163      * @throws MalformedDocumentException if specified {@link Document} is
164      *         malformed for specified {@link XMLContextEventType}.
165      * @throws NullPointerException if specified event type, source ID, time
166      *         stamp or document is <code>null</code>.
167      * @throws IllegalArgumentException if specified source ID is an empty or
168      *         blank string.
169      */
170     public XMLContextEvent(final XMLContextEventType eventType,
171             final String sourceID, final Date timeStamp, final Document document)
172             throws MalformedDocumentException {
173         super(eventType, sourceID, timeStamp);
174 
175         if (document == null)
176             throw new NullPointerException("XML DOM document cannot be null.");
177 
178         eventType.validate(document);
179 
180         this.document = (Document) document.cloneNode(true);
181 
182         this.authorizations = AUTHORIZATIONS_BUILDER.withElement(
183                 this.document.getDocumentElement()).build();
184     }
185 
186     /**
187      * Returns the {@link XMLContextEventType} associated with this
188      * {@link XMLContextEvent}.
189      * 
190      * @return the {@link XMLContextEventType} associated with this
191      *         {@link XMLContextEvent}.
192      */
193     @Override
194     public XMLContextEventType getType() {
195         return (XMLContextEventType) super.getType();
196     }
197 
198     /**
199      * @return {@link Document} with all COPAL {@link Element}s injected.
200      */
201     public Document getDocument() {
202         if (this.elementsInjected) {
203             TimeToLive.INSTANCE.update(this.ttlElement, this);
204             Priority.INSTANCE.update(this.priorityElement, this);
205             Attributes.INSTANCE.update(this.attributesElement, this);
206             Actions.INSTANCE.update(this.actionsElement, this);
207             this.authorizations.marshal(Arrays.asList(getAuthorizations()));
208         } else
209             injectElements();
210 
211         return this.document;
212     }
213 
214     private void injectElements() {
215         final Element root = this.document.getDocumentElement();
216         root.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:"
217                 + Constants.COPAL_PREFIX, Constants.COPAL_NAMESPACE);
218         final Node firstChild = root.getFirstChild();
219 
220         for (final XMLElement<?> element : XML_ELEMENTS)
221             if (element instanceof ConcreteElement<?>) {
222                 final ConcreteElement<?> concreteElement = (ConcreteElement<?>) element;
223 
224                 final Element result = concreteElement.createElement(
225                         this.document, this);
226 
227                 if (concreteElement instanceof Actions)
228                     this.actionsElement = result;
229                 else if (concreteElement instanceof Attributes)
230                     this.attributesElement = result;
231                 else if (concreteElement instanceof TimeToLive)
232                     this.ttlElement = result;
233                 else if (concreteElement instanceof Priority)
234                     this.priorityElement = result;
235 
236                 root.insertBefore(result, firstChild);
237             }
238 
239         this.authorizations.marshal(Arrays.asList(getAuthorizations()));
240 
241         this.elementsInjected = true;
242     }
243 }