Source code: com/hp/hpl/jena/ontology/event/OntEventManager.java
1 /*****************************************************************************
2 * Source code information
3 * -----------------------
4 * Original author Ian Dickinson, HP Labs Bristol
5 * Author email ian.dickinson@hp.com
6 * Package Jena 2
7 * Web http://sourceforge.net/projects/jena/
8 * Created 10-Sep-2003
9 * Filename $RCSfile: OntEventManager.java,v $
10 * Revision $Revision: 1.6 $
11 * Release status $State: Exp $
12 *
13 * Last modified on $Date: 2005/02/21 12:06:02 $
14 * by $Author: andy_seaborne $
15 *
16 * (c) Copyright 2001, 2002, 2003, 2004, 2005 Hewlett-Packard Development Company, LP
17 * [See end of file]
18 *****************************************************************************/
19
20 // Package
21 ///////////////
22 package com.hp.hpl.jena.ontology.event;
23
24
25
26 // Imports
27 ///////////////
28 import java.util.*;
29
30 import com.hp.hpl.jena.ontology.*;
31 import com.hp.hpl.jena.rdf.listeners.StatementListener;
32 import com.hp.hpl.jena.rdf.model.*;
33 import com.hp.hpl.jena.vocabulary.*;
34 import com.hp.hpl.jena.vocabulary.OntEventsVocab;
35
36
37 /**
38 * <p>
39 * An adapter that translates RDF model-level changes into higher level changes that
40 * are appropriate to ontology users. The large number of specific events that the
41 * ontology model can produce makes the traditional Java style of listener interface
42 * impractical. Instead, this event manager allows the user to register
43 * {@linkplain OntEventHandler handlers}, based on command pattern, against specific
44 * {@linkplain OntEventsVocab event types}.
45 * </p>
46 * <p>
47 * For example, to register a handler for the declaration of an ontology class:
48 * </p>
49 * <pre>
50 * OntModel m = ...
51 * OntEventManager em = m.getEventManager();
52 * em.addHandler( OntEvents.CLASS_DECLARATION,
53 * new OntEventHandler() {
54 * public void action( Resource ev, boolean added,
55 * RDFNode arg0, RDFNode arg1 ) {
56 * OntClass c = (OntClass) arg0;
57 * if (added) {
58 * // class c added to model
59 * }
60 * else {
61 * // class c removed from model
62 * }
63 * }
64 * }
65 * );
66 * </pre>
67 * <p>
68 * This listener acts as an adapter for graph events (i.e. adding and
69 * removing triples), converting them to higher-level ontology events. This is non-trivial, because
70 * Jena currently doesn't have a means of batching changes so that only consistent
71 * graph states are seen. For efficiency in non event-using models, this listener
72 * is only attached as a statement listener to the underlying graph when the first
73 * ontology event listener is added.
74 * </p>
75 *
76 * @author Ian Dickinson, HP Labs
77 * (<a href="mailto:Ian.Dickinson@hp.com" >email</a>)
78 * @version CVS $Id: OntEventManager.java,v 1.6 2005/02/21 12:06:02 andy_seaborne Exp $
79 */
80 public class OntEventManager
81 extends StatementListener
82 {
83 // Constants
84 //////////////////////////////////
85
86 // Static variables
87 //////////////////////////////////
88
89 /** Initialisation data for the rdf:type to event type table */
90 private static Object[][] s_rdfTypeInit = {
91 {OntEventsVocab.classDeclaration, new ProfileAccessor() {public Resource get(Profile p) {return p.CLASS();} } },
92 {OntEventsVocab.datarangeDeclaration, new ProfileAccessor() {public Resource get(Profile p) {return p.DATARANGE();} } },
93 {OntEventsVocab.propertyDeclaration, new ProfileAccessor() {public Resource get(Profile p) {return p.PROPERTY();} } },
94 {OntEventsVocab.objectPropertyDeclaration, new ProfileAccessor() {public Resource get(Profile p) {return p.OBJECT_PROPERTY();} } },
95 {OntEventsVocab.datatypePropertyDeclaration, new ProfileAccessor() {public Resource get(Profile p) {return p.DATATYPE_PROPERTY();} } },
96 {OntEventsVocab.transitivePropertyDeclaration, new ProfileAccessor() {public Resource get(Profile p) {return p.TRANSITIVE_PROPERTY();} } },
97 {OntEventsVocab.symmetricPropertyDeclaration, new ProfileAccessor() {public Resource get(Profile p) {return p.SYMMETRIC_PROPERTY();} } },
98 {OntEventsVocab.functionalPropertyDeclaration, new ProfileAccessor() {public Resource get(Profile p) {return p.FUNCTIONAL_PROPERTY();} } },
99 {OntEventsVocab.inverseFunctionalPropertyDeclaration, new ProfileAccessor() {public Resource get(Profile p) {return p.INVERSE_FUNCTIONAL_PROPERTY();} } },
100 {OntEventsVocab.annotationPropertyDeclaration, new ProfileAccessor() {public Resource get(Profile p) {return p.ANNOTATION_PROPERTY();} } },
101 {OntEventsVocab.ontologyPropertyDeclaration, new ProfileAccessor() {public Resource get(Profile p) {return p.ONTOLOGY_PROPERTY();} } },
102 {OntEventsVocab.restrictionDeclaration, new ProfileAccessor() {public Resource get(Profile p) {return p.RESTRICTION();} } },
103 {OntEventsVocab.allDifferentDeclaration, new ProfileAccessor() {public Resource get(Profile p) {return p.ALL_DIFFERENT();} } },
104 {OntEventsVocab.ontologyDeclaration, new ProfileAccessor() {public Resource get(Profile p) {return p.ONTOLOGY();} } },
105 };
106
107 /** Initialisation data for the predicate to event type table */
108 private static Object[][] s_predicateInit = {
109 {OntEventsVocab.intersectionOf, new ProfileAccessor() {public Resource get(Profile p) {return p.INTERSECTION_OF();} } },
110 {OntEventsVocab.equivalentClass, new ProfileAccessor() {public Resource get(Profile p) {return p.EQUIVALENT_CLASS();} } },
111 {OntEventsVocab.disjointWith, new ProfileAccessor() {public Resource get(Profile p) {return p.DISJOINT_WITH();} } },
112 {OntEventsVocab.equivalentProperty, new ProfileAccessor() {public Resource get(Profile p) {return p.EQUIVALENT_PROPERTY();} } },
113 {OntEventsVocab.sameAs, new ProfileAccessor() {public Resource get(Profile p) {return p.SAME_AS();} } },
114 {OntEventsVocab.differentFrom, new ProfileAccessor() {public Resource get(Profile p) {return p.DIFFERENT_FROM();} } },
115 {OntEventsVocab.distinctMembers, new ProfileAccessor() {public Resource get(Profile p) {return p.DISTINCT_MEMBERS();} } },
116 {OntEventsVocab.unionOf, new ProfileAccessor() {public Resource get(Profile p) {return p.UNION_OF();} } },
117 {OntEventsVocab.intersectionOf, new ProfileAccessor() {public Resource get(Profile p) {return p.INTERSECTION_OF();} } },
118 {OntEventsVocab.complementOf, new ProfileAccessor() {public Resource get(Profile p) {return p.COMPLEMENT_OF();} } },
119 {OntEventsVocab.oneOf, new ProfileAccessor() {public Resource get(Profile p) {return p.ONE_OF();} } },
120 {OntEventsVocab.onProperty, new ProfileAccessor() {public Resource get(Profile p) {return p.ON_PROPERTY();} } },
121 {OntEventsVocab.allValuesFrom, new ProfileAccessor() {public Resource get(Profile p) {return p.ALL_VALUES_FROM();} } },
122 {OntEventsVocab.hasValue, new ProfileAccessor() {public Resource get(Profile p) {return p.HAS_VALUE();} } },
123 {OntEventsVocab.someValuesFrom, new ProfileAccessor() {public Resource get(Profile p) {return p.SOME_VALUES_FROM();} } },
124 {OntEventsVocab.minCardinality, new ProfileAccessor() {public Resource get(Profile p) {return p.MIN_CARDINALITY();} } },
125 {OntEventsVocab.maxCardinality, new ProfileAccessor() {public Resource get(Profile p) {return p.MAX_CARDINALITY();} } },
126 {OntEventsVocab.cardinalityQ, new ProfileAccessor() {public Resource get(Profile p) {return p.CARDINALITY_Q();} } },
127 {OntEventsVocab.minCardinalityQ, new ProfileAccessor() {public Resource get(Profile p) {return p.MIN_CARDINALITY_Q();} } },
128 {OntEventsVocab.maxCardinalityQ, new ProfileAccessor() {public Resource get(Profile p) {return p.MAX_CARDINALITY_Q();} } },
129 {OntEventsVocab.cardinality, new ProfileAccessor() {public Resource get(Profile p) {return p.CARDINALITY();} } },
130 {OntEventsVocab.inverseOf, new ProfileAccessor() {public Resource get(Profile p) {return p.INVERSE_OF();} } },
131 {OntEventsVocab.imports, new ProfileAccessor() {public Resource get(Profile p) {return p.IMPORTS();} } },
132 {OntEventsVocab.versionInfo, new ProfileAccessor() {public Resource get(Profile p) {return p.VERSION_INFO();} } },
133 {OntEventsVocab.priorVersion, new ProfileAccessor() {public Resource get(Profile p) {return p.PRIOR_VERSION();} } },
134 {OntEventsVocab.backwardCompatibleWith, new ProfileAccessor() {public Resource get(Profile p) {return p.BACKWARD_COMPATIBLE_WITH();} } },
135 {OntEventsVocab.incompatibleWith, new ProfileAccessor() {public Resource get(Profile p) {return p.INCOMPATIBLE_WITH();} } },
136 };
137
138
139 // Instance variables
140 //////////////////////////////////
141
142 /** Map from event types to handlers */
143 private Map m_handlers = new HashMap();
144
145 /** Map from rdf:type to event type */
146 private Map m_rdfTypeToEventType = new HashMap();
147
148 /** Map from predicate to event type */
149 private Map m_predicateToEventType = new HashMap();
150
151 /** Default event handler */
152 private OntEventHandler m_defaultHandler = null;
153
154
155 // Constructors
156 //////////////////////////////////
157
158 /**
159 * <p>Construct an ontology event manager for the given ontology model.
160 * This involves registering adapters for the ontology events corresponding
161 * to the language profile of the given model.</p>
162 * @param m An ontology model
163 */
164 public OntEventManager( OntModel m ) {
165 Profile p = m.getProfile();
166 initialiseTable( m_rdfTypeToEventType, p, s_rdfTypeInit );
167 initialiseTable( m_predicateToEventType, p, s_predicateInit );
168 }
169
170
171 // External signature methods
172 //////////////////////////////////
173
174 /**
175 * <p>Handle the addition of a statement to the model.</p>
176 * @param s The added statement
177 */
178 public void addedStatement( Statement s ) {
179 processStatement( s, true );
180 }
181
182
183 /**
184 * <p>Handle the removal of a statement to the model</p>
185 * @param s The removed statement
186 */
187 public void removedStatement( Statement s ) {
188 processStatement( s, false );
189 }
190
191
192 /**
193 * <p>Raise an event to be handled by the attached event handlers.</p>
194 * @param event The resource representing the event type
195 * @param added True if this is an addition to the model, false otherwise
196 * @param source The model that caused the event to be raised
197 * @param arg0 The first argument to the event
198 * @param arg1 The second argument to the event, or null
199 * @param arg2 The third argument to the event, or null
200 */
201 public void raise( Resource event, boolean added, Model source, RDFNode arg0, RDFNode arg1, RDFNode arg2 ) {
202 OntEventHandler h = getHandler( event );
203 if (h != null) {
204 h.action( event, added, source, arg0, arg1, arg2 );
205 }
206 else if (m_defaultHandler != null) {
207 // if no assigned handler, call the default handler
208 m_defaultHandler.action( event, added, source, arg0, arg1, arg2 );
209 }
210 }
211
212
213 /**
214 * <p>Add the given handler as the default event handler, which will be invoked if
215 * no other handler is applicable to a given event.</p>
216 * @param handler The event handler object
217 */
218 public void addDefaultHandler( OntEventHandler handler ) {
219 m_defaultHandler = handler;
220 }
221
222
223 /**
224 * <p>Add the given handler as the handler for the given event type, replacing
225 * any existing handler.</p>
226 * @param event The event type to be handled, as a resource
227 * @param handler The event handler object
228 */
229 public void addHandler( Resource event, OntEventHandler handler ) {
230 m_handlers.put( event, handler );
231 }
232
233
234 /**
235 * <p>Add the given handlers as the handler for the given event types, replacing
236 * any existing handlers.</p>
237 * @param handlers An array of pairs, where the first element of each pair
238 * is the resource denoting the event to be handled, and the second is the
239 * handler object
240 */
241 public void addHandlers( Object[][] handlers ) {
242 for (int i = 0; i < handlers.length; ) {
243 Resource r = (Resource) handlers[i][0];
244 OntEventHandler h = (OntEventHandler) handlers[i][1];
245 addHandler( r, h );
246 }
247 }
248
249 /**
250 * <p>Answer the event handler for the given event, or null if not defined</p>
251 * @param event An event type to look up
252 * @return The current handler for the event, or null
253 */
254 public OntEventHandler getHandler( Resource event ) {
255 return (OntEventHandler) m_handlers.get( event );
256 }
257
258
259 /**
260 * <p>Answer the default event handler, or null if not defined</p>
261 * @return The default event handler, or null
262 */
263 public OntEventHandler getDefaultHandler() {
264 return m_defaultHandler;
265 }
266
267
268 /**
269 * <p>Remove any existing handler for the given event type.</p>
270 * @param event The event for which the handler is to be removed
271 */
272 public void removeHandler( Resource event ) {
273 m_handlers.remove( event );
274 }
275
276
277 /**
278 * <p>Answer true if there is a defined handler for the given event type.</p>
279 * @param event An event type, as a resource
280 * @return True if there is a defined handler for the event
281 */
282 public boolean hasHandler( Resource event ) {
283 return m_handlers.containsKey( event );
284 }
285
286
287 /**
288 * <p>Answer an iterator over the events that are registered in this
289 * event manager.</p>
290 * @return An iterator over the event types of events that have registered handlers
291 */
292 public Iterator listRegisteredEvents() {
293 return m_handlers.keySet().iterator();
294 }
295
296
297 // Internal implementation methods
298 //////////////////////////////////
299
300 /**
301 * <p>Initialise the given map from static initialisation data. This will be a mapping
302 * that allows us to determine the correct ontology event type for a given triple.</p>
303 * @param map The map to update
304 * @param p An ontology language profile
305 * @param source A table of initialisation data
306 */
307 private void initialiseTable( Map map, Profile p, Object[][] source ) {
308 for (int i = 0; i < source.length; i++) {
309 // get the initialisation data from the table
310 Resource evType = (Resource) source[i][0];
311 Resource key = ((ProfileAccessor) source[i][1]).get( p );
312
313 if (key != null) {
314 // is defined for this term
315 map.put( key, evType );
316 }
317 }
318 }
319
320
321 /**
322 * <p>Process an incoming added or removed statement to raise the appropriate ontology event.</p>
323 * @param s
324 * @param added
325 */
326 private void processStatement( Statement s, boolean added ) {
327 // first check if this an rdf:type statement
328 if (s.getPredicate().equals( RDF.type )) {
329 // yes - now is it a builtin type?
330 Resource type = s.getResource();
331 Resource evType = (Resource) m_rdfTypeToEventType.get( type );
332
333 if (evType != null) {
334 // was a known type, so we know which event to raise
335 raise( evType, added, s.getModel(), s.getSubject(), null, null );
336 }
337 else {
338 // must be an individual
339 raise( OntEventsVocab.individualDeclaration, added, s.getModel(), s.getSubject(), type, null );
340 }
341 }
342 else {
343 // not rdf:type, but may still be a known predicate
344 Property pred = s.getPredicate();
345 Resource evType = (Resource) m_predicateToEventType.get( pred );
346
347 if (evType != null) {
348 // was a known predicate, so we know which event to raise
349 raise( evType, added, s.getModel(), s.getSubject(), s.getObject(), null );
350 }
351 else {
352 // default - assume user data
353 raise( OntEventsVocab.userData, added, s.getModel(), s.getSubject(), pred, s.getObject() );
354 }
355 }
356 }
357
358
359 //==============================================================================
360 // Inner class definitions
361 //==============================================================================
362
363 /** Simple wrapper to allow us to dynamically extract elements from the profile */
364 private static interface ProfileAccessor {
365 public Resource get( Profile p );
366 }
367 }
368
369
370 /*
371 * (c) Copyright 2001, 2002, 2003, 2004, 2005 Hewlett-Packard Development Company, LP
372 * All rights reserved.
373 *
374 * Redistribution and use in source and binary forms, with or without
375 * modification, are permitted provided that the following conditions
376 * are met:
377 * 1. Redistributions of source code must retain the above copyright
378 * notice, this list of conditions and the following disclaimer.
379 * 2. Redistributions in binary form must reproduce the above copyright
380 * notice, this list of conditions and the following disclaimer in the
381 * documentation and/or other materials provided with the distribution.
382 * 3. The name of the author may not be used to endorse or promote products
383 * derived from this software without specific prior written permission.
384 *
385 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
386 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
387 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
388 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
389 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
390 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
391 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
392 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
393 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
394 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
395 */