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.apache.commons.collections.iterators.FilterIterator;
023: import org.apache.commons.collections.iterators.IteratorChain;
024: import org.mandarax.kernel.*;
025: import org.mandarax.kernel.validation.TestCase;
026: import org.mandarax.util.logging.*;
027:
028: /**
029: * Abstract super class for knowledge bases. Includes some support methods for event handling.
030: * <br>
031: * Queries are organized in a Hashtable.
032: * @author <A href="http://www-ist.massey.ac.nz/JBDietrich" target="_top">Jens Dietrich</A>
033: * @version 3.4 <7 March 05>
034: * @since 1.2
035: */
036: public abstract class AbstractKnowledgeBase implements
037: org.mandarax.kernel.KnowledgeBase, LogCategories {
038:
039: // event listener
040: private transient Vector knowledgeBaseListener = new Vector();
041: private List queries = new Vector();
042: private List testCases = new Vector();
043:
044: /**
045: * Constructor.
046: */
047: public AbstractKnowledgeBase() {
048: super ();
049: }
050:
051: /**
052: * Add a knowledge base change listener.
053: * @param l org.mandarax.kernel.KnowledgeBaseChangeListener
054: */
055: public void addKnowledgeBaseChangeListener(
056: KnowledgeBaseChangeListener l) {
057: knowledgeBaseListener.add(l);
058:
059: if (LOG_KB_EVENT.isDebugEnabled()) {
060: LOG_KB_EVENT.debug("Add knowledge base change listener "
061: + ((l == null) ? "null" : l.toString())
062: + " to knowledge base " + toString());
063: }
064: }
065:
066: /**
067: * Fire a knowledge base change event.
068: * @see KnowledgeBaseChangeEvent
069: * @param state should be one of the constant defined in KnowledgeBaseEvent
070: */
071: protected void fireKnowledgeBaseChanged(int state) {
072: fireKnowledgeBaseChanged(new KnowledgeBaseChangeEvent(this ,
073: state));
074: }
075:
076: /**
077: * Fire a knowledge base change event.
078: * @see KnowledgeBaseChangeEvent
079: * @param state should be one of the constant defined in KnowledgeBaseEvent
080: * @param changed the affected clause set
081: */
082: protected void fireKnowledgeBaseChanged(int state, Object changed) {
083: fireKnowledgeBaseChanged(new KnowledgeBaseChangeEvent(this ,
084: state, changed));
085: }
086:
087: /**
088: * Fire a knowledge base changed event.
089: * @see KnowledgeBaseChangeEvent
090: * @param state should be one of the constant defined in KnowledgeBaseEvent
091: * @param changed the collection of affected clause sets
092: */
093: protected void fireKnowledgeBaseChanged(int state,
094: Collection changed) {
095: fireKnowledgeBaseChanged(new KnowledgeBaseChangeEvent(this ,
096: state, changed));
097: }
098:
099: /**
100: * Fire a knowledge base change event.
101: * @param e the event
102: */
103: protected void fireKnowledgeBaseChanged(KnowledgeBaseChangeEvent e) {
104: if ((knowledgeBaseListener == null) || (e == null)) {
105: return;
106: }
107:
108: for (Iterator it = knowledgeBaseListener.iterator(); it
109: .hasNext();) {
110: ((KnowledgeBaseChangeListener) it.next())
111: .knowledgeBaseChanged(e);
112: }
113: }
114:
115: /**
116: * Remove a knowledge base change listener.
117: * @param l a listener
118: */
119: public void removeKnowledgeBaseChangeListener(
120: KnowledgeBaseChangeListener l) {
121: knowledgeBaseListener.remove(l);
122:
123: if (LOG_KB_EVENT.isDebugEnabled()) {
124: LOG_KB_EVENT.debug("Remove knowledge base change listener "
125: + ((l == null) ? "null" : l.toString())
126: + " from knowledge base " + toString());
127: }
128: }
129:
130: /**
131: * Add a query. If a query with the name name already exists, replace it.
132: * @param q a query
133: */
134: public void addQuery(Query q) {
135: String name = q.getName();
136: if (name == null)
137: throw new IllegalArgumentException(
138: "Queries must have a name");
139: synchronized (queries) {
140: for (Iterator iter = queries.iterator(); iter.hasNext();) {
141: Query nextQuery = (Query) iter.next();
142: if (name.equals(nextQuery.getName())) {
143: iter.remove();
144: break;
145: }
146: }
147: queries.add(q);
148: }
149: }
150:
151: /**
152: * Remove a query.
153: * @param q a query
154: * @return true if the object has been found (true) or not (false)
155: */
156: public boolean removeQuery(Query q) {
157: synchronized (queries) {
158: return queries.remove(q);
159: }
160: }
161:
162: /**
163: * Get a query by name or null if there is no query with this name.
164: * @param queryName a query name
165: * @return a query of null if there is no query registered with this name
166: */
167: public Query getQuery(String queryName) {
168: synchronized (queries) {
169: for (int i = 0; i < queries.size(); i++) {
170: Query nextQuery = (Query) queries.get(i);
171: if (nextQuery.getName().equals(queryName))
172: return nextQuery;
173: }
174: return null;
175: }
176: }
177:
178: /**
179: * Get an iterator for the names of all queries registered.
180: * @return an iterator
181: */
182: public Iterator queryNames() {
183: synchronized (queries) {
184: List names = new ArrayList(queries.size());
185: for (int i = 0; i < queries.size(); i++) {
186: Query nextQuery = (Query) queries.get(i);
187: names.add(nextQuery.getName());
188: }
189: return names.iterator();
190: }
191: }
192:
193: /**
194: * Get an iterator for all queries registered.
195: * The iterator cannot be used to remove elements!
196: * @return an iterator
197: */
198: public Iterator queries() {
199: synchronized (queries) {
200: return Collections.unmodifiableList(queries).iterator();
201: }
202: }
203:
204: /**
205: * Add a test case.
206: * @param testCase a test case
207: */
208: public void addTestCase(TestCase testCase) {
209: synchronized (testCases) {
210: testCases.add(testCase);
211: testCase.setKb(this );
212: }
213: }
214:
215: /**
216: * Remove a test case.
217: * @param testCase a test case
218: * @return true if the object has been found (true) or not (false)
219: */
220: public boolean removeTestCase(TestCase testCase) {
221: synchronized (testCases) {
222: boolean result = testCases.remove(testCase);
223: testCase.setKb(null);
224: return result;
225: }
226: }
227:
228: /**
229: * Get an iterator for all test cases registered.
230: * @return an iterator
231: */
232: public Iterator testcases() {
233: synchronized (testCases) {
234: return Collections.unmodifiableList(testCases).iterator();
235: }
236: }
237:
238: /**
239: * Get an iterator for all predicates contained (in any clause set within the kb).
240: * @return an iterator
241: */
242: public synchronized Iterator predicates() {
243: List chain = new ArrayList();
244: List clauseSets = getClauseSets();
245: for (int i = 0; i < clauseSets.size(); i++)
246: chain.add(((ClauseSet) clauseSets.get(i)).predicates());
247: return new IteratorChain(chain);
248: }
249:
250: /**
251: * Get a predicate by name.
252: * Note that there might by more than one predicate in the kb with the same name.
253: * In some cases, this makes sense (e.g. polymorphic predicates such as < for different
254: * types), but should be avoided for "custom" predicates (such as SimplePredicates).
255: * In this case, this method should return one predicate. In this case, applications can still use
256: * predicates() in order to find all predicates with a particular name.
257: * @return a predicate or null indicating that the kb does not contain a predicate with this name
258: * @param name a predicate name
259: */
260: public synchronized Predicate getPredicate(final String name) {
261: Iterator predicates = predicates();
262: org.apache.commons.collections.Predicate condition = new org.apache.commons.collections.Predicate() {
263: public boolean evaluate(Object obj) {
264: return (obj instanceof Predicate)
265: && (name.equals(((Predicate) obj).getName()));
266: }
267: };
268: Iterator filtered = new FilterIterator(predicates, condition);
269: if (filtered.hasNext()) {
270: return (Predicate) filtered.next();
271: }
272: return null;
273: }
274: }
|