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 }