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    }