001: package org.mandarax.reference;
002:
003: /*
004: * Copyright (C) 1999-2004 <A href="http://www-ist.massey.ac.nz/JBDietrich" target="_top">Jens Dietrich</a>
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: import java.util.*;
021:
022: import org.mandarax.kernel.*;
023:
024: /**
025: * Default implementation of a knowledge base managing clause sets
026: * in anordered container (like a list). Moving clause sets up and down
027: * (assigning higher/lower priority) is supported here.
028: * <br>
029: * This class has been completely redesigned in version 2.1
030: * New methods for adding/removing plugins to the knowledgebase are added since 3.3.1 (by Adrian Paschke).
031: * @author <A href="http://www-ist.massey.ac.nz/JBDietrich" target="_top">Jens Dietrich</A>
032: * @version 3.4 <7 March 05>
033: * @since 1.2
034: */
035: public final class AdvancedKnowledgeBase extends AbstractKnowledgeBase
036: implements ExtendedKnowledgeBase {
037:
038: // the container. change this to a sorted collection !
039: private java.util.Vector container = new java.util.Vector();
040:
041: // indexed container
042: private java.util.Hashtable indexed = new java.util.Hashtable();
043:
044: // the feature descriptor
045: private static KnowledgeBaseFeatureDescriptions featureDescriptions = null;
046:
047: // the comparator
048: private Comparator comparator = null;
049:
050: /**
051: * Constructor.
052: */
053: public AdvancedKnowledgeBase() {
054: super ();
055: }
056:
057: /**
058: * Set a comperator.
059: * @param comp a comparator
060: */
061: public synchronized void setComparator(java.util.Comparator comp) {
062: comparator = comp;
063:
064: // apply new sorting
065: resort();
066: }
067:
068: /**
069: * Resort all entries. Does nothing if the comparator is null.
070: */
071: public synchronized void resort() {
072: if (comparator != null) {
073: for (Iterator keys = getKeys().iterator(); keys.hasNext();) {
074: resort(keys.next());
075: }
076: }
077: }
078:
079: /**
080: * Resort all entries for a particular key. Does nothing if the comparator is null.
081: * @param key a key (a predicate)
082: */
083: public void resort(Object key) {
084: if (comparator != null) {
085: List clauseSets = (List) indexed.get(key);
086: Collections.sort(clauseSets, comparator);
087: }
088: }
089:
090: /**
091: * Get the comperator.
092: * @param comp a comparator
093: */
094: public synchronized Comparator getComparator() {
095: return comparator;
096: }
097:
098: /**
099: * Indicates whether the (manual) move operations are enabled.
100: * @return a boolean
101: */
102: public synchronized boolean isMoveEnabled() {
103: return comparator == null;
104: }
105:
106: /**
107: * Add a clause set.
108: * @param c org.mandarax.kernel.ClauseSet
109: */
110: public synchronized void add(ClauseSet c) {
111: int position = container.size();
112: Object[] keys = getKeys(c);
113: container.add(c);
114:
115: for (int i = 0; i < keys.length; i++) {
116: List list = getOrAddIndexed(keys[i]);
117: list.add(c);
118: // resort !!
119: // note that we can not take a SortedSet - we want to be able to store
120: // two clause sets which are equal !
121: resort(keys[i]);
122: }
123:
124: c.addClauseSetChangeListener(this );
125: fireKnowledgeBaseChanged(KnowledgeBaseChangeEvent.CLAUSE_ADDED,
126: c);
127: }
128:
129: /**
130: * Iterate over all clauses
131: * @return org.mandarax.util.ClauseIterator
132: */
133: public synchronized org.mandarax.util.ClauseIterator clauses() {
134: return new org.mandarax.util.MultipleClauseSetIterator(
135: getClauseSets());
136: }
137:
138: /**
139: * Iterate over all clauses found for a certain key.
140: * @return org.mandarax.util.ClauseIterator
141: * @param query org.mandarax.kernel.Clause
142: * @param additionalParameter java.lang.Object
143: */
144: public synchronized org.mandarax.util.ClauseIterator clauses(
145: Clause query, Object additionalParameter) {
146: Object key = query.getKey();
147:
148: if (key instanceof org.mandarax.kernel.Predicate) {
149: List list = new ArrayList(getOrAddIndexed(key));
150: return new org.mandarax.util.MultipleClauseSetIterator(
151: list, query, additionalParameter);
152: } else {
153: return new org.mandarax.util.MultipleClauseSetIterator(
154: new ArrayList(0));
155: }
156: }
157:
158: /**
159: * Handle a clause set change event.
160: * @param e org.mandarax.kernel.ClauseSetChangeEvent
161: */
162: public void clauseSetChanged(ClauseSetChangeEvent e) {
163:
164: // if the predicate has changed, register changed clause
165: if ((e.getType() == ClauseSetChangeEvent.KEY_CHANGED)
166: && (e.getSource() instanceof ClauseSet)) {
167: ClauseSet cs = (ClauseSet) e.getSource();
168:
169: remove(cs, getKeyArray(e.getOldValue()));
170: add(cs);
171: }
172: }
173:
174: /**
175: * Dump the content. For testing only!
176: */
177: public void dump() {
178: System.out.println("---------------------------------");
179: System.out.println("Dumping container ");
180:
181: for (Iterator it = container.iterator(); it.hasNext();) {
182: System.out.println(it.next());
183: }
184:
185: System.out.println("");
186:
187: for (Enumeration en = indexed.keys(); en.hasMoreElements();) {
188: Object key = en.nextElement();
189:
190: System.out.println("KEY IS : " + key.toString());
191:
192: for (Iterator it = getOrAddIndexed(key).iterator(); it
193: .hasNext();) {
194: System.out.println(it.next());
195: }
196: }
197: }
198:
199: /**
200: * Get a list containing all clause sets.
201: * Note that the collection returned is a read only copy of the
202: * internal collection of clause sets.
203: * @return java.util.List
204: */
205: public synchronized java.util.List getClauseSets() {
206: List list = new ArrayList();
207: for (Iterator iter = getKeys().iterator(); iter.hasNext();) {
208: Object key = iter.next();
209: list.addAll(getOrAddIndexed(key));
210: }
211: return list;
212: }
213:
214: /**
215: * Get a list containing all clause sets that have the key.
216: * New in 2.2 - return copy of the original list.
217: * @param the key object
218: * @return a list containing all clause sets
219: */
220: public synchronized java.util.List getClauseSets(Object key) {
221: List list = new ArrayList();
222: list.addAll(getOrAddIndexed(key));
223: return list;
224: }
225:
226: /**
227: * Get the feature descriptions.
228: * @return org.mandarax.kernel.KnowledgeBaseFeatureDescriptions
229: */
230: public KnowledgeBaseFeatureDescriptions getFeatureDescriptions() {
231: if (featureDescriptions == null) {
232: featureDescriptions = new KnowledgeBaseFeatureDescriptions() {
233: protected void initialize() {
234: super .initialize();
235: this .supported
236: .add(KnowledgeBaseFeatureDescriptions.AUTO_PRIORITIES);
237: }
238: };
239: }
240:
241: return featureDescriptions;
242: }
243:
244: /**
245: * If the object is an array, return it. otherwise
246: * wrap it by an one element array..
247: * @return an array of keys
248: * @param an object
249: */
250: private Object[] getKeyArray(Object obj) {
251: if (obj != null) {
252: if (obj.getClass().isArray()) {
253: return (Object[]) obj;
254: } else {
255: Object[] keys = new Object[1];
256: keys[0] = obj;
257: return keys;
258: }
259: } else {
260: return new Object[0];
261: }
262: }
263:
264: /**
265: * Get the keys.
266: * @return the keys
267: */
268: public java.util.Collection getKeys() {
269: return indexed.keySet();
270: }
271:
272: /**
273: * Retrieve the keys for a clause set.
274: * @return a array of objects (predicates)
275: * @param cs a clause set
276: */
277: private Object[] getKeys(ClauseSet cs) {
278: return getKeyArray(cs.getKey());
279: }
280:
281: /**
282: * Either get or add an indexed container for the clause c.
283: * @return a container
284: * @param predicate java.lang.Object
285: */
286: private List getOrAddIndexed(Object k) {
287: List v = (List) indexed.get(k);
288: if (v == null) {
289: v = new ArrayList();
290: indexed.put(k, v);
291: }
292: return v;
293: }
294:
295: /**
296: * Move a clause set down. (= assign a lower priority)
297: * @param cs org.mandarax.kernel.ClauseSet
298: */
299: public synchronized void moveDown(ClauseSet cs) {
300:
301: if (this .isMoveEnabled()) {
302: Object[] keys = getKeys(cs);
303: List list = null;
304: int position = -1;
305: for (int i = 0; i < keys.length; i++) {
306: list = (List) getOrAddIndexed(keys[i]);
307: position = list.indexOf(cs);
308: if (position < (list.size() - 1)) {
309: list.remove(position);
310: list.add(position + 1, cs);
311: }
312: }
313: } else
314: LOG_KB_MOVE
315: .warn("Move operation disabled - clauses are arranged by comparator");
316: }
317:
318: /**
319: * Move a clause set to the bottom. (= assign the lowest priority)
320: * @param cs org.mandarax.kernel.ClauseSet
321: */
322: public synchronized void moveToBottom(ClauseSet cs) {
323: if (this .isMoveEnabled()) {
324: Object[] keys = getKeys(cs);
325: List list = null;
326: int position = -1;
327: for (int i = 0; i < keys.length; i++) {
328: list = (List) getOrAddIndexed(keys[i]);
329: position = list.indexOf(cs);
330: if (position < (list.size() - 1)) {
331: list.remove(position);
332: list.add(cs);
333: }
334: }
335: } else
336: LOG_KB_MOVE
337: .warn("Move operation disabled - clauses are arranged by comparator");
338: }
339:
340: /**
341: * Move a clause set to the top. (= assign the highest priority)
342: * @param cs org.mandarax.kernel.ClauseSet
343: */
344: public synchronized void moveToTop(ClauseSet cs) {
345: if (this .isMoveEnabled()) {
346: Object[] keys = getKeys(cs);
347: List list = null;
348: int position = -1;
349: for (int i = 0; i < keys.length; i++) {
350: list = (List) getOrAddIndexed(keys[i]);
351: position = list.indexOf(cs);
352: if (position > 0) {
353: list.remove(position);
354: list.add(0, cs);
355: }
356: }
357: } else
358: LOG_KB_MOVE
359: .warn("Move operation disabled - clauses are arranged by comparator");
360: }
361:
362: /**
363: * Move a clause set up. (= assign a higher priority)
364: * @param cs org.mandarax.kernel.ClauseSet
365: */
366: public synchronized void moveUp(ClauseSet cs) {
367: if (this .isMoveEnabled()) {
368: Object[] keys = getKeys(cs);
369: List list = null;
370: int position = -1;
371: for (int i = 0; i < keys.length; i++) {
372: list = (List) getOrAddIndexed(keys[i]);
373: position = list.indexOf(cs);
374: if (position > 0) {
375: list.remove(position);
376: list.add(position - 1, cs);
377: }
378: }
379: } else
380: LOG_KB_MOVE
381: .warn("Move operation disabled - clauses are arranged by comparator");
382: }
383:
384: /**
385: * Remove a clause set.
386: * @return boolean - indicating whether the object has been found (true) or not (false)
387: * @param c org.mandarax.kernel.ClauseSet
388: */
389: public synchronized boolean remove(ClauseSet c) {
390: boolean result = remove(c, getKeys(c));
391: fireKnowledgeBaseChanged(
392: KnowledgeBaseChangeEvent.CLAUSE_REMOVED, c);
393: return result;
394: }
395:
396: /**
397: * Remove a clause set.
398: * @return boolean - indicating whether the object has been found (true) or not (false)
399: * @param c org.mandarax.kernel.ClauseSet
400: */
401: private boolean remove(ClauseSet c, Object keys[]) {
402: boolean success = container.remove(c);
403: for (int i = 0; i < keys.length; i++) {
404: Collection clauses = getOrAddIndexed(keys[i]);
405: success = success && clauses.remove(c);
406: }
407: // unregister listener
408: c.removeClauseSetChangeListener(this );
409:
410: return success;
411: }
412:
413: /**
414: * Remove all clauses.
415: */
416: public synchronized void removeAll() {
417: indexed = new Hashtable();
418: // remove all listeners
419: for (Iterator it = container.iterator(); it.hasNext();) {
420: ((ClauseSet) it.next()).removeClauseSetChangeListener(this );
421: }
422: container = new Vector();
423: }
424:
425: /**
426: * Add plugin to knowledgebase
427: * @parameter plugin a KnowledgeBase
428: * @parameter id String - the URI of the plugin
429: * @since 3.3.1 added by Adrian Paschke
430: */
431: public synchronized void addPlugIn(
432: org.mandarax.kernel.KnowledgeBase plugin, String id)
433: throws IllegalArgumentException {
434: if (plugin == null)
435: throw new IllegalArgumentException();
436: // check if plugin already in knowledgebase
437: List clauses1 = getClauseSets();
438: for (Iterator it1 = clauses1.iterator(); it1.hasNext();) {
439: ClauseSet cs1 = (ClauseSet) it1.next();
440: String id1 = cs1.getProperty("ID");
441: if (id1 != null)
442: if (id1.equals(id))
443: throw new IllegalArgumentException("PlugIn " + id
444: + " already exists");
445: }
446:
447: // add new clause set (axiom) to knowledgebase
448: List clauses = plugin.getClauseSets();
449: for (Iterator it = clauses.iterator(); it.hasNext();) {
450: ClauseSet cs = (ClauseSet) it.next();
451: cs.setProperty("ID", id);
452: add(cs);
453: }
454: }
455:
456: /**
457: * Add plugin to knowledgebase
458: * @parameter plugin a ClauseSet list
459: * @parameter id String - the URI of the plugin
460: * @since 3.3.1 added by Adrian Paschke
461: */
462: public synchronized void addPlugIn(List plugin, String id)
463: throws IllegalArgumentException {
464: if (plugin == null)
465: throw new IllegalArgumentException();
466: // check if plugin already in knowledgebase
467: List clauses1 = getClauseSets();
468: for (Iterator it1 = clauses1.iterator(); it1.hasNext();) {
469: ClauseSet cs1 = (ClauseSet) it1.next();
470: String id1 = cs1.getProperty("ID");
471: if (id1 != null)
472: if (id1.equals(id))
473: throw new IllegalArgumentException("PlugIn " + id
474: + " already exists");
475: }
476:
477: // add new clause set (axiom) to knowledgebase
478: for (Iterator it = plugin.iterator(); it.hasNext();) {
479: try {
480: ClauseSet cs = (ClauseSet) it.next();
481: cs.setProperty("ID", id);
482: add(cs);
483: } catch (Exception ex) {
484: throw new IllegalArgumentException("PlugIn " + id
485: + " contains no ClauseSets");
486: }
487: }
488: }
489:
490: /**
491: * Add plugin to knowledgebase
492: * @parameter plugin a ClauseSet[] array
493: * @parameter id String - the URI of the plugin
494: * @since 3.3.1 added by Adrian Paschke
495: */
496: public synchronized void addPlugIn(ClauseSet[] plugin, String id)
497: throws IllegalArgumentException {
498: if (plugin == null)
499: throw new IllegalArgumentException();
500: // check if plugin already in knowledgebase
501: List clauses1 = getClauseSets();
502: for (Iterator it1 = clauses1.iterator(); it1.hasNext();) {
503: ClauseSet cs1 = (ClauseSet) it1.next();
504: String id1 = cs1.getProperty("ID");
505: if (id1 != null)
506: if (id1.equals(id))
507: throw new IllegalArgumentException("PlugIn " + id
508: + " already exists");
509: }
510:
511: // add new clause set (axiom) to knowledgebase
512: for (int i = 0; i <= plugin.length; i++) {
513: ClauseSet cs = (ClauseSet) plugin[i];
514: cs.setProperty("ID", id);
515: add(cs);
516: }
517: }
518:
519: /**
520: * Remove plugin with id from knowledgebase
521: * @parameter id String - the plugin URI
522: * @since 3.3.1 added by Adrian Paschke
523: */
524: public synchronized void removePlugIn(String id) {
525: List clauses = getClauseSets();
526: for (Iterator it = clauses.iterator(); it.hasNext();) {
527: ClauseSet cs = (ClauseSet) it.next();
528: String id1 = cs.getProperty("ID");
529: if (id1 != null)
530: if (id.equals(id))
531: remove(cs);
532: }
533: }
534:
535: }
|