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.util.ArrayList; 027 import java.util.Collection; 028 import java.util.HashMap; 029 import java.util.Iterator; 030 import java.util.LinkedHashMap; 031 import java.util.Map; 032 033 import javax.rules.Handle; 034 035 import biz.hammurapi.config.Component; 036 import biz.hammurapi.config.ComponentBase; 037 import biz.hammurapi.config.ConfigurationException; 038 039 /** 040 * Handle manager implementation which keeps handles in memory. 041 * Objects are compared by eqals(). 042 * This handle manager doesn't hold facts which superced each other, but retains only superceeding fact 043 * (e.g. if Mother supercedes Parent then Parent will not be retained in the manager when Mother is added). 044 * @author Pavel Vlasov 045 * @version ${Revision} 046 */ 047 public class KnowledgeMaximizingHandleManager extends ComponentBase implements HandleManager, Component { 048 049 private Map handleMap; 050 051 private String storageReference; 052 053 /** 054 * Path to object storage. 055 * @param storageReference 056 */ 057 public void setStorageReference(String storageReference) { 058 this.storageReference = storageReference; 059 } 060 061 062 private static class HandleImpl implements Handle, Serializable { 063 private static final long serialVersionUID = -6198273232968377370L; 064 private Object master; 065 066 067 HandleImpl(Object master) { 068 this.master=master; 069 } 070 071 public boolean equals(Object obj) { 072 return obj instanceof HandleImpl && master.equals(((HandleImpl) obj).master); 073 } 074 075 public int hashCode() { 076 return master==null ? 0 : master.hashCode(); 077 } 078 079 public String toString() { 080 return getClass().getName() + " -> " + master; 081 } 082 } 083 084 /** 085 * Adds new object to the manager if this object is not already there and there is no superceding object. 086 * If object being added supercedes any objects in the manager these objects will be removed. 087 */ 088 synchronized public Handle addObject(Object object) { 089 Iterator it=handleMap.keySet().iterator(); 090 while (it.hasNext()) { 091 HandleImpl existingHandle=(HandleImpl) it.next(); 092 if (existingHandle.master.equals(object)) { 093 // Merge derivations for equal conclusions. 094 if (object!=existingHandle.master && object instanceof Conclusion) { 095 ((Conclusion) existingHandle.master).mergeDerivations((Conclusion) object); 096 } 097 return existingHandle; 098 } 099 100 if (object instanceof Supercedable && existingHandle.master instanceof Supercedable) { 101 if (((Supercedable) existingHandle.master).supercedes(object)) { 102 return existingHandle; 103 } 104 } 105 106 if (object instanceof Supercedable && existingHandle.master instanceof Supercedable) { 107 if (((Supercedable) object).supercedes(existingHandle.master)) { 108 rebind(existingHandle, object); 109 return existingHandle; 110 } 111 } 112 } 113 114 Handle newHandle=new HandleImpl(object); 115 handleMap.put(newHandle, newHandle); 116 return newHandle; 117 } 118 119 synchronized public Object getObject(Handle handle) { 120 if (handle instanceof HandleImpl) { 121 return ((HandleImpl) handle).master; 122 } 123 124 throw new IllegalArgumentException("Foreign handle: "+handle); 125 } 126 127 public Collection getObjects() { 128 ArrayList ret=new ArrayList(); 129 Iterator it=handleMap.keySet().iterator(); 130 while (it.hasNext()) { 131 ret.add(((HandleImpl) it.next()).master); 132 } 133 return ret; 134 } 135 136 public Collection getHandles() { 137 return handleMap.keySet(); 138 } 139 140 synchronized public void remove(Handle handle) { 141 handleMap.remove(handle); 142 } 143 144 public boolean contains(Handle handle) { 145 return handleMap.containsKey(handle); 146 } 147 148 synchronized public void rebind(Handle handle, Object object) { 149 if (handle instanceof HandleImpl) { 150 handleMap.remove(handle); 151 ((HandleImpl) handle).master=object; 152 handleMap.put(handle, handle); 153 } else { 154 throw new IllegalArgumentException("Foreign handle: "+handle); 155 } 156 } 157 158 public void remove(Object obj) { 159 handleMap.remove(new HandleImpl(obj)); 160 } 161 162 public synchronized void clear() { 163 handleMap.clear(); 164 } 165 166 public void start() throws ConfigurationException { 167 if (storageReference==null) { 168 handleMap=new LinkedHashMap(); 169 } else { 170 handleMap = (Map) ((ObjectStorage) get(storageReference)).get("handle-manager"); 171 if (handleMap==null) { 172 handleMap=new HashMap(); 173 ((ObjectStorage) get(storageReference)).put("handle-manager", handleMap); 174 } 175 } 176 } 177 178 public void stop() throws ConfigurationException { 179 // Nothing 180 } 181 182 public synchronized boolean isNegatedBy(Negator negator) { 183 Iterator it=handleMap.keySet().iterator(); 184 while (it.hasNext()) { 185 HandleImpl handle=(HandleImpl) it.next(); 186 if (Conclusion.object2Negator(handle.master, negator)) { 187 it.remove(); 188 } 189 } 190 191 return false; 192 } 193 }