001    package toolbus.matching;
002    
003    import java.util.ArrayList;
004    import java.util.HashMap;
005    import java.util.HashSet;
006    import java.util.Iterator;
007    import java.util.LinkedList;
008    import java.util.List;
009    import java.util.Map;
010    import java.util.Set;
011    
012    import toolbus.TBTermFactory;
013    import toolbus.atom.Atom;
014    import toolbus.atom.msg.RecMsg;
015    import toolbus.atom.msg.SndMsg;
016    import toolbus.atom.note.SndNote;
017    import toolbus.atom.note.Subscribe;
018    import toolbus.process.ProcessInstance;
019    import toolbus.util.collections.ConcurrentHashMap;
020    import aterm.ATerm;
021    
022    /**
023     * This store keeps track of all links between sending and receiving communication atoms.
024     * 
025     * @author Arnold Lankamp
026     */
027    public class MatchStore{
028            private final static List<RecMsg> NOMESSAGEPARTNERS = new ArrayList<RecMsg>(0);
029            private final static Set<ProcessInstance> NONOTEPARTNERS = new HashSet<ProcessInstance>(0);
030            
031            private final TBTermFactory tbFactory;
032            private volatile List<Atom> atomSet;
033            
034            private final ConcurrentHashMap<ATerm, List<ATerm>> messageLinks;
035            private final ConcurrentHashMap<ATerm, List<ATerm>> noteLinks;
036            
037            private final Map<ATerm, List<RecMsg>> messageMappings;
038            private final Map<ATerm, Map<ProcessInstance, MappingRefCount>> noteMappings;
039            
040            private final Object messageLock = new Object();
041            private final Object noteLock = new Object();
042            
043            /**
044             * Constructor.
045             * 
046             * @param tbTermFactory
047             *            The term factory to use for matching.
048             */
049            public MatchStore(TBTermFactory tbTermFactory){
050                    super();
051                    
052                    this.tbFactory = tbTermFactory;
053                    
054                    messageLinks = new ConcurrentHashMap<ATerm, List<ATerm>>();
055                    noteLinks = new ConcurrentHashMap<ATerm, List<ATerm>>();
056                    
057                    messageMappings = new HashMap<ATerm, List<RecMsg>>();
058                    noteMappings = new HashMap<ATerm, Map<ProcessInstance, MappingRefCount>>();
059            }
060            
061            /**
062             * Initializes this match store with the given set of atoms.
063             * 
064             * @param atomSet
065             *            The complete collection of atoms, for which the relations must to be calculated.
066             */
067            public void initialize(List<Atom> atomSet){
068                    this.atomSet = atomSet;
069                    
070                    calculateMatches();
071            }
072            
073            /**
074             * Staticly determains all (potential) relations between the atoms.
075             */
076            private void calculateMatches(){
077                    List<ATerm> receiveMessages = new LinkedList<ATerm>();
078                    List<ATerm> subscribeNotes = new LinkedList<ATerm>();
079                    
080                    Iterator<Atom> atomsIteratorForRecMsg = atomSet.iterator();
081                    while(atomsIteratorForRecMsg.hasNext()){
082                            Atom atom = atomsIteratorForRecMsg.next();
083                            if(atom instanceof RecMsg){
084                                    addReceiveMessagePattern((RecMsg) atom, receiveMessages);
085                            }else if(atom instanceof Subscribe){
086                                    addSubscribeNotePattern((Subscribe) atom, subscribeNotes);
087                            }
088                    }
089                    
090                    Iterator<Atom> atomsIteratorForSndMsg = atomSet.iterator();
091                    while(atomsIteratorForSndMsg.hasNext()){
092                            Atom atom = atomsIteratorForSndMsg.next();
093                            if(atom instanceof SndMsg){
094                                    addSendMessagePattern((SndMsg) atom, receiveMessages);
095                            }else if(atom instanceof SndNote){
096                                    addSendNotePattern((SndNote) atom, subscribeNotes);
097                            }
098                    }
099            }
100            
101            /**
102             * Indexes the given receive message atom.
103             * 
104             * @param message
105             *            The receive message atom.
106             * @param receiveMessages
107             *            The list to add the message's match pattern to.
108             */
109            private void addReceiveMessagePattern(RecMsg message, List<ATerm> receiveMessages){
110                    ATerm matchPattern = message.msg;
111                    receiveMessages.add(matchPattern);
112                    messageLinks.put(matchPattern, new ArrayList<ATerm>());
113            }
114            
115            /**
116             * Indexes the given send message atom. NOTE: All receive message atoms need to have been added
117             * prior to the invokation of this method, otherwise the the ToolBus may exhibit undefined
118             * runtime behaviour.
119             * 
120             * @param message
121             *            The send message atom.
122             * @param receiveMessages
123             *            The complete list of receive messages.
124             */
125            private void addSendMessagePattern(SndMsg message, List<ATerm> receiveMessages){
126                    ATerm matchPattern = message.msg;
127                    
128                    Iterator<ATerm> receiveMessagePatternIterator = receiveMessages.iterator();
129                    while(receiveMessagePatternIterator.hasNext()){
130                            ATerm receiveMessagePattern = receiveMessagePatternIterator.next();
131                            
132                            if(tbFactory.mightMatch(matchPattern, receiveMessagePattern)){
133                                    List<ATerm> sendMessageList = messageLinks.get(receiveMessagePattern);
134                                    sendMessageList.add(matchPattern);
135                            }
136                    }
137            }
138            
139            /**
140             * Indexes the given subscribe atom.
141             * 
142             * @param subscribeNote
143             *            The subscribe atom.
144             * @param subscribeNotes
145             *            The list to add the subscribe's match pattern to.
146             */
147            private void addSubscribeNotePattern(Subscribe subscribeNote, List<ATerm> subscribeNotes){
148                    ATerm matchPattern = subscribeNote.notePattern;
149                    subscribeNotes.add(matchPattern);
150                    noteLinks.put(matchPattern, new ArrayList<ATerm>());
151            }
152            
153            /**
154             * Indexes the given send note atom. NOTE: All subscribe atoms need to have been added prior to
155             * the invokation of this method, otherwise the the ToolBus may exhibit undefined runtime
156             * behaviour.
157             * 
158             * @param message
159             *            The send note atom.
160             * @param subscribeNotes
161             *            The complete list of subscribes.
162             */
163            private void addSendNotePattern(SndNote message, List<ATerm> subscribeNotes){
164                    ATerm matchPattern = message.notePattern;
165                    
166                    Iterator<ATerm> subscribeNotePatternIterator = subscribeNotes.iterator();
167                    while(subscribeNotePatternIterator.hasNext()){
168                            ATerm subscribeNotePattern = subscribeNotePatternIterator.next();
169                            
170                            if(tbFactory.mightMatch(matchPattern, subscribeNotePattern)){
171                                    List<ATerm> sendNoteList = noteLinks.get(subscribeNotePattern);
172                                    sendNoteList.add(matchPattern);
173                            }
174                    }
175            }
176            
177            /**
178             * Registers the given, instantiated, receive message atom.
179             * 
180             * @param receiveMessage
181             *            The receive message atom to register.
182             */
183            public void registerReceiveMessage(RecMsg receiveMessage){
184                    List<ATerm> matches = messageLinks.get(receiveMessage.msg);
185                    if(matches == null) return;
186                    
187                    Iterator<ATerm> matchesIterator = matches.iterator();
188                    
189                    synchronized(messageLock){
190                            while(matchesIterator.hasNext()){
191                                    ATerm pattern = matchesIterator.next();
192                                    List<RecMsg> receiveMessageList = messageMappings.get(pattern);
193                                    if(receiveMessageList == null){
194                                            receiveMessageList = new LinkedList<RecMsg>();
195                                            messageMappings.put(pattern, receiveMessageList);
196                                    }
197                                    receiveMessageList.add(receiveMessage);
198                            }
199                    }
200            }
201            
202            /**
203             * Deregisteres the given, instantiated, receive message atom.
204             * 
205             * @param receiveMessage
206             *            The receive message atom to deregister.
207             */
208            public void deregisterReceiveMessage(RecMsg receiveMessage){
209                    List<ATerm> matches = messageLinks.get(receiveMessage.msg);
210                    if(matches == null) return;
211                    
212                    Iterator<ATerm> matchesIterator = matches.iterator();
213                    
214                    synchronized(messageLock){
215                            while(matchesIterator.hasNext()){
216                                    ATerm pattern = matchesIterator.next();
217                                    List<RecMsg> receiveMessageList = messageMappings.get(pattern);
218                                    receiveMessageList.remove(receiveMessage);
219                            }
220                    }
221            }
222            
223            /**
224             * Gathers the potential partners for the given send message pattern.
225             * 
226             * @param pattern
227             *            The send message pattern.
228             * @return The collections of potentially matching partners.
229             */
230            public List<RecMsg> getPossibleMessagePartners(ATerm pattern){
231                    List<RecMsg> recMessages;
232                    
233                    synchronized(messageLock){
234                            recMessages = messageMappings.get(pattern);
235                    }
236                    
237                    if(recMessages == null) return NOMESSAGEPARTNERS;
238                    
239                    return recMessages;
240            }
241            
242            /**
243             * Reference count structure.
244             * 
245             * @author Arnold Lankamp
246             */
247            private static class MappingRefCount{
248                    public int nrOfMappings;
249            }
250            
251            /**
252             * Registers the given, instantiated, subscribe atom.
253             * 
254             * @param subscribeNote
255             *            The subscribe atom to register.
256             */
257            public void registerSubscribeNote(Subscribe subscribeNote){
258                    List<ATerm> matches = noteLinks.get(subscribeNote.notePattern);
259                    if(matches == null) return;
260                    
261                    Iterator<ATerm> matchesIterator = matches.iterator();
262                    
263                    synchronized(noteLock){
264                            while(matchesIterator.hasNext()){
265                                    ProcessInstance pi = subscribeNote.getProcess();
266                                    
267                                    ATerm pattern = matchesIterator.next();
268                                    Map<ProcessInstance, MappingRefCount> subscribeMappingsMap = noteMappings.get(pattern);
269                                    if(subscribeMappingsMap == null){
270                                            subscribeMappingsMap = new HashMap<ProcessInstance, MappingRefCount>(4);
271                                            noteMappings.put(pattern, subscribeMappingsMap);
272                                            
273    
274                                            MappingRefCount mrc = new MappingRefCount();
275                                            mrc.nrOfMappings = 1;
276                                            subscribeMappingsMap.put(pi, mrc);
277                                    }else{
278                                            MappingRefCount existingValue = subscribeMappingsMap.get(pi);
279                                            if(existingValue != null){
280                                                    existingValue.nrOfMappings++;
281                                            }else{
282                                                    MappingRefCount mrc = new MappingRefCount();
283                                                    mrc.nrOfMappings = 1;
284                                                    subscribeMappingsMap.put(pi, mrc);
285                                            }
286                                    }
287                            }
288                    }
289            }
290            
291            /**
292             * Deregisters the given, instantiated, subscribe atom.
293             * 
294             * @param subscribeNote
295             *            The subscribe atom to deregister.
296             */
297            public void deregisterSubscribeNote(Subscribe subscribeNote){
298                    List<ATerm> matches = noteLinks.get(subscribeNote.notePattern);
299                    if(matches == null) return;
300                    
301                    Iterator<ATerm> matchesIterator = matches.iterator();
302                    
303                    synchronized(noteLock){
304                            while(matchesIterator.hasNext()){
305                                    ATerm pattern = matchesIterator.next();
306                                    Map<ProcessInstance, MappingRefCount> subscribeMappingsMap = noteMappings.get(pattern);
307                                    
308                                    MappingRefCount mapping = subscribeMappingsMap.get(subscribeNote.getProcess());
309                                    if((--mapping.nrOfMappings) == 0){
310                                            subscribeMappingsMap.remove(mapping);
311                                    }
312                            }
313                    }
314            }
315            
316            /**
317             * Gathers the potential partners for the given send note pattern.
318             * 
319             * @param pattern
320             *            The send message pattern.
321             * @return The collections of potentially matching partners.
322             */
323            public Set<ProcessInstance> getPossibleNotePartners(ATerm pattern){
324                    synchronized(noteLock){
325                            Map<ProcessInstance, MappingRefCount> subNotesMappings = noteMappings.get(pattern);
326                            if(subNotesMappings == null) return NONOTEPARTNERS;
327                            
328                            return subNotesMappings.keySet();
329                    }
330            }
331            
332            /**
333             * Dumps a list of partnerless communication atoms to standard out.
334             */
335            public void printPartnerlessCommunicationAtoms(){
336                    printPartnerlessSenders();
337                    printPartnerlessReceivers();
338            }
339            
340            /**
341             * Gathers a list of partnerless message sending atoms.
342             * 
343             * @return A list of partnerless message sending atoms.
344             */
345            public List<SndMsg> findPartnerlessSendMessageAtoms(){
346                    List<SndMsg> deadSendMessageAtoms = new ArrayList<SndMsg>();
347                    
348                    List<ATerm> receiveMessages = new LinkedList<ATerm>();
349    
350                    Iterator<Atom> atomsIteratorForRecMsg = atomSet.iterator();
351                    while(atomsIteratorForRecMsg.hasNext()){
352                            Atom atom = atomsIteratorForRecMsg.next();
353                            if(atom instanceof RecMsg){
354                                    receiveMessages.add(((RecMsg) atom).msg);
355                            }
356                    }
357                    
358                    Iterator<Atom> atomsIteratorForSndMsg = atomSet.iterator();
359                    while(atomsIteratorForSndMsg.hasNext()){
360                            Atom atom = atomsIteratorForSndMsg.next();
361                            
362                            if(atom instanceof SndMsg){
363                                    SndMsg sendMessage = (SndMsg) atom;
364                                    ATerm matchPattern = sendMessage.msg;
365                                    
366                                    boolean added = false;
367                                    
368                                    Iterator<ATerm> receiveMessagePatternIterator = receiveMessages.iterator();
369                                    while(receiveMessagePatternIterator.hasNext()){
370                                            ATerm receiveMessagePattern = receiveMessagePatternIterator.next();
371                                            
372                                            if(tbFactory.mightMatch(matchPattern, receiveMessagePattern)){
373                                                    added = true;
374                                                    break;
375                                            }
376                                    }
377                                    
378                                    if(!added) deadSendMessageAtoms.add(sendMessage);
379                            }
380                    }
381                    
382                    return deadSendMessageAtoms;
383            }
384            
385            /**
386             * Gathers a list of partnerless note sending atoms.
387             * 
388             * @return A list of partnerless note sending atoms.
389             */
390            public List<SndNote> findPartnerlessSendNoteAtoms(){
391                    List<SndNote> deadSendNoteAtoms = new ArrayList<SndNote>();
392                    
393                    List<ATerm> subscribeNotes = new LinkedList<ATerm>();
394    
395                    Iterator<Atom> atomsIteratorForRecMsg = atomSet.iterator();
396                    while(atomsIteratorForRecMsg.hasNext()){
397                            Atom atom = atomsIteratorForRecMsg.next();
398                            if(atom instanceof Subscribe){
399                                    subscribeNotes.add(((Subscribe) atom).notePattern);
400                            }
401                    }
402                    
403                    Iterator<Atom> atomsIteratorForSndMsg = atomSet.iterator();
404                    while(atomsIteratorForSndMsg.hasNext()){
405                            Atom atom = atomsIteratorForSndMsg.next();
406                            
407                            if(atom instanceof SndNote){
408                                    SndNote sendNote = (SndNote) atom;
409                                    ATerm matchPattern = sendNote.notePattern;
410    
411                                    boolean added = false;
412                                    
413                                    Iterator<ATerm> subscribeNotePatternIterator = subscribeNotes.iterator();
414                                    while(subscribeNotePatternIterator.hasNext()){
415                                            ATerm subscribeNotePattern = subscribeNotePatternIterator.next();
416                                            
417                                            if(tbFactory.mightMatch(matchPattern, subscribeNotePattern)){
418                                                    added = true;
419                                                    break;
420                                            }
421                                    }
422                                    
423                                    if(!added) deadSendNoteAtoms.add(sendNote);
424                            }
425                    }
426                    
427                    return deadSendNoteAtoms;
428            }
429            
430            /**
431             * Gathers a list of partnerless message receiving atoms.
432             * 
433             * @return A list of partnerless message receiving atoms.
434             */
435            public List<RecMsg> findPartnerLessReceiveMessageAtoms(){
436                    List<RecMsg> deadReceiveMessageAtoms = new ArrayList<RecMsg>();
437                    
438                    Iterator<Atom> atomsIterator = atomSet.iterator();
439                    while(atomsIterator.hasNext()){
440                            Atom atom = atomsIterator.next();
441                            if(atom instanceof RecMsg){
442                                    RecMsg recMsg = (RecMsg) atom;
443                                    
444                                    if(messageLinks.get(recMsg.msg).isEmpty()){
445                                            deadReceiveMessageAtoms.add(recMsg);
446                                    }
447                            }
448                    }
449                    
450                    return deadReceiveMessageAtoms;
451            }
452            
453            /**
454             * Gathers a list of partnerless subscribe atoms.
455             * 
456             * @return A list of partnerless subscribe atoms.
457             */
458            public List<Subscribe> findPartnerlessSubscribeAtoms(){
459                    List<Subscribe> deadSubscribeAtoms = new ArrayList<Subscribe>();
460                    
461                    Iterator<Atom> atomsIterator = atomSet.iterator();
462                    while(atomsIterator.hasNext()){
463                            Atom atom = atomsIterator.next();
464                            
465                            if(atom instanceof Subscribe){
466                                    Subscribe subscribe = (Subscribe) atom;
467                                    
468                                    if(noteLinks.get(subscribe.notePattern).isEmpty()){
469                                            deadSubscribeAtoms.add(subscribe);
470                                    }
471                            }
472                    }
473                    
474                    return deadSubscribeAtoms;
475            }
476            
477            /**
478             * Dumps a list of partnerless sending atoms to standard out.
479             */
480            private void printPartnerlessSenders(){
481                    List<SndMsg> deadSendMessageAtoms = findPartnerlessSendMessageAtoms();
482                    List<SndNote> deadSendNoteAtoms = findPartnerlessSendNoteAtoms();
483                    
484                    List<Atom> deadSendAtoms = new ArrayList<Atom>();
485                    deadSendAtoms.addAll(deadSendMessageAtoms);
486                    deadSendAtoms.addAll(deadSendNoteAtoms);
487                    
488                    Set<Atom> partnerlessAtoms = new HashSet<Atom>();
489                    
490                    Iterator<Atom> deadSendAtomsIterator = deadSendAtoms.iterator();
491                    while(deadSendAtomsIterator.hasNext()){
492                            partnerlessAtoms.add(deadSendAtomsIterator.next());
493                    }
494                    
495                    if((partnerlessAtoms.size()) == 0){
496                            System.out.println("No partnerless send atoms found.");
497                    }else{
498                            System.out.println("Partnerless send atoms:");
499                            System.out.println("{");
500                            
501                            Iterator<Atom> partnerlessMessagesIterator = partnerlessAtoms.iterator();
502                            while(partnerlessMessagesIterator.hasNext()){
503                                    Atom sendAtom = partnerlessMessagesIterator.next();
504                                    System.out.println("\t"+sendAtom.getPosInfo()+"\t\t: "+sendAtom);
505                            }
506                            
507                            System.out.println("}");
508                    }
509            }
510            
511            /**
512             * Dumps a list of partnerless receiving atoms to standard out.
513             */
514            private void printPartnerlessReceivers(){
515                    List<RecMsg> partnerlessReceiveMessageAtoms = findPartnerLessReceiveMessageAtoms();
516                    List<Subscribe> partnerlessSubscribeAtoms = findPartnerlessSubscribeAtoms();
517                    
518                    List<Atom> partnerlessReceivers = new ArrayList<Atom>();
519                    partnerlessReceivers.addAll(partnerlessReceiveMessageAtoms);
520                    partnerlessReceivers.addAll(partnerlessSubscribeAtoms);
521                    
522                    if(partnerlessReceivers.size() == 0){
523                            System.out.println("No partnerless receivers found.");
524                    }else{
525                            System.out.println("Partnerless receive atoms:");
526                            System.out.println("{");
527                            
528                            Iterator<Atom> partnerlessReceiversIterator = partnerlessReceivers.iterator();
529                            while(partnerlessReceiversIterator.hasNext()){
530                                    Atom atom = partnerlessReceiversIterator.next();
531                                    
532                                    System.out.println("\t"+atom.getPosInfo()+"\t\t: "+atom);
533                            }
534                            
535                            System.out.println("}");
536                    }
537            }
538    }