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;
19  
20  import java.text.MessageFormat;
21  import java.util.Arrays;
22  import java.util.Calendar;
23  import java.util.Date;
24  import java.util.LinkedList;
25  import java.util.List;
26  import org.w3c.dom.Document;
27  import org.w3c.dom.Element;
28  import at.ac.tuwien.infosys.sm4all.copal.api.event.xml.AttributeUnmarshaller;
29  import at.ac.tuwien.infosys.sm4all.copal.api.event.xml.AuthorizationUnmarshaller;
30  import at.ac.tuwien.infosys.sm4all.copal.api.event.xml.ContextEventActionMarshaller;
31  import at.ac.tuwien.infosys.sm4all.copal.api.event.xml.CurrentActionUnmarshaller;
32  import at.ac.tuwien.infosys.sm4all.copal.api.event.xml.ProcessedActionUnmarshaller;
33  import at.ac.tuwien.infosys.sm4all.copal.api.event.xml.UnprocessedActionUnmarshaller;
34  import at.ac.tuwien.infosys.sm4all.copal.api.event.xml.XMLContextEventTypeUnmarshaller;
35  import at.ac.tuwien.infosys.sm4all.copal.api.security.Authorization;
36  import at.ac.tuwien.infosys.sm4all.copal.api.util.Attribute;
37  import at.ac.tuwien.infosys.sm4all.copal.api.util.FailedUnmarshallingException;
38  import at.ac.tuwien.infosys.sm4all.copal.api.util.Marshaller;
39  import at.ac.tuwien.infosys.sm4all.copal.api.util.MissingFieldException;
40  import at.ac.tuwien.infosys.sm4all.copal.api.util.Unmarshaller;
41  import at.ac.tuwien.infosys.sm4all.copal.api.xml.Constants;
42  import at.ac.tuwien.infosys.sm4all.copal.api.xml.DateAttribute;
43  import at.ac.tuwien.infosys.sm4all.copal.api.xml.ElementMarshaller;
44  import at.ac.tuwien.infosys.sm4all.copal.api.xml.ElementUnmarshaller;
45  import at.ac.tuwien.infosys.sm4all.copal.api.xml.IntegerAttribute;
46  import at.ac.tuwien.infosys.sm4all.copal.api.xml.LongAttribute;
47  import at.ac.tuwien.infosys.sm4all.copal.api.xml.MarshallerBuilder;
48  import at.ac.tuwien.infosys.sm4all.copal.api.xml.RemoveStrategy;
49  import at.ac.tuwien.infosys.sm4all.copal.api.xml.StringAttribute;
50  import at.ac.tuwien.infosys.sm4all.copal.api.xml.UnmarshallerBuilder;
51  import static at.ac.tuwien.infosys.sm4all.copal.api.xml.Constants.COPAL_PREFIX;
52  
53  /**
54   * Class which must be used to wrap a {@link Document} event so COPAL can inject
55   * <code>Event</code> {@link Element} as needed for processing of the event.
56   * Each {@link XMLContextEvent} also has a {@link XMLContextEventType}
57   * associated with it to be able to easily distinguish {@link XMLContextEvent}s
58   * of same type.
59   * 
60   * @author sanjin
61   */
62  public class XMLContextEvent extends ContextEvent {
63  
64      /**
65       * The local name of {@link Element} that holds injected marshaled
66       * properties of {@link XMLContextEvent}.
67       */
68      public static final String EVENT_ELEMENT = "Event";
69      /**
70       * The name of attribute that holds marshaled type.
71       */
72      public static final String SOURCE_ID_ATTRIBUTE = "sourceID";
73      /**
74       * The name of attribute that holds marshaled type.
75       */
76      public static final String TIME_STAMP_ATTRIBUTE = "timeStamp";
77      /**
78       * The name of attribute that holds marshaled type.
79       */
80      public static final String TYPE_ATTRIBUTE = "type";
81      /**
82       * The name of attribute that holds marshaled time-to-live.
83       */
84      public static final String TTL_ATTRIBUTE = XMLContextEventTypeUnmarshaller.TTL_ATTRIBUTE;
85      /**
86       * The name of attribute that holds marshaled priority.
87       */
88      public static final String PRIORITY_ATTRIBUTE = XMLContextEventTypeUnmarshaller.PRIORITY_ATTRIBUTE;
89      /**
90       * The local name of child {@link Element} that holds marshaled
91       * {@link Attribute}s.
92       */
93      public static final String ATTRIBUTES_ELEMENT = XMLContextEventTypeUnmarshaller.ATTRIBUTES_ELEMENT;
94      /**
95       * The local name of child {@link Element} that holds marshaled
96       * {@link Authorization}s.
97       */
98      public static final String AUTHORIZATIONS_ELEMENT = XMLContextEventTypeUnmarshaller.AUTHORIZATIONS_ELEMENT;
99      /**
100      * The local name of child {@link Element} that holds marshaled
101      * {@link ContextEventAction}s.
102      */
103     public static final String ACTIONS_ELEMENT = "Actions";
104     /**
105      * The local name of child {@link Element} that holds marshaled
106      * {@link UnprocessedAction}s.
107      */
108     public static final String UNPROCESSED_ACTIONS_ELEMENT = "Unprocessed";
109     /**
110      * The local name of child {@link Element} that holds marshaled
111      * {@link CurrentAction}.
112      */
113     public static final String CURRENT_ACTION_ELEMENT = "Current";
114     /**
115      * The local name of child {@link Element} that holds marshaled
116      * {@link ProcessedAction}s.
117      */
118     public static final String PROCESSED_ACTIONS_ELEMENT = "Processed";
119 
120     /**
121      * The {@link UnmarshallerBuilder} for the source ID of a
122      * {@link XMLContextEvent}.
123      */
124     public static final UnmarshallerBuilder<String> SOURCE_ID = getBuilder(new StringAttribute.Builder().withName(SOURCE_ID_ATTRIBUTE));
125     /**
126      * The {@link UnmarshallerBuilder} for the time stamp of a
127      * {@link XMLContextEvent}.
128      */
129     public static final UnmarshallerBuilder<Date> TIME_STAMP = getBuilder(new DateAttribute.Builder().withName(TIME_STAMP_ATTRIBUTE));
130     /**
131      * The {@link UnmarshallerBuilder} for the name of the
132      * {@link XMLContextEventType} of a {@link XMLContextEvent}.
133      */
134     public static final UnmarshallerBuilder<String> TYPE = getBuilder(new StringAttribute.Builder().withName(TYPE_ATTRIBUTE));
135     /**
136      * The {@link UnmarshallerBuilder} for the time-to-live of a
137      * {@link XMLContextEvent}.
138      */
139     public static final UnmarshallerBuilder<Long> TTL = getBuilder(new LongAttribute.Builder().withName(TTL_ATTRIBUTE));
140     /**
141      * The {@link UnmarshallerBuilder} for the priority of a
142      * {@link XMLContextEvent}.
143      */
144     public static final UnmarshallerBuilder<Integer> PRIORITY = getBuilder(new IntegerAttribute.Builder().withName(PRIORITY_ATTRIBUTE));
145     /**
146      * The {@link UnmarshallerBuilder} for the {@link Attribute}s of a
147      * {@link XMLContextEvent}.
148      */
149     public static final UnmarshallerBuilder<List<Attribute>> ATTRIBUTES = getBuilder(
150             ATTRIBUTES_ELEMENT, AttributeUnmarshaller.getListBuilder());
151     /**
152      * The {@link UnmarshallerBuilder} for the {@link Authorization}s of a
153      * {@link XMLContextEvent}.
154      */
155     public static final UnmarshallerBuilder<List<Authorization>> AUTHORIZATIONS = getBuilder(
156             AUTHORIZATIONS_ELEMENT, AuthorizationUnmarshaller.getListBuilder());
157     /**
158      * The {@link UnmarshallerBuilder} for the {@link UnprocessedAction}s of a
159      * {@link XMLContextEvent}.
160      */
161     public static final UnmarshallerBuilder<List<UnprocessedAction>> UNPROCESSED_ACTIONS = getActionsBuilder(
162             UNPROCESSED_ACTIONS_ELEMENT,
163             UnprocessedActionUnmarshaller.getListBuilder());
164     /**
165      * The {@link UnmarshallerBuilder} for the {@link CurrentAction} of a
166      * {@link XMLContextEvent}.
167      */
168     public static final UnmarshallerBuilder<CurrentAction> CURRENT_ACTION = getActionsBuilder(
169             CURRENT_ACTION_ELEMENT, new CurrentActionUnmarshaller.Builder());
170     /**
171      * The {@link UnmarshallerBuilder} for the {@link ProcessedAction}s of a
172      * {@link XMLContextEvent}.
173      */
174     public static final UnmarshallerBuilder<List<ProcessedAction>> PROCESSED_ACTIONS = getActionsBuilder(
175             PROCESSED_ACTIONS_ELEMENT,
176             ProcessedActionUnmarshaller.getListBuilder());
177 
178     private static final MarshallerBuilder<List<ContextEventAction>> ACTIONS_REMOVER_BUILDER = new ElementMarshaller.Builder<List<ContextEventAction>>().withChildName(
179             EVENT_ELEMENT).withBuilder(
180             new ElementMarshaller.Builder<List<ContextEventAction>>().withChildName(
181                     ACTIONS_ELEMENT).withBuilder(
182                     ContextEventActionMarshaller.getListBuilder()).withStrategy(
183                     RemoveStrategy.RemoveElement)).withStrategy(
184             RemoveStrategy.Delegate);
185 
186     private final Document document;
187     private final Unmarshaller<Long> ttl;
188     private final Unmarshaller<Integer> priority;
189     private final Unmarshaller<List<Attribute>> attributes;
190     private final Unmarshaller<List<Authorization>> authorizations;
191     private final Unmarshaller<List<UnprocessedAction>> unprocessedActions;
192     private final Unmarshaller<CurrentAction> currentAction;
193     private final Unmarshaller<List<ProcessedAction>> processedActions;
194     private final Marshaller<List<ContextEventAction>> actionsRemover;
195 
196     /**
197      * Create instance of the {@link XMLContextEvent} with specified
198      * {@link XMLContextEventType} and {@link Document}. The specified
199      * {@link Document} is deep cloned, thus ensuring that any later changes on
200      * this instance will not be reflected in {@link Document} saved in the
201      * {@link XMLContextEvent}.
202      * 
203      * @param eventType the {@link XMLContextEventType} of this
204      *        {@link XMLContextEvent}.
205      * @param document the event {@link Document}.
206      * @throws MalformedDocumentException if specified {@link Document} is
207      *         malformed {@link XMLContextEvent}.
208      * @throws NullPointerException if specified {@link Document} is
209      *         <code>null</code>.
210      */
211     public XMLContextEvent(final XMLContextEventType eventType,
212             final Document document) throws MalformedDocumentException {
213         super(eventType, getProperty(document, SOURCE_ID), getProperty(
214                 document, TIME_STAMP));
215 
216         final String expectedType = eventType.getName();
217         final String actualType = getProperty(document, TYPE);
218         if (!expectedType.equals(actualType)) {
219             throw new MalformedDocumentException(MessageFormat.format(
220                     "Event has type ''{0}'', but ''{1}'' expected.",
221                     actualType, expectedType));
222         }
223 
224         this.document = (Document) document.cloneNode(true);
225 
226         this.ttl = getUnmarshaller(TTL);
227         this.priority = getUnmarshaller(PRIORITY);
228         this.attributes = getUnmarshaller(ATTRIBUTES);
229         this.authorizations = getUnmarshaller(AUTHORIZATIONS);
230         this.unprocessedActions = getUnmarshaller(UNPROCESSED_ACTIONS);
231         this.currentAction = getUnmarshaller(CURRENT_ACTION);
232         this.processedActions = getUnmarshaller(PROCESSED_ACTIONS);
233         this.actionsRemover = ACTIONS_REMOVER_BUILDER.withElement(
234                 this.document.getDocumentElement()).build();
235 
236         setTTL(this.ttl.unmarshal());
237         setPriority(this.priority.unmarshal());
238         try {
239             @SuppressWarnings("hiding")
240             final List<Attribute> attributes = this.attributes.unmarshal();
241             set(attributes.toArray(new Attribute[attributes.size()]));
242         } catch (final MissingFieldException ex) {
243             if (!ATTRIBUTES_ELEMENT.equals(ex.getFieldName())) {
244                 throw ex;
245             }
246         }
247         try {
248             @SuppressWarnings("hiding")
249             final List<Authorization> authorizations = this.authorizations.unmarshal();
250             set(authorizations.toArray(new Authorization[authorizations.size()]));
251         } catch (final MissingFieldException ex) {
252             if (!AUTHORIZATIONS_ELEMENT.equals(ex.getFieldName())) {
253                 throw ex;
254             }
255         }
256         final List<ContextEventAction> actions = new LinkedList<ContextEventAction>();
257         try {
258             actions.addAll(this.processedActions.unmarshal());
259         } catch (final MissingFieldException ex) {
260             if ((!ACTIONS_ELEMENT.equals(ex.getFieldName()))
261                     && (!PROCESSED_ACTIONS_ELEMENT.equals(ex.getFieldName()))) {
262                 throw ex;
263             }
264         }
265         try {
266             actions.add(this.currentAction.unmarshal());
267         } catch (final MissingFieldException ex) {
268             if ((!ACTIONS_ELEMENT.equals(ex.getFieldName()))
269                     && (!CURRENT_ACTION_ELEMENT.equals(ex.getFieldName()))) {
270                 throw ex;
271             }
272         }
273         try {
274             actions.addAll(this.unprocessedActions.unmarshal());
275         } catch (final MissingFieldException ex) {
276             if ((!ACTIONS_ELEMENT.equals(ex.getFieldName()))
277                     && (!UNPROCESSED_ACTIONS_ELEMENT.equals(ex.getFieldName()))) {
278                 throw ex;
279             }
280         }
281         super.set(actions.toArray(new ContextEventAction[actions.size()]));
282     }
283 
284     /**
285      * Create instance of {@link XMLContextEvent} with specified
286      * {@link XMLContextEventType}, source ID and {@link Document}. The
287      * specified {@link Document} is deep cloned, thus ensuring that any later
288      * changes on this instance will not be reflected in {@link Document} saved
289      * in the {@link XMLContextEvent}.
290      * 
291      * @param eventType the {@link XMLContextEventType} of this
292      *        {@link XMLContextEvent}.
293      * @param sourceID the source ID of the {@link XMLContextEvent}.
294      * @param document the event {@link Document}.
295      * @throws MalformedDocumentException if specified {@link Document} is
296      *         malformed for specified {@link ContextEventType}.
297      * @throws NullPointerException if specified {@link XMLContextEventType},
298      *         source ID, or {@link Document} is <code>null</code>.
299      * @throws IllegalArgumentException if specified source ID is an empty or
300      *         blank string.
301      */
302     public XMLContextEvent(final XMLContextEventType eventType,
303             final String sourceID, final Document document)
304             throws MalformedDocumentException {
305         this(eventType, sourceID, Calendar.getInstance().getTime(), document);
306     }
307 
308     /**
309      * Create instance of {@link XMLContextEvent} with specified
310      * {@link XMLContextEventType}, source ID, time stamp and {@link Document}.
311      * The given {@link Document} is deep cloned, thus ensuring that any later
312      * changes on this instance will not be reflected in {@link Document} saved
313      * in the {@link XMLContextEvent}.
314      * 
315      * @param eventType the {@link XMLContextEventType} of the
316      *        {@link XMLContextEvent}.
317      * @param sourceID the source ID of the {@link XMLContextEvent}.
318      * @param timeStamp the time stamp for the {@link XMLContextEvent}.
319      * @param document the event as XML DOM document.
320      * @throws MalformedDocumentException if specified {@link Document} is
321      *         malformed for specified {@link XMLContextEventType}.
322      * @throws NullPointerException if specified event type, source ID, time
323      *         stamp or document is <code>null</code>.
324      * @throws IllegalArgumentException if specified source ID is an empty or
325      *         blank string.
326      */
327     public XMLContextEvent(final XMLContextEventType eventType,
328             final String sourceID, final Date timeStamp, final Document document)
329             throws MalformedDocumentException {
330         super(eventType, sourceID, timeStamp);
331 
332         if (null == document) {
333             throw new NullPointerException("XML DOM document cannot be null.");
334         }
335 
336         eventType.validate(document);
337 
338         this.document = (Document) document.cloneNode(true);
339 
340         getUnmarshaller(SOURCE_ID).marshal(sourceID);
341         getUnmarshaller(TIME_STAMP).marshal(timeStamp);
342         getUnmarshaller(TYPE).marshal(eventType.getName());
343 
344         this.ttl = getUnmarshaller(TTL);
345         this.priority = getUnmarshaller(PRIORITY);
346         this.attributes = getUnmarshaller(ATTRIBUTES);
347         this.authorizations = getUnmarshaller(AUTHORIZATIONS);
348         this.unprocessedActions = getUnmarshaller(UNPROCESSED_ACTIONS);
349         this.currentAction = getUnmarshaller(CURRENT_ACTION);
350         this.processedActions = getUnmarshaller(PROCESSED_ACTIONS);
351         this.actionsRemover = ACTIONS_REMOVER_BUILDER.withElement(
352                 this.document.getDocumentElement()).build();
353     }
354 
355     /**
356      * Returns the {@link XMLContextEventType} associated with this
357      * {@link XMLContextEvent}.
358      * 
359      * @return the {@link XMLContextEventType} associated with this
360      *         {@link XMLContextEvent}.
361      */
362     @Override
363     public XMLContextEventType getType() {
364         return (XMLContextEventType) super.getType();
365     }
366 
367     /**
368      * @return {@link Document} with all COPAL {@link Element}s injected.
369      */
370     public Document getDocument() {
371         this.ttl.marshal(getTTL());
372         this.priority.marshal(getPriority());
373 
374         @SuppressWarnings("hiding")
375         final Attribute[] attributes = getAttributes();
376         if (attributes.length > 0) {
377             this.attributes.marshal(Arrays.asList(attributes));
378         } else {
379             this.attributes.remove();
380         }
381 
382         @SuppressWarnings("hiding")
383         final Authorization[] authorizations = getAuthorizations();
384         if (authorizations.length > 0) {
385             this.authorizations.marshal(Arrays.asList(authorizations));
386         } else {
387             this.authorizations.remove();
388         }
389 
390         final ContextEventAction[] actions = getActions();
391         if (actions.length > 0) {
392             final int currentActionIndex = getCurrentActionIndex();
393 
394             @SuppressWarnings("hiding")
395             final List<UnprocessedAction> unprocessedActions = new LinkedList<UnprocessedAction>();
396             for (int i = currentActionIndex + 1; i < actions.length; i++) {
397                 unprocessedActions.add((UnprocessedAction) getAction(i));
398             }
399             if (unprocessedActions.isEmpty()) {
400                 this.unprocessedActions.remove();
401             } else {
402                 this.unprocessedActions.marshal(unprocessedActions);
403             }
404 
405             if ((currentActionIndex >= 0)
406                     && (currentActionIndex < actions.length)) {
407                 this.currentAction.marshal((CurrentAction) actions[currentActionIndex]);
408             } else {
409                 this.currentAction.remove();
410             }
411 
412             @SuppressWarnings("hiding")
413             final List<ProcessedAction> processedActions = new LinkedList<ProcessedAction>();
414             for (int i = 0; i < currentActionIndex; i++) {
415                 processedActions.add((ProcessedAction) actions[i]);
416             }
417             if (processedActions.isEmpty()) {
418                 this.processedActions.remove();
419             } else {
420                 this.processedActions.marshal(processedActions);
421             }
422         } else {
423             this.actionsRemover.remove();
424         }
425 
426         return this.document;
427     }
428 
429     /**
430      * Return the unmarshaled property of a {@link XMLContextEvent} that is
431      * marshaled in specified {@link Document} using specified
432      * {@link UnmarshallerBuilder}.
433      * 
434      * @param <T> the type of unmarshaled property.
435      * @param document the {@link Document}
436      * @param builder the {@link UnmarshallerBuilder}.
437      * @return the unmarshaled property of a {@link XMLContextEvent}
438      * @throws FailedUnmarshallingException if unmarshalling was unsuccessful.
439      */
440     public static <T> T getProperty(final Document document,
441             final UnmarshallerBuilder<T> builder)
442             throws FailedUnmarshallingException {
443         return getUnmarshaller(document, builder).unmarshal();
444     }
445 
446     private <T> Unmarshaller<T> getUnmarshaller(
447             final UnmarshallerBuilder<T> builder) {
448         return getUnmarshaller(this.document, builder);
449     }
450 
451     private static <T> Unmarshaller<T> getUnmarshaller(final Document document,
452             final UnmarshallerBuilder<T> builder) {
453         return builder.withElement(document.getDocumentElement()).build();
454     }
455 
456     private static <T> UnmarshallerBuilder<T> getBuilder(
457             final UnmarshallerBuilder<T> builder) {
458         return new ElementUnmarshaller.Builder<T>().withChildName(EVENT_ELEMENT).withBuilder(
459                 builder).withStrategy(RemoveStrategy.Delegate);
460     }
461 
462     private static <T> UnmarshallerBuilder<T> getBuilder(final String element,
463             final UnmarshallerBuilder<T> builder) {
464         return getBuilder(new ElementUnmarshaller.Builder<T>().withChildName(
465                 element).withBuilder(builder).withStrategy(
466                 RemoveStrategy.RemoveElement));
467     }
468 
469     private static <T> UnmarshallerBuilder<T> getActionsBuilder(
470             final String element, final UnmarshallerBuilder<T> builder) {
471         return getBuilder(new ElementUnmarshaller.Builder<T>().withChildName(
472                 ACTIONS_ELEMENT).withBuilder(
473                 new ElementUnmarshaller.Builder<T>().withChildName(element).withBuilder(
474                         builder).withStrategy(RemoveStrategy.RemoveElement)).withStrategy(
475                 RemoveStrategy.Delegate));
476     }
477 
478     /**
479      * Retrievable property marshaled in a {@link XMLContextEvent}.
480      * 
481      * @author sanjin
482      */
483     public static enum Property {
484 
485         /**
486          * The name of the {@link XMLContextEventType} associated with a
487          * {@link XMLContextEvent}.
488          */
489         Type(String.class) {
490             @Override
491             public String getXPath() {
492                 return EVENT_ELEMENT_XPATH + "/@" + TYPE_ATTRIBUTE;
493             }
494         },
495         /**
496          * The source ID of a {@link XMLContextEvent}.
497          */
498         SourceID(String.class) {
499 
500             @Override
501             public String getXPath() {
502                 return EVENT_ELEMENT_XPATH + "/@" + SOURCE_ID_ATTRIBUTE;
503             }
504         },
505         /**
506          * The time stamp of a {@link XMLContextEvent}.
507          */
508         TimeStamp(String.class) {
509 
510             @Override
511             public String getXPath() {
512                 return EVENT_ELEMENT_XPATH + "/@" + TIME_STAMP_ATTRIBUTE;
513             }
514         },
515         /**
516          * The time-to-live of a {@link XMLContextEvent}.
517          */
518         TimeToLive(long.class) {
519 
520             @Override
521             public String getXPath() {
522                 return EVENT_ELEMENT_XPATH + "/@" + TTL_ATTRIBUTE;
523             }
524         },
525         /**
526          * The priority of a {@link XMLContextEvent}.
527          */
528         Priority(int.class) {
529 
530             @Override
531             public String getXPath() {
532                 return EVENT_ELEMENT_XPATH + "/@" + PRIORITY_ATTRIBUTE;
533             }
534         },
535         /**
536          * The name of the {@link CurrentAction} in a {@link XMLContextEvent}.
537          */
538         CurrentAction(String.class) {
539 
540             @Override
541             public String getXPath() {
542                 return MessageFormat.format("{0}/{1}:{2}/{1}:{3}/@{4}",
543                         EVENT_ELEMENT_XPATH, COPAL_PREFIX, ACTIONS_ELEMENT,
544                         CURRENT_ACTION_ELEMENT,
545                         CurrentActionUnmarshaller.NAME_ATTRIBUTE);
546             }
547         },
548         /**
549          * If a {@link XMLContextEvent} is fully processed i.e. there are no
550          * more {@link UnprocessedAction}s and no {@link CurrentAction}.
551          */
552         Processed(boolean.class) {
553 
554             @Override
555             public String getXPath() {
556                 return MessageFormat.format(
557                         "(count({0}/{1}:{2}/{1}:{3}) = 0) and (count({0}/{1}:{2}/{1}:{4}) = 0)",
558                         EVENT_ELEMENT_XPATH, COPAL_PREFIX, ACTIONS_ELEMENT,
559                         UNPROCESSED_ACTIONS_ELEMENT, CURRENT_ACTION_ELEMENT);
560             }
561         };
562 
563         /**
564          * The XPath to retrieve the {@link XMLContextEvent#EVENT_ELEMENT}
565          * {@link Element}.
566          */
567         protected static final String EVENT_ELEMENT_XPATH = MessageFormat.format(
568                 "//{0}:{1}", COPAL_PREFIX, EVENT_ELEMENT);
569 
570         private final Class<?> type;
571 
572         /**
573          * Create {@link Property} with specified type.
574          * 
575          * @param type the type.
576          */
577         Property(final Class<?> type) {
578             this.type = type;
579         }
580 
581         /**
582          * Return the type of this {@link Property}.
583          * 
584          * @return the type of this {@link Property}.
585          */
586         public Class<?> getType() {
587             return this.type;
588         }
589 
590         /**
591          * Return the XPath to retrieve the value of this {@link Property}.
592          * 
593          * @return the XPath to retrieve the value of this {@link Property}.
594          */
595         public abstract String getXPath();
596 
597         @Override
598         public String toString() {
599             return Constants.COPAL_PREFIX + ":" + super.toString();
600         }
601     }
602 }