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