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 }