View Javadoc

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 		//    if (out!=null) {
106 		//      kb.setOutputStream(out);
107 		//    }
108 		//kb.setVerbosity(OWLJessKB.OUT_NO);
109 		// create queries
110 		// matching queries
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         // parser queries
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 		// actor queries
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 			//			throw new Exception( "Error running query " + queryName + " on rete" );
188 		}
189 		return q;
190 
191 		// end query
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 		// end simpleSubjectQuery
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 			//value = value.substring(value.lastIndexOf("#")+1);
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 			//value = value.substring(value.lastIndexOf("#")+1);
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 			//value = value.substring(value.lastIndexOf("#")+1);
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 //	public String getProfileCompleteName(String concept) 
403 //	{
404 //		Vector values = this.simpleSubjectQuery("query-subclass",new String[] {OwlsParser.profileID},1, 1);
405 //		for (int i=0; i<values.size(); i++) 
406 //		{
407 //			String value = (String)values.elementAt(i);
408 //			String suffix = value.substring(value.lastIndexOf("#")+1);
409 //			if (suffix.equals(concept)) 
410 //			{
411 //				return value;
412 //			}
413 //		}
414 //		return null;
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 		/*    if (param1.getParameterDescription()==null) {
429 		 typeDegree = SUBSUMES;
430 		 } else if (param2.getParameterDescription()==null) {
431 		 typeDegree = SUBSUMES_INVERT;
432 		 } else {*/
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 	// liefert für ein QueryName(indirekt predicate) und ein gegebenes Argument(Subjekt) die Liste der in der KB
488 	// vorhandenen Objekte
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     // liefert für ein QueryName(indirekt predicate) und den letzten Teil eines gegebenen Argumentes(Subjekt)
512     // die Liste der in der KB vorhandenen Objekte
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 	// liefert für ein QueryName(indirekt predicate) und ein gegebenes Argument(Subjekt) das erste in der KB
539 	// vorhandene Objekt. Null, falls nicht vorhanden..
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 }