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.IteratorChain;
023: import org.mandarax.kernel.*;
024: import org.mandarax.util.SingleClauseSetIterator;
025:
026: /**
027: * Reference implementation for rules.
028: * @author <A href="http://www-ist.massey.ac.nz/JBDietrich" target="_top">Jens Dietrich</A>
029: * @version 3.4 <7 March 05>
030: * @since 1.1
031: * Prova re-integration modifications
032: * @author <A HREF="mailto:a.kozlenkov@city.ac.uk">Alex Kozlenkov</A>
033: * @version 3.4 <7 March 05>
034: */
035: public class RuleImpl extends AbstractClause implements Rule,
036: ClauseSetChangeListener {
037:
038: private Fact head = null;
039: private List posLiterals = null;
040: private List body = new ArrayList();
041: private static String IF = "IF";
042: private static String THEN = "THEN";
043: private static String AND = "AND";
044: private static String OR = "OR";
045: private boolean prerequisitesConnectedByOr = false;
046: private boolean isBound;
047:
048: /** Cache the string representation for optimized debugging. */
049: private transient String cachedToString = null;
050:
051: /**
052: * Constructor.
053: */
054: public RuleImpl() {
055: super ();
056: }
057:
058: /**
059: * Constructor. Pass a head and a body.
060: * @param c a list of facts that is going to be the body of the rule
061: * @param h a fact that is going to be the head of the rule
062: */
063: RuleImpl(java.util.List c, Fact h) {
064: super ();
065: setBody(c);
066: setHead(h);
067: }
068:
069: /**
070: * Constructor. Pass a head and a body.
071: * @param c a list of facts that is going to be the body of the rule
072: * @param h a fact that is going to be the head of the rule
073: * @param or if true the <strong>premisses</strong> are connected by OR, otherwise by AND
074: */
075: RuleImpl(java.util.List c, Fact h, boolean or) {
076: super ();
077: setBody(c);
078: setHead(h);
079: prerequisitesConnectedByOr = or;
080: }
081:
082: /**
083: * Constructor. Pass a single prerequisite and a conclusion.
084: * @param p a prerequisite
085: * @param h a fact that is going to be the head of the rule
086: * @deprecated use prerequisite instead of fact
087: */
088: private RuleImpl(Fact p, Fact h) {
089: super ();
090: body.add(p);
091: setHead(h);
092: }
093:
094: /**
095: * Constructor. Pass a single prerequisite and a conclusion.
096: * @param p a prerequisite
097: * @param h a fact that is going to be the head of the rule
098: */
099: private RuleImpl(Prerequisite p, Fact h) {
100: super ();
101: body.add(p);
102: setHead(h);
103: }
104:
105: /**
106: * Add a fact to the body.
107: * @param f a fact
108: * @deprecated - rules use now prerequisites instead of facts in the body
109: */
110: public void addToBody(Fact f) {
111: cachedToString = null;
112: body.add(f);
113: }
114:
115: /**
116: * Apply a set of replacements to a clause. Returns a new clause!
117: * @return the rule (clause) resulting from the application of the replacements
118: * @param r a collection of replacements
119: */
120: public Clause apply(java.util.Collection r) {
121: return applyToRule(r);
122: }
123:
124: /**
125: * Apply a single replacement to a fact. Returns a new clause!
126: * @return the rule (clause) resulting from the application of the replacemen
127: * @param r the replacement
128: */
129: public Clause apply(Replacement r) {
130: return applyToRule(r);
131: }
132:
133: /**
134: * Apply a set of substitutions.
135: * @return the rule that is the result of applying the replacements
136: * @param r a collection of replacements
137: */
138: public Rule applyToRule(java.util.Collection r) {
139: cachedToString = null;
140: Vector newBody = new Vector(getBody().size());
141:
142: for (Iterator it = getBody().iterator(); it.hasNext();) {
143: newBody.add(((Fact) it.next()).apply(r));
144: }
145:
146: Rule rule = new RuleImpl(newBody, (Fact) getHead().apply(r));
147: rule.setBodyOrConnected(isBodyOrConnected());
148: return rule;
149: }
150:
151: /**
152: * Apply a replacement to a rule.
153: * @return the rule that is the result of applying the replacement
154: * @param r a replacement
155: */
156: public Rule applyToRule(Replacement r) {
157: cachedToString = null;
158: RuleImpl rule = new RuleImpl(new java.util.Vector(getBody()
159: .size()), getHead().applyToFact(r));
160:
161: for (Iterator it = getBody().iterator(); it.hasNext();) {
162: rule.getBody().add(((Fact) it.next()).applyToFact(r));
163: }
164:
165: rule.setBodyOrConnected(isBodyOrConnected());
166: return rule;
167: }
168:
169: /**
170: * Whether the head contains variables
171: * @return boolean
172: */
173: public boolean isBound() {
174: return isBound;
175: }
176:
177: /**
178: * Get a clause iterator.
179: * @return a clause iterator
180: */
181: public org.mandarax.util.ClauseIterator clauses() {
182: if (prerequisitesConnectedByOr) {
183: ArrayList rules = new ArrayList(body.size());
184: RuleImpl rule = null;
185:
186: for (Iterator it = body.iterator(); it.hasNext();) {
187: rule = new RuleImpl((Fact) it.next(), head);
188: rule.container = this ;
189:
190: rules.add(rule);
191: }
192:
193: return new SingleClauseSetIterator(rules);
194: } else {
195: return new org.mandarax.util.SingleClauseIterator(this );
196: }
197: }
198:
199: /**
200: * Handle a clause set change event.
201: * @param e an event
202: */
203: public void clauseSetChanged(ClauseSetChangeEvent e) {
204:
205: // forward key changed events coming from the head
206: if ((e.getType() == ClauseSetChangeEvent.KEY_CHANGED)
207: && (e.getSource() == head)) {
208: fireClauseSetChangeEvent(new ClauseSetChangeEvent(this , e
209: .getOldValue(), e.getNewValue(),
210: ClauseSetChangeEvent.KEY_CHANGED));
211: }
212: }
213:
214: /**
215: * Check whether objects are equal.
216: * @param obj the object to compare this object with
217: * @return true if the objects are equal, false otherwise
218: */
219: public boolean equals(Object obj) {
220: if ((obj != null) && (obj instanceof RuleImpl)) {
221: RuleImpl r = (RuleImpl) obj;
222: boolean result = this .isBodyOrConnected() == r
223: .isBodyOrConnected();
224: result = result
225: && ((head == null) ? r.head == null : head
226: .equals(r.head));
227:
228: if (result) {
229: if (body == null) {
230: result = r.body == null;
231: } else {
232: if (body.size() != r.body.size()) {
233: return false;
234: } else {
235: for (int i = 0; i < body.size(); i++) {
236: result = result
237: && body.get(i)
238: .equals(r.body.get(i));
239:
240: if (!result) {
241: return false;
242: }
243: }
244: }
245: }
246: }
247:
248: return result;
249: }
250:
251: return false;
252: }
253:
254: /**
255: * Get the body of the rule.
256: * @return the body (a list of facts)
257: */
258: public java.util.List getBody() {
259: return body;
260: }
261:
262: /**
263: * Get the head fact.
264: * @return the hhead of the rule
265: */
266: public Fact getHead() {
267: return head;
268: }
269:
270: /**
271: * Get a key for fast access.
272: * @see org.mandarax.kernel.Fact
273: * @return the key object
274: */
275: public Object getKey() {
276: return getHead().getPredicate();
277: }
278:
279: /**
280: * Get the negative literals.
281: * @return the body of the rule
282: */
283: public java.util.List getNegativeLiterals() {
284: return body;
285: }
286:
287: /**
288: * Get the positive literals.
289: * @return a list containing only the head of the rule
290: */
291: public synchronized java.util.List getPositiveLiterals() {
292: if (posLiterals == null) {
293: posLiterals = new ArrayList(1);
294: posLiterals.add(head);
295: }
296:
297: return posLiterals;
298: }
299:
300: /**
301: * Get the hashcode of the object.
302: * @return a hash code
303: */
304: public int hashCode() {
305: return (head == null) ? 0 : head.hashCode();
306: }
307:
308: /**
309: * Indicates whether the clause is atomic.
310: * @return a boolean
311: */
312: public boolean isAtomic() {
313: return !prerequisitesConnectedByOr;
314: }
315:
316: /**
317: * Indicates whether the premisses in the body are connected by OR.
318: * @return a boolean
319: */
320: public boolean isBodyOrConnected() {
321: return prerequisitesConnectedByOr;
322: }
323:
324: /**
325: * Indicates whether the clause is the empty clause.
326: * @return true if head and body are empty, false otherwise
327: */
328: public boolean isEmpty() {
329: return getPositiveLiterals().isEmpty()
330: && getNegativeLiterals().isEmpty();
331: }
332:
333: /**
334: * Indicates whether the object (usually a term or a clause set) can be performed
335: * using the java semantics.
336: * @return a boolean
337: */
338: public boolean isExecutable() {
339: return body.isEmpty() && head.isExecutable();
340: }
341:
342: /**
343: * Remove a fact from the body.
344: * @return boolean - indicates whether the element has been found or not
345: * @param f a fact
346: * @deprecated - rules use now prerequisites instead of facts in the body
347: */
348: public boolean removeFromBody(Fact f) {
349: cachedToString = null;
350: return body.remove(f);
351: }
352:
353: /**
354: * Indicate whether two facts share the same key.
355: * @return true if both facts have the same key
356: * @param f1 the first fact
357: * @param f2 the second fact
358: */
359: private boolean sameKey(Fact f1, Fact f2) {
360: Predicate p1 = (f1 == null) ? null : f1.getPredicate();
361: Predicate p2 = (f2 == null) ? null : f2.getPredicate();
362:
363: return (p1 == null) ? p2 == null : (p1.equals(p2));
364: }
365:
366: /**
367: * Set the body.
368: * @param b the new body
369: */
370: public void setBody(java.util.List b) {
371: cachedToString = null;
372: body = b;
373: }
374:
375: /**
376: * Set a new value. A value true means that the prerequisistes are connected by OR,
377: * a value false means that the prerequistes are connected by AND.
378: * @param value a boolean
379: */
380: public void setBodyOrConnected(boolean value) {
381: cachedToString = null;
382: if (prerequisitesConnectedByOr != value) {
383: boolean oldValue = prerequisitesConnectedByOr;
384: prerequisitesConnectedByOr = value;
385: fireClauseSetChangeEvent(new ClauseSetChangeEvent(this ,
386: Boolean.valueOf(oldValue), Boolean.valueOf(value),
387: ClauseSetChangeEvent.OTHER));
388: }
389: }
390:
391: /**
392: * Set the head. Note that we fire an event because changing the head
393: * could imply changing the key and containers (knowledge bases) need to be notified
394: * about this.
395: * Prova: add isBound processing
396: * @param f the new head
397: */
398: public void setHead(Fact f) {
399: cachedToString = null;
400: Fact oldHead = head;
401:
402: if (oldHead != null) {
403: oldHead.removeClauseSetChangeListener(this );
404: }
405:
406: head = f;
407: isBound = head.isBound();
408:
409: // add this as clause set change listener in order to
410: // forward key change events
411:
412: if (f != null) {
413: f.addClauseSetChangeListener(this );
414: }
415:
416: // the key has changed
417: if (!sameKey(oldHead, f)) {
418: fireClauseSetChangeEvent(new ClauseSetChangeEvent(this ,
419: (oldHead == null) ? null : oldHead.getKey(),
420: (f == null) ? null : f.getKey(),
421: ClauseSetChangeEvent.KEY_CHANGED));
422: }
423:
424: }
425:
426: /**
427: * Convert the receiver to a string.
428: * @return the string representation of this object
429: */
430: public String toString() {
431: if (cachedToString == null) {
432: StringBuffer buf = new StringBuffer();
433: buf.append(IF);
434: buf.append(" ");
435: boolean first = true;
436: for (Iterator it = getBody().iterator(); it.hasNext();) {
437: if (first) {
438: first = false;
439: } else {
440: buf.append(" ");
441: buf.append(prerequisitesConnectedByOr ? OR : AND);
442: buf.append(" ");
443: }
444: buf.append(it.next().toString());
445: }
446: buf.append(" ");
447: buf.append(THEN);
448: buf.append(" ");
449: buf.append(getHead().toString());
450: cachedToString = new String(buf);
451: }
452: return cachedToString;
453: }
454:
455: /**
456: * Get an iterator iterating over the predicates contained in this clause set.
457: * @return an iterator
458: */
459: public Iterator predicates() {
460: List chain = new ArrayList();
461: chain.add(head.predicates());
462: for (int i = 0; i < body.size(); i++) {
463: chain.add(((ClauseSet) body.get(i)).predicates());
464: }
465: return new IteratorChain(chain);
466: }
467:
468: /**
469: * Indicates whether the clause is ground (= does not have variables).
470: * @return a boolean
471: */
472: public boolean isGround() {
473: if (!head.isGround())
474: return false;
475: if (this .body != null) {
476: for (int i = 0; i < this .body.size(); i++) {
477: if (!((Clause) this .body.get(i)).isGround())
478: return false;
479: }
480: }
481: return true;
482: }
483: }
|