001 package nl.cwi.sen1.visbase.rstorecontainer;
002
003 import java.util.ArrayList;
004 import java.util.LinkedHashMap;
005 import java.util.List;
006 import java.util.Map;
007 import java.util.NoSuchElementException;
008 import java.util.TreeSet;
009
010 import nl.cwi.sen1.relationstores.types.RStore;
011 import nl.cwi.sen1.relationstores.types.RTuple;
012 import nl.cwi.sen1.relationstores.types.RTupleRtuples;
013 import nl.cwi.sen1.visbase.rstorecontainer.datatypes.FactInfo;
014 import nl.cwi.sen1.visbase.rstorecontainer.datatypes.FactInfoList;
015
016 import org.apache.commons.logging.Log;
017 import org.apache.commons.logging.LogFactory;
018
019 import aterm.pure.ATermApplImpl;
020
021 /**
022 * Keeps track of the Rtuples associated to a RStore. Manages the identifiers.
023 */
024 public class RStoreTracker {
025 private static final Log m_log = LogFactory.getLog(RStoreContainer.class);
026
027 private RStore m_RStore;
028
029 private Map<Integer, RTuple> m_loadedRTuplesMap;
030
031 private Map<String, Integer> m_identifiedRTuplesMap;
032
033 /**
034 * Default Constructor. Reads all RTuple (facts) from the given RStore
035 *
036 * @param rStore
037 *
038 * @throws RuntimeException
039 * if rStore input is null
040 */
041 public RStoreTracker(RStore rStore) {
042 if (rStore == null) {
043 throw new RuntimeException("RStore input should not be null");
044 }
045
046 updateRStore(rStore);
047 }
048
049 /**
050 * Updates this tracker with the given RStore
051 *
052 * @param rStore
053 */
054 private void updateRStore(RStore rStore) {
055 m_RStore = rStore;
056
057 m_identifiedRTuplesMap = new LinkedHashMap<String, Integer>();
058 m_loadedRTuplesMap = new LinkedHashMap<Integer, RTuple>();
059
060 RTupleRtuples rTuples = rStore.getRtuples();
061
062 final int numRTuples = rTuples.getLength();
063
064 for (int tupleNum = 0; tupleNum < numRTuples; tupleNum++) {
065 RTuple rTuple = rTuples.getRTupleAt(tupleNum);
066
067 if (rTuple != null) {
068 registerNewRTuple(rTuple);
069 }
070 }
071 }
072
073 /**
074 * Updates this tracker with new, deleted or updated facts
075 *
076 * @param updatedRStore
077 * @return list of ID's of facts that where updated
078 */
079 public synchronized List<Integer> update(RStore updatedRStore) {
080 List<Integer> updatedFactIds = new ArrayList<Integer>();
081
082 RTupleRtuples rTuples = updatedRStore.getRtuples();
083
084 final int numRTuples = rTuples.getLength();
085
086 for (int tupleNum = 0; tupleNum < numRTuples; tupleNum++) {
087 RTuple rTuple = rTuples.getRTupleAt(tupleNum);
088
089 if (rTuple != null) {
090 final String identifier = createIdentifierForRTuple(rTuple);
091
092 // Check if a RTuple already existed with this identifier
093 if (m_identifiedRTuplesMap.containsKey(identifier)) {
094 /* RTuple exists */
095
096 Integer existingId = m_identifiedRTuplesMap.get(identifier);
097
098 if (m_log.isDebugEnabled()) {
099 m_log.info("RTuple already exists for identidier: "
100 + identifier + "(id: " + existingId + ")");
101 }
102
103 RTuple existingRTuple = m_loadedRTuplesMap.get(existingId);
104
105 boolean rTuplesDifferFromEachOther = diffRtuples(
106 existingRTuple, rTuple);
107
108 if (rTuplesDifferFromEachOther) {
109 if (m_log.isInfoEnabled()) {
110 m_log
111 .info("Replacing existing RTuple with new RTuple");
112 }
113
114 updatedFactIds.add(existingId);
115
116 // Replace existing loaded RTuple with new RTuple
117 m_loadedRTuplesMap.put(existingId, rTuple);
118 }
119 }
120 }
121 }
122
123 // Replace current RStore with new RStore, which ensures that all added
124 // and removed relations are updated
125 updateRStore(updatedRStore);
126
127 return updatedFactIds;
128 }
129
130 /**
131 * Simple getter
132 *
133 * @return the RStore object belonging to this Fact Tracker
134 */
135 public RStore getRStore() {
136 return m_RStore;
137 }
138
139 /**
140 * Simple getter
141 *
142 * @param id
143 * @return the RTuple belonging to the id (null if it doesn't exist)
144 */
145 public RTuple getRTuple(int id) {
146 RTuple rTuple = m_loadedRTuplesMap.get(new Integer(id));
147
148 if (rTuple == null) {
149 if (m_log.isWarnEnabled()) {
150 m_log.warn("RTuple fact doesn't exist for id: " + id
151 + ". Valid ID's are: " + m_loadedRTuplesMap.keySet());
152 }
153 }
154
155 return rTuple;
156 }
157
158 /**
159 * Creates a list of FactInfo objects containing the descriptive data from
160 * the found facts (using the RTuples in the RStore).
161 *
162 * @return A FactInfoList object containing information about all the facts
163 * in the RStore.
164 */
165 public FactInfoList getFactInfoFromRStore() {
166
167 FactInfoList factInfoList = new FactInfoList();
168
169 RTupleRtuples rTuples = m_RStore.getRtuples();
170
171 final int numRTuples = rTuples.getLength();
172 for (int tupleNum = 0; tupleNum < numRTuples; tupleNum++) {
173 RTuple rTuple = rTuples.getRTupleAt(tupleNum);
174
175 if (rTuple != null) {
176 FactInfo factInfo = new FactInfo(tupleNum + 1, rTuple);
177
178 factInfoList.addFactInfoToList(factInfo);
179 }
180 }
181
182 return factInfoList;
183 }
184
185 /**
186 * Checks if RTuples differ from eachother
187 *
188 * @param existingRTuple
189 * @param newRTuple
190 * @return <b>true</b> if RTuples are <b>NOT equal</b>
191 *
192 * @throws RuntimeException
193 * if input is null
194 */
195 public static boolean diffRtuples(RTuple existingRTuple, RTuple newRTuple) {
196 boolean result_not_equal;
197
198 if (existingRTuple == null) {
199 throw new RuntimeException(
200 "existingRTuple input should not be null");
201 }
202 if (newRTuple == null) {
203 throw new RuntimeException("newRTuple input should not be null");
204 }
205
206 result_not_equal = !existingRTuple.equals(newRTuple);
207
208 m_log.debug("existingRTuple is " + (result_not_equal ? "NOT" : "")
209 + " EQUAL to newRTuple");
210
211 return result_not_equal;
212 }
213
214 /**
215 * Registers
216 *
217 * @param rTuple
218 * @throws RuntimeException
219 * if rtuple is null
220 */
221 protected void registerNewRTuple(RTuple rTuple) {
222 if (rTuple == null) {
223 throw new RuntimeException("RTuple input should not be null");
224 }
225
226 /* RTuple is new, generate new number identidfier */
227
228 // Create identification for this RTuple
229 final String identifier = createIdentifierForRTuple(rTuple);
230
231 // Use a TreeSet to sort the Keys (whe want to have the highest)
232 TreeSet<Integer> existingIds = new TreeSet<Integer>(m_loadedRTuplesMap
233 .keySet());
234
235 Integer lastId = null;
236
237 try {
238 lastId = existingIds.last();
239 } catch (NoSuchElementException ex) {
240 lastId = new Integer(0);
241 }
242
243 Integer newId = new Integer(lastId.intValue() + 1);
244
245 // while (m_loadedRTuplesMap.containsKey(newId)) {
246 // if (m_log.isWarnEnabled()) {
247 // m_log.warn("Key already existed in m_loadedRTuplesMap: "
248 // + newId + ". *generating a new one*");
249 // }
250 //
251 // newId = new Integer(newId.intValue() + 1);
252 // }
253
254 if (m_log.isDebugEnabled()) {
255 m_log.debug("Identified new RTuple. id:" + newId + ", identifier: "
256 + identifier);
257 }
258
259 // Register new RTuple
260 m_identifiedRTuplesMap.put(identifier, newId);
261 m_loadedRTuplesMap.put(newId, rTuple);
262 }
263
264 /**
265 * Used to create a textual identifier for a RTuple
266 *
267 * @param rTuple
268 * @return the identifier (name + "-" + rType)
269 * @throws RuntimeException
270 * if rtuple is null
271 */
272 public static String createIdentifierForRTuple(RTuple rTuple) {
273 if (rTuple == null) {
274 throw new RuntimeException("RTuple input should not be null");
275 }
276
277 final String m_name = ((ATermApplImpl) rTuple.getVariable()
278 .getArgument(0)).getName();
279 // TODO is it wise to use the toString() method
280 final String m_rType = rTuple.getRtype().toString();
281
282 final String identifier = m_name + "-" + m_rType;
283
284 return identifier;
285 }
286 }