001: /*
002: * hammurapi-rules @mesopotamia.version@
003: * Hammurapi rules engine.
004: * Copyright (C) 2005 Hammurapi Group
005: *
006: * This program 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 program 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: * URL: http://http://www.hammurapi.biz
021: * e-Mail: support@hammurapi.biz
022: */
023: package biz.hammurapi.rules;
024:
025: import java.io.Serializable;
026: import java.lang.reflect.Method;
027: import java.util.Collections;
028: import java.util.HashSet;
029: import java.util.Iterator;
030: import java.util.Set;
031:
032: import javax.swing.table.DefaultTableModel;
033: import javax.swing.table.TableModel;
034: import javax.swing.tree.DefaultMutableTreeNode;
035: import javax.swing.tree.MutableTreeNode;
036:
037: import org.w3c.dom.Element;
038:
039: import biz.hammurapi.swing.CompositeVisualizer;
040: import biz.hammurapi.swing.Visualizable;
041: import biz.hammurapi.xml.dom.CompositeDomSerializer;
042: import biz.hammurapi.xml.dom.DomSerializable;
043:
044: /**
045: * This class is a tracking element for inference process.
046: * It is created by rules during inference process and attached to facts.
047: * @author Pavel Vlasov
048: * @revision $Revision$
049: */
050: public class Derivation implements DomSerializable, Serializable,
051: Visualizable {
052:
053: /**
054: *
055: */
056: private static final long serialVersionUID = 2217441881574645228L;
057:
058: // Class and method name and object hash code
059: private String ruleFullName;
060: private Object ruleInstance;
061: private Method ruleMethod;
062:
063: public Object getRuleInstance() {
064: return ruleInstance;
065: }
066:
067: public Method getRuleMethod() {
068: return ruleMethod;
069: }
070:
071: private int depth;
072:
073: /**
074: * Derivation depth is the size of the longest of logical chains which lead to this derivation.
075: * @return derivation depth
076: */
077: public int getDepth() {
078: return depth;
079: }
080:
081: /**
082: * @param target
083: * @param method
084: */
085: Derivation(Object target, Method method) {
086: StringBuffer ruleBuf = new StringBuffer(target.getClass()
087: .getName());
088: ruleBuf.append("[");
089: if (target instanceof AbstractRule
090: && ((AbstractRule) target).getName() != null) {
091: ruleBuf.append(((AbstractRule) target).getName());
092: } else {
093: ruleBuf.append(Integer.toString(target.hashCode(),
094: Character.MAX_RADIX));
095: }
096:
097: this .ruleInstance = target;
098: this .ruleMethod = method;
099:
100: ruleBuf.append("].");
101: ruleBuf.append(method.getName());
102: ruleBuf.append("(");
103: Class[] parameterTypes = method.getParameterTypes();
104: for (int i = 0; i < parameterTypes.length; i++) {
105: if (i > 0) {
106: ruleBuf.append(",");
107: }
108: ruleBuf.append(parameterTypes[i].getName());
109: }
110: ruleBuf.append(")");
111: ruleFullName = ruleBuf.toString();
112:
113: final int PRIME = 31;
114: int result = 1;
115: result = PRIME * result + depth;
116: result = PRIME * result
117: + ((facts == null) ? 0 : facts.hashCode());
118: result = PRIME * result + hashCode;
119: result = PRIME
120: * result
121: + ((ruleFullName == null) ? 0 : ruleFullName.hashCode());
122:
123: hashCode = result;
124: }
125:
126: private Set facts = new HashSet();
127:
128: /**
129: * Adds fact to derivation.
130: * If fact is instance of conclusion then its derivation depth becomes Max(ownDepth, conclusionDepth).
131: * @param fact
132: */
133: public synchronized void addSourceFact(Object fact) {
134: if (facts.add(fact)) {
135: hashCode ^= fact.hashCode();
136: if (fact instanceof Conclusion) {
137: depth = Math.max(depth, ((Conclusion) fact).getDepth());
138: }
139: }
140: }
141:
142: /**
143: * @return Full rule name which includes class and method names and object hash code
144: */
145: public String getRuleFullName() {
146: return ruleFullName;
147: }
148:
149: public synchronized String toString() {
150: StringBuffer buf = new StringBuffer("[Derivation] "
151: + ruleFullName);
152: buf.append("[");
153: Iterator it = facts.iterator();
154: while (it.hasNext()) {
155: buf.append(it.next());
156: if (it.hasNext()) {
157: buf.append(",");
158: }
159: }
160: buf.append("]");
161: return buf.toString();
162: }
163:
164: public void toDom(Element holder) {
165: holder.setAttribute("depth", String.valueOf(getDepth()));
166: holder.setAttribute("rule", ruleFullName);
167: holder.setAttribute("hash-code", Integer.toString(hashCode,
168: Character.MAX_RADIX));
169: CompositeDomSerializer domSerializer = CompositeDomSerializer
170: .getThreadInstance();
171: Iterator it = facts.iterator();
172: while (it.hasNext()) {
173: Element fe = holder.getOwnerDocument()
174: .createElement("fact");
175: holder.appendChild(fe);
176: domSerializer.toDomSerializable(it.next()).toDom(fe);
177: }
178: }
179:
180: /**
181: * Derivation is negated if any of its constituent facts in negated.
182: * @param negator
183: * @return
184: */
185: public boolean negatedBy(Negator negator) {
186: Iterator it = facts.iterator();
187: while (it.hasNext()) {
188: Object fact = it.next();
189: if (fact instanceof Conclusion) {
190: if (((Conclusion) fact).isNegatedBy(negator)) {
191: return true;
192: }
193: } else {
194: if (negator.negates(fact)) {
195: return true;
196: }
197: }
198: }
199: return false;
200: }
201:
202: // public boolean equals(Object obj) {
203: // return obj==this || (
204: // obj!=null
205: // && hashCode==obj.hashCode()
206: // && getClass().equals(obj.getClass())
207: // && rule.equals(((Derivation) obj).getRule())
208: // && facts.equals(((Derivation) obj).getFacts()));
209: // }
210:
211: public boolean equals(Object obj) {
212: if (this == obj) {
213: return true;
214: }
215:
216: if (obj == null) {
217: return false;
218: }
219:
220: if (getClass() != obj.getClass()) {
221: return false;
222: }
223:
224: final Derivation other = (Derivation) obj;
225: // if (depth != other.depth) {
226: // return false;
227: // }
228:
229: if (facts == null) {
230: if (other.facts != null) {
231: return false;
232: }
233: } else if (!facts.equals(other.facts)) {
234: return false;
235: }
236:
237: if (ruleFullName == null) {
238: if (other.ruleFullName != null) {
239: return false;
240: }
241: } else if (!ruleFullName.equals(other.ruleFullName)) {
242: return false;
243: }
244: return true;
245: }
246:
247: public Set getFacts() {
248: return Collections.unmodifiableSet(facts);
249: }
250:
251: private int hashCode;
252:
253: public int hashCode() {
254: return hashCode;
255: }
256:
257: public MutableTreeNode toTree(final String title) {
258: DefaultMutableTreeNode ret = new DefaultMutableTreeNode(this ) {
259: public String toString() {
260: return title + " [Derivation] " + ruleFullName
261: + " [depth=" + depth + "]";
262: }
263: };
264:
265: Iterator it = facts.iterator();
266: while (it.hasNext()) {
267: ret.add(CompositeVisualizer.getThreadInstance()
268: .toVisualizable(it.next()).toTree(""));
269: }
270:
271: return ret;
272: }
273:
274: public TableModel toTable() {
275: DefaultTableModel tm = new DefaultTableModel(facts.size() + 1,
276: 2);
277: tm.setColumnIdentifiers(new String[] { "Type", "Value" });
278:
279: tm.setValueAt("(rule)", 0, 0);
280: tm.setValueAt(ruleFullName, 0, 1);
281:
282: Iterator it = facts.iterator();
283: for (int i = 1; it.hasNext(); i++) {
284: Object fact = it.next();
285: tm.setValueAt(fact.getClass().getName(), i, 0);
286: tm.setValueAt(fact, i, 1);
287: }
288:
289: return tm;
290: }
291:
292: public boolean isDerivedFrom(Object fact) {
293: if (fact == null) {
294: return false;
295: }
296:
297: Iterator it = facts.iterator();
298: while (it.hasNext()) {
299: Object baseFact = it.next();
300: if (fact.equals(baseFact)) {
301: return true;
302: }
303:
304: if (baseFact instanceof Conclusion
305: && ((Conclusion) baseFact).isDerivedFrom(fact)) {
306: return true;
307: }
308: }
309: return false;
310: }
311: }
|