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    }