1 /***
2 *
3 * The owl-s matcher software is subject to the GNU Lesser General
4 * Public License Version 2.1 (the "License"). You may not copy or use this
5 * file, in either source code or executable form, except in compliance
6 * with the License. You may obtain a copy of the License at
7 * http://www.fsf.org/licenses/lgpl.txt or http://www.opensource.org/.
8 *
9 * Software distributed under the License is distributed on an "AS IS"
10 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied without
11 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12 * PURPOSE. See the License for the specific language governing rights and
13 * limitations under the License.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this distribution; if not, write to the
17 *
18 * Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330,
20 * Boston, MA 02111-1307 USA
21 *
22 * Copyright (C) 2003-2004
23 * TU Berlin, FG IVS
24 * Stefan Tang,
25 * Christoph Liebetruth,
26 * Michael C. Jaeger,
27 *
28 * More information available at http://ivs.tu-berlin.de/
29 *
30 * $Id$
31 *
32 */
33 package de.tuberlin.ivs.owl.matching;
34
35 import jess.JessException;
36 import jess.ValueVector;
37 import jess.Token;
38 import jess.Fact;
39 import java.util.Vector;
40 import java.util.Iterator;
41 import java.util.ArrayList;
42 import edu.drexel.gicl.semanticweb.owljesskb.*;
43 import de.tuberlin.ivs.owl.service.OwlsParser;
44 import de.tuberlin.ivs.owl.service.Parameter;
45 import java.io.PrintStream;
46 import java.io.StringWriter;
47 import java.io.FileWriter;
48 import java.net.URL;
49
50 /***
51 * Provides functionality for reasoning in OWL. Implements the OWLJessKB.
52 *
53 * @author Stefan Tang (steftang@stanford.edu), Christoph Liebetruth (christophl@voelcker.com)
54 * @version 1.1
55 */
56 public class Reasoner
57 {
58
59 /***
60 * Denotes that either two concepts or two properties are equivalent.
61 */
62 public static int EQUIVALENT = 5;
63
64
65 /***
66 * Denotes the propertyMatch degree is subpropery.
67 */
68 public static int SUBPROPERTY = 2;
69
70 /***
71 * Denotes that the propertyMatch degree is unclassified, i.e. at least one
72 * property has not been classified.
73 */
74 public static int UNCLASSIFIED = 1;
75
76 /***
77 * Denotes that the conceptType degree is subsumes.
78 */
79 public static int SUBSUMES = 4;
80
81 /***
82 * Denotes that the conceptType degree is inverted subsume.
83 */
84 public static int SUBSUMES_INVERT = 3;
85
86 /***
87 * Denotes that no relation could be determined between two concepts or
88 * two properties.
89 */
90 public static int FAIL = 0;
91
92 /***
93 * The konwledge base that will store the information from all the
94 * ontologies that are loaded during the matching.
95 */
96 private OWLJessKB kb;
97
98 /***
99 * Constructor. Initializes the knowledge base and adds queries.
100 * @param out The stream to which output information is written.
101 */
102 public Reasoner(PrintStream out)
103 {
104 kb = new OWLJessKB();
105
106
107
108
109
110
111
112 kb.executeCommand( "(defquery query-parametertype (declare( variables ?x)) (triple(predicate \"http://www.daml.org/services/owl-s/1.0/Process.owl#parameterType\")(subject ?x)(object ?y)))" );
113 kb.executeCommand( "(defquery query-subproperty (declare (variables ?y)) (triple(predicate \"http://www.w3.org/2000/01/rdf-schema#subPropertyOf\")(subject ?x)(object ?y)))" );
114 kb.executeCommand( "(defquery query-subclass (declare (variables ?y)) (triple(predicate \"http://www.w3.org/2000/01/rdf-schema#subClassOf\")(subject ?x)(object ?y)))" );
115 kb.executeCommand( "(defquery query-equivalentClass (declare (variables ?y)) (triple(predicate \"http://www.w3.org/2002/07/owl#equivalentClass\")(subject ?x)(object ?y)))" );
116 kb.executeCommand( "(defquery query-equivalentProperty (declare (variables ?y)) (triple(predicate \"http://www.w3.org/2002/07/owl#equivalentProperty\")(subject ?x)(object ?y)))" );
117
118
119 kb.executeCommand( "(defquery rdfType (declare( variables ?x)) (triple(predicate \"http://www.w3.org/1999/02/22-rdf-syntax-ns#type\")(subject ?x)(object ?y)))" );
120 kb.executeCommand( "(defquery presentsProfile (triple(predicate \"http://www.daml.org/services/owl-s/1.0/Service.owl#presents\")(subject ?x)(object ?y)))" );
121 kb.executeCommand( "(defquery profilePresentedBy (declare( variables ?x)) (triple(predicate \"http://www.daml.org/services/owl-s/1.0/Service.owl#presentedBy\")(subject ?x)(object ?y)))" );
122 kb.executeCommand( "(defquery profileTextDescription (declare( variables ?x)) (triple(predicate \"http://www.daml.org/services/owl-s/1.0/Profile.owl#textDescription\")(subject ?x)(object ?y)))" );
123 kb.executeCommand( "(defquery profileServiceName (declare( variables ?x)) (triple(predicate \"http://www.daml.org/services/owl-s/1.0/Profile.owl#serviceName\")(subject ?x)(object ?y)))" );
124 kb.executeCommand( "(defquery stringLiteral (declare( variables ?x)) (triple(predicate \"http://www.w3.org/1999/02/22-rdf-syntax-ns#value\")(subject ?x)(object ?y)))" );
125 kb.executeCommand( "(defquery hasPrecondition (declare( variables ?x)) (triple(predicate \"http://www.daml.org/services/owl-s/1.0/Profile.owl#hasPrecondition\")(subject ?x)(object ?y)))" );
126 kb.executeCommand( "(defquery hasInput (declare( variables ?x)) (triple(predicate \"http://www.daml.org/services/owl-s/1.0/Profile.owl#hasInput\")(subject ?x)(object ?y)))" );
127 kb.executeCommand( "(defquery hasOutput (declare( variables ?x)) (triple(predicate \"http://www.daml.org/services/owl-s/1.0/Profile.owl#hasOutput\")(subject ?x)(object ?y)))" );
128 kb.executeCommand( "(defquery hasEffect (declare( variables ?x)) (triple(predicate \"http://www.daml.org/services/owl-s/1.0/Profile.owl#hasEffect\")(subject ?x)(object ?y)))" );
129 kb.executeCommand( "(defquery profileContactInformationIDs (declare( variables ?x)) (triple(predicate \"http://www.daml.org/services/owl-s/1.0/Profile.owl#contactInformation\")(subject ?x)(object ?y)))" );
130
131
132 kb.executeCommand( "(defquery actorName (declare( variables ?x)) (triple(predicate \"http://www.daml.org/services/owl-s/1.0/ActorDefault.owl#name\")(subject ?x)(object ?y)))" );
133 kb.executeCommand( "(defquery actorTitle (declare( variables ?x)) (triple(predicate \"http://www.daml.org/services/owl-s/1.0/ActorDefault.owl#title\")(subject ?x)(object ?y)))" );
134 kb.executeCommand( "(defquery actorPhone (declare( variables ?x)) (triple(predicate \"http://www.daml.org/services/owl-s/1.0/ActorDefault.owl#phone\")(subject ?x)(object ?y)))" );
135 kb.executeCommand( "(defquery actorFax (declare( variables ?x)) (triple(predicate \"http://www.daml.org/services/owl-s/1.0/ActorDefault.owl#fax\")(subject ?x)(object ?y)))" );
136 kb.executeCommand( "(defquery actorEMail (declare( variables ?x)) (triple(predicate \"http://www.daml.org/services/owl-s/1.0/ActorDefault.owl#email\")(subject ?x)(object ?y)))" );
137 kb.executeCommand( "(defquery actorPhysicalAddress (declare( variables ?x)) (triple(predicate \"http://www.daml.org/services/owl-s/1.0/ActorDefault.owl#physicalAddress\")(subject ?x)(object ?y)))" );
138 kb.executeCommand( "(defquery actorWebUrl(declare( variables ?x)) (triple(predicate \"http://www.daml.org/services/owl-s/1.0/ActorDefault.owl#webURL\")(subject ?x)(object ?y)))" );
139 }
140
141 public String getPropertyType( String parameter )
142 {
143 Vector values = this.simpleSubjectQuery("query-parametertype",new String[] { parameter }, 1, 2);
144 if ( values.size() == 0 )
145 return( null );
146 return( (String) values.get( 0 ) );
147 }
148
149 /***
150 * Loads a OWL file and adds all information to the knowledge base.
151 * @param path The URL of the OWL file.
152 * @throws java.net.ConnectException If any OWL file could not be opened. This
153 * also includes imported OWL files by the OWL file that path pointed to.
154 */
155 public void loadOwlFile(URL path) throws java.net.ConnectException
156 {
157 kb.loadOWLResource(path.toString());
158 }
159
160
161
162
163 /***
164 */
165 private Iterator queryKB(String queryName, String args[])
166 {
167
168 StringBuffer buff = new
169 StringBuffer("(store QUERY-RESULT (run-query " +
170 queryName + " ");
171 for (int i = 0; i < args.length; i++)
172 {
173 buff.append(args[i]);
174 buff.append(" ");
175 }
176 buff.append("))");
177
178 Iterator q = null;
179
180 try
181 {
182 kb.executeCommand(buff.toString());
183 q = (Iterator) kb.fetch("QUERY-RESULT").externalAddressValue(null);
184 }
185 catch (JessException e)
186 {
187
188 }
189 return q;
190
191
192 }
193
194
195
196 /***
197 * Run a query and return vector of subject value for the specified fact
198 * in each matching pattern. For example, to retrieve all objects of
199 * type daml:Thing you would use the following:
200 * <pre> Vector snuffy = simpleSubjectQuery("query-for-type",
201 * new String[] { "http://www.daml.org/2001/03/daml+oil#Thing" },
202 * 1);
203 * </pre>
204 *
205 * @param queryName The Jess name (label) of the query to be run.
206 * @param args An array containing the arguments to be passed in to
207 * the query. These and their order are declared using the
208 * (declare (variables -----)) construct in the Jess query definition.
209 * You may create new array on the fly in Java; one way of doing so is
210 * of the form new Meep[] { foo, bar, Chrisanthemum } where foo, bar, and
211 * Chrisanthemum are variables, constants, literals, etc and Meep is the
212 * covering type.
213 * @param fact The numerical position of the fact from which the object
214 * should be extracted. This effectively begins at 1 as (initial-fact) is
215 * always zero in a query result.
216 */
217 private Vector simpleSubjectQuery( String queryName,
218 String args[],
219 int fact,
220 int value )
221 {
222 for ( int i = 0; i < args.length; i++ )
223 args[ i ] = "\"" + args[ i ] + "\"";
224
225 Vector results = new Vector();
226 try
227 {
228 Iterator res;
229 res = this.queryKB(queryName, args);
230
231 while (res.hasNext())
232 {
233 Token t = (Token) res.next();
234 Fact f = t.fact(fact);
235 String obj = f.get( value ).toString();
236
237 if ( ( obj.startsWith( "\"" ) ) && ( obj.endsWith( "\"" ) ) )
238 results.add( obj.substring( 1, obj.length() - 1 ) );
239 else
240 results.add( obj );
241 }
242 }
243 catch ( Exception ex )
244 {
245 }
246 return results;
247
248
249 }
250
251 /***
252 * Determines whether a property is a subproperty of another property based
253 * on all information available thus far in the knowledge base.
254 * @param subProperty The name of the property that is supposed to be the subproperty.
255 * @param superProperty The name of the property that is supposed to be the superproperty.
256 * @return true if subProperty is a subproperty of superProperty, false otherwise.
257 */
258 public boolean isSubProperty(String subProperty, String superProperty)
259 {
260 Vector values = this.simpleSubjectQuery("query-subproperty",new String[] { superProperty },1, 1 );
261 for (int i=0; i<values.size(); i++)
262 {
263 String value = (String)values.elementAt(i);
264 value = value.substring(value.lastIndexOf("#")+1);
265 if (value.equals(subProperty))
266 {
267 return true;
268 }
269 }
270 return false;
271 }
272
273 /***
274 * Determines the degree of a property-match.
275 * @param propertyA
276 * @param propertyB
277 * @return An integer value denoting the degree of match for the two properties.
278 */
279 public int propertyMatch(String propertyA, String propertyB)
280 {
281 if (propertyA.equals("input") || propertyA.equals("output") ||
282 propertyB.equals("input") || propertyB.equals("output"))
283 {
284 return UNCLASSIFIED;
285 }
286 else if (propertyA.equals(propertyB) || sameProperty(propertyA,propertyB))
287 {
288 return EQUIVALENT;
289 }
290 else if (isSubProperty(propertyA,propertyB))
291 {
292 return SUBPROPERTY;
293 }
294 else
295 {
296 return FAIL;
297 }
298 }
299
300 /***
301 * Determines the degree of a concept match.
302 * @param conceptA
303 * @param conceptB
304 * @return An integer value denoting the degree of match for the two concepts.
305 */
306 public int conceptMatch(String conceptA, String conceptB)
307 {
308 if (conceptA.equals(conceptB) || sameClass(conceptA,conceptB))
309 {
310 return EQUIVALENT;
311 }
312 else if (subsumes(conceptA,conceptB))
313 {
314 return SUBSUMES;
315 }
316 else if (subsumes(conceptB,conceptA))
317 {
318 return SUBSUMES_INVERT;
319 }
320 else
321 {
322 return FAIL;
323 }
324 }
325
326 /***
327 * Determines if one concept subsumes another concept.
328 * @param conceptA
329 * @param conceptB
330 * @return True is conceptA subsumes conceptB, false otherwise.
331 */
332 private boolean subsumes(String conceptA, String conceptB)
333 {
334 Vector values = this.simpleSubjectQuery("query-subclass",new String[] {conceptA},1, 2);
335 for (int i=0; i<values.size(); i++)
336 {
337 String value = (String)values.elementAt(i);
338
339 if (value.equals(conceptB))
340 {
341 return true;
342 }
343 }
344 return false;
345 }
346
347 /***
348 * Determines if two concepts denote the same class by querying the knowledge
349 * base.
350 * @param conceptA
351 * @param conceptB
352 * @return True if conceptA and conceptB are equivalent, false otherwise.
353 */
354 private boolean sameClass(String conceptA, String conceptB)
355 {
356 Vector values = this.simpleSubjectQuery("query-equivalentClass",new String[] {conceptA},1, 1);
357 for (int i=0; i<values.size(); i++)
358 {
359 String value = (String)values.elementAt(i);
360
361 if (value.equals(conceptB))
362 {
363 return true;
364 }
365 }
366 return false;
367 }
368
369 /***
370 * Determines if two properties denote the same property by querying the knowledge
371 * base.
372 * @param propertyA
373 * @param propertyB
374 * @return True if conceptA and conceptB are equivalent, false otherwise.
375 */
376 private boolean sameProperty(String propertyA, String propertyB)
377 {
378 Vector values = this.simpleSubjectQuery("query-equivalentProperty",new String[] {propertyA},1, 1);
379 for (int i=0; i<values.size(); i++)
380 {
381 String value = (String)values.elementAt(i);
382
383 if (value.equals(propertyB))
384 {
385 return true;
386 }
387 }
388 return false;
389 }
390
391 /***
392 * Finds out the prefix for a concept that classifies a profile that is
393 * prefix-less. Queries the knowledge base. This becomes neccessary when
394 * a profile is parsed and the profile is subclassified, as the tag element
395 * does not contain the complete name. For exampple, if the concept name is
396 * BuyComputers (as defined in Classifications.owl), the returned string
397 * would be http://localhost:8080/Ontologies/Classifications.owl#BuyComputers.
398 * @param concept The concept that classifies a profile.
399 * @return The complete concept name, including prefix. Returns null if the
400 * concept is not found.
401 */
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417 /***
418 * Returns the rank for two parameters (IOPEs) based on their matching degree
419 * with respect to the property and type match.
420 * @param param1
421 * @param param2
422 * @return The rank specifying the degree of match for two parameters. See
423 * integer flags defined in this class for possible ranks.
424 */
425 public int rankForParameters(Parameter param1, Parameter param2)
426 {
427 int typeDegree = EQUIVALENT;
428
429
430
431
432
433 typeDegree = conceptMatch(param1.getRestrictedTo(),
434 param2.getRestrictedTo());
435
436 int propertyDegree = propertyMatch(param2.getPropertyName(),param1.getPropertyName());
437 if (typeDegree==FAIL || propertyDegree==FAIL)
438 {
439 return 0;
440 }
441 else if (propertyDegree==UNCLASSIFIED)
442 {
443 if (typeDegree==SUBSUMES_INVERT)
444 {
445 return 1;
446 } if (typeDegree==SUBSUMES)
447 {
448 return 2;
449 }
450 else
451 {
452 return 3;
453 }
454 }
455 else if (propertyDegree==SUBPROPERTY)
456 {
457 if (typeDegree==SUBSUMES_INVERT)
458 {
459 return 4;
460 } if (typeDegree==SUBSUMES)
461 {
462 return 5;
463 }
464 else
465 {
466 return 6;
467 }
468
469 }
470 else
471 {
472 if (typeDegree==SUBSUMES_INVERT)
473 {
474 return 7;
475 } if (typeDegree==SUBSUMES)
476 {
477 return 8;
478 }
479 else
480 {
481 return 9;
482 }
483
484 }
485 }
486
487
488
489 private ArrayList GetKBQueryList( String queryName, String arg )
490 {
491 ArrayList list = new ArrayList();
492 try
493 {
494 Iterator res = this.queryKB( queryName, new String[] { "\"" + arg + "\"" } );
495 while (res.hasNext())
496 {
497 Token t = (Token) res.next();
498 Fact f = t.fact( 1 );
499 String value = f.get( 2 ).toString();
500 if ( ( value.startsWith( "\"" ) ) && ( value.endsWith( "\"" ) ) )
501 list.add( value.substring( 1, value.length() - 1 ) );
502 else
503 list.add( value );
504 }
505 }
506 catch ( Exception ex ) {}
507 return( list );
508 }
509
510
511
512
513 private ArrayList GetKBQueryList2( String queryName, String argEndsWith )
514 {
515 ArrayList list = new ArrayList();
516 try
517 {
518 Iterator res = this.queryKB( queryName, new String[ 0 ] );
519 while (res.hasNext())
520 {
521 Token t = (Token) res.next();
522 Fact f = t.fact( 1 );
523 String subject = f.get( 1 ).toString();
524 if ( subject.endsWith( argEndsWith + "\"" ) )
525 {
526 String value = f.get( 2 ).toString();
527 if ( ( value.startsWith( "\"" ) ) && ( value.endsWith( "\"" ) ) )
528 list.add( value.substring( 1, value.length() - 1 ) );
529 else
530 list.add( value );
531 }
532 }
533 }
534 catch ( Exception ex ) {}
535 return( list );
536 }
537
538
539
540 private String GetKBQuerySingle( String queryName, String arg )
541 {
542 try
543 {
544 Iterator res = this.queryKB( queryName, new String[] { "\"" + arg + "\"" } );
545 if ( !res.hasNext() )
546 return( null );
547 Token t = (Token) res.next();
548 Fact f = t.fact( 1 );
549 String value = f.get( 2 ).toString();
550 if ( ( value.startsWith( "\"" ) ) && ( value.endsWith( "\"" ) ) )
551 return( value.substring( 1, value.length() - 1 ) );
552 else
553 return( value );
554 }
555 catch ( Exception ex ) {}
556 return( null );
557 }
558
559 public ArrayList GetPresentedProfileIDs( String serviceID )
560 {
561 return( this.GetKBQueryList2( "presentsProfile", serviceID ) );
562 }
563
564 private String GetSingleLiteral( String queryName, String parameter )
565 {
566 String literalID = this.GetKBQuerySingle( queryName, parameter );
567 if ( literalID == null )
568 return( null );
569 return( this.GetKBQuerySingle( "stringLiteral", literalID ).replace( '~', ' ' ) );
570 }
571
572 public String GetProfileTextDescription( String profileID )
573 {
574 return( this.GetSingleLiteral( "profileTextDescription", profileID ) );
575 }
576
577 public String GetRdfType( String rdfID )
578 {
579 return( this.GetKBQuerySingle( "rdfType", rdfID ) );
580 }
581
582 public String GetProfileServiceName( String profileID )
583 {
584 return( this.GetSingleLiteral( "profileServiceName", profileID ) );
585 }
586
587 public ArrayList GetProfileContactInformationIDs( String profileID )
588 {
589 return( this.GetKBQueryList( "profileContactInformationIDs", profileID ) );
590 }
591
592 public String GetActorName( String contactID )
593 {
594 return( this.GetSingleLiteral( "actorName", contactID ) );
595 }
596
597 public String GetActorTitle( String contactID )
598 {
599 return( this.GetSingleLiteral( "actorTitle", contactID ) );
600 }
601
602 public String GetActorPhone( String contactID )
603 {
604 return( this.GetSingleLiteral( "actorPhone", contactID ) );
605 }
606
607 public String GetActorFax( String contactID )
608 {
609 return( this.GetSingleLiteral( "actorFax", contactID ) );
610 }
611
612 public String GetActorEMail( String contactID )
613 {
614 return( this.GetSingleLiteral( "actorEMail", contactID ) );
615 }
616
617 public String GetActorPhysicalAddress( String contactID )
618 {
619 return( this.GetSingleLiteral( "actorPhysicalAddress", contactID ) );
620 }
621
622 public String GetActorWebUrl( String contactID )
623 {
624 return( this.GetSingleLiteral( "actorWebUrl", contactID ) );
625 }
626
627 public ArrayList GetProfileInputIDs( String profileID )
628 {
629 return( this.GetKBQueryList( "hasInput", profileID ) );
630 }
631
632 public ArrayList GetProfileOutputIDs( String profileID )
633 {
634 return( this.GetKBQueryList( "hasOutput", profileID ) );
635 }
636
637 public ArrayList GetProfilePreconditionIDs( String profileID )
638 {
639 return( this.GetKBQueryList( "hasPrecondition", profileID ) );
640 }
641
642 public ArrayList GetProfileEffectIDs( String profileID )
643 {
644 return( this.GetKBQueryList( "hasEffect", profileID ) );
645 }
646
647
648 public void WriteFacts( String fileName )
649 {
650 this.WriteFacts( fileName, this.kb.listFacts() );
651 }
652
653 private void WriteFacts( String fileName, Iterator it )
654 {
655 try
656 {
657 FileWriter w = new FileWriter( fileName );
658 while ( it.hasNext() )
659 {
660 Fact f = (Fact) it.next();
661 String fString = f.toString();
662 w.write( fString, 0, fString.length() );
663 w.write( "\r\n", 0, 2 );
664 }
665 w.close();
666 }
667 catch ( Exception ex )
668 {
669 }
670 }
671
672 }