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    }