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 /**
046 * This class is a tracking element for inference process.
047 * It is created by rules during inference process and attached to facts.
048 * @author Pavel Vlasov
049 * @revision $Revision$
050 */
051 public class Derivation implements DomSerializable, Serializable, 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().getName());
087 ruleBuf.append("[");
088 if (target instanceof AbstractRule && ((AbstractRule) target).getName()!=null) {
089 ruleBuf.append(((AbstractRule) target).getName());
090 } else {
091 ruleBuf.append(Integer.toString(target.hashCode(), Character.MAX_RADIX));
092 }
093
094 this.ruleInstance=target;
095 this.ruleMethod=method;
096
097 ruleBuf.append("].");
098 ruleBuf.append(method.getName());
099 ruleBuf.append("(");
100 Class[] parameterTypes = method.getParameterTypes();
101 for (int i=0; i<parameterTypes.length; i++) {
102 if (i>0) {
103 ruleBuf.append(",");
104 }
105 ruleBuf.append(parameterTypes[i].getName());
106 }
107 ruleBuf.append(")");
108 ruleFullName=ruleBuf.toString();
109
110 final int PRIME = 31;
111 int result = 1;
112 result = PRIME * result + depth;
113 result = PRIME * result + ((facts == null) ? 0 : facts.hashCode());
114 result = PRIME * result + hashCode;
115 result = PRIME * result + ((ruleFullName == null) ? 0 : ruleFullName.hashCode());
116
117 hashCode=result;
118 }
119
120 private Set facts=new HashSet();
121
122 /**
123 * Adds fact to derivation.
124 * If fact is instance of conclusion then its derivation depth becomes Max(ownDepth, conclusionDepth).
125 * @param fact
126 */
127 public synchronized void addSourceFact(Object fact) {
128 if (facts.add(fact)) {
129 hashCode^=fact.hashCode();
130 if (fact instanceof Conclusion) {
131 depth=Math.max(depth, ((Conclusion) fact).getDepth());
132 }
133 }
134 }
135
136 /**
137 * @return Full rule name which includes class and method names and object hash code
138 */
139 public String getRuleFullName() {
140 return ruleFullName;
141 }
142
143 public synchronized String toString() {
144 StringBuffer buf=new StringBuffer("[Derivation] "+ruleFullName);
145 buf.append("[");
146 Iterator it=facts.iterator();
147 while (it.hasNext()) {
148 buf.append(it.next());
149 if (it.hasNext()) {
150 buf.append(",");
151 }
152 }
153 buf.append("]");
154 return buf.toString();
155 }
156
157 public void toDom(Element holder) {
158 holder.setAttribute("depth", String.valueOf(getDepth()));
159 holder.setAttribute("rule", ruleFullName);
160 holder.setAttribute("hash-code", Integer.toString(hashCode, Character.MAX_RADIX));
161 CompositeDomSerializer domSerializer = CompositeDomSerializer.getThreadInstance();
162 Iterator it=facts.iterator();
163 while (it.hasNext()) {
164 Element fe=holder.getOwnerDocument().createElement("fact");
165 holder.appendChild(fe);
166 domSerializer.toDomSerializable(it.next()).toDom(fe);
167 }
168 }
169
170 /**
171 * Derivation is negated if any of its constituent facts in negated.
172 * @param negator
173 * @return
174 */
175 public boolean negatedBy(Negator negator) {
176 Iterator it=facts.iterator();
177 while (it.hasNext()) {
178 Object fact=it.next();
179 if (fact instanceof Conclusion) {
180 if (((Conclusion) fact).isNegatedBy(negator)) {
181 return true;
182 }
183 } else {
184 if (negator.negates(fact)) {
185 return true;
186 }
187 }
188 }
189 return false;
190 }
191
192 // public boolean equals(Object obj) {
193 // return obj==this || (
194 // obj!=null
195 // && hashCode==obj.hashCode()
196 // && getClass().equals(obj.getClass())
197 // && rule.equals(((Derivation) obj).getRule())
198 // && facts.equals(((Derivation) obj).getFacts()));
199 // }
200
201 public boolean equals(Object obj) {
202 if (this == obj) {
203 return true;
204 }
205
206 if (obj == null) {
207 return false;
208 }
209
210 if (getClass() != obj.getClass()) {
211 return false;
212 }
213
214 final Derivation other = (Derivation) obj;
215 // if (depth != other.depth) {
216 // return false;
217 // }
218
219 if (facts == null) {
220 if (other.facts != null) {
221 return false;
222 }
223 } else if (!facts.equals(other.facts)) {
224 return false;
225 }
226
227 if (ruleFullName == null) {
228 if (other.ruleFullName != null) {
229 return false;
230 }
231 } else if (!ruleFullName.equals(other.ruleFullName)) {
232 return false;
233 }
234 return true;
235 }
236
237
238 public Set getFacts() {
239 return Collections.unmodifiableSet(facts);
240 }
241
242 private int hashCode;
243
244 public int hashCode() {
245 return hashCode;
246 }
247
248 public MutableTreeNode toTree(final String title) {
249 DefaultMutableTreeNode ret=new DefaultMutableTreeNode(this) {
250 public String toString() {
251 return title + " [Derivation] " + ruleFullName +" [depth="+depth+"]";
252 }
253 };
254
255 Iterator it=facts.iterator();
256 while (it.hasNext()) {
257 ret.add(CompositeVisualizer.getThreadInstance().toVisualizable(it.next()).toTree(""));
258 }
259
260 return ret;
261 }
262
263 public TableModel toTable() {
264 DefaultTableModel tm=new DefaultTableModel(facts.size()+1,2);
265 tm.setColumnIdentifiers(new String[] {"Type", "Value"});
266
267 tm.setValueAt("(rule)", 0, 0);
268 tm.setValueAt(ruleFullName, 0, 1);
269
270 Iterator it=facts.iterator();
271 for (int i=1; it.hasNext(); i++) {
272 Object fact=it.next();
273 tm.setValueAt(fact.getClass().getName(), i, 0);
274 tm.setValueAt(fact, i, 1);
275 }
276
277 return tm;
278 }
279
280 public boolean isDerivedFrom(Object fact) {
281 if (fact==null) {
282 return false;
283 }
284
285 Iterator it=facts.iterator();
286 while (it.hasNext()) {
287 Object baseFact=it.next();
288 if (fact.equals(baseFact)) {
289 return true;
290 }
291
292 if (baseFact instanceof Conclusion && ((Conclusion) baseFact).isDerivedFrom(fact)) {
293 return true;
294 }
295 }
296 return false;
297 }
298 }