001    /**
002     * 
003     * Copyright 2004 Protique Ltd
004     * 
005     * Licensed under the Apache License, Version 2.0 (the "License"); 
006     * you may not use this file except in compliance with the License. 
007     * You may obtain a copy of the License at 
008     * 
009     * http://www.apache.org/licenses/LICENSE-2.0
010     * 
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS, 
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
014     * See the License for the specific language governing permissions and 
015     * limitations under the License. 
016     * 
017     **/
018    package org.activemq.filter;
019    
020    import java.util.HashSet;
021    import java.util.Iterator;
022    import java.util.List;
023    import java.util.Map;
024    import java.util.Set;
025    import org.activemq.message.ActiveMQDestination;
026    import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
027    
028    /**
029     * A Map-like data structure allowing values to be indexed by {@link ActiveMQDestination}
030     * and retrieved by destination - supporting both * and > style of wildcard
031     * as well as composite destinations.
032     * <br>
033     * This class assumes that the index changes rarely but that fast lookup into the index is required.
034     * So this class maintains a pre-calculated index for destination steps. So looking up the values
035     * for "TEST.*" or "*.TEST" will be pretty fast.
036     * <br>
037     * Looking up of a value could return a single value or a List of matching values if a wildcard or
038     * composite destination is used.
039     *
040     * @version $Revision: 1.1.1.1 $
041     */
042    public class DestinationMap {
043        private DestinationMapNode rootNode = new DestinationMapNode();
044        private Map vanillaDestinations = new ConcurrentHashMap();
045        private boolean containsWildCards = false;
046        protected static final String ANY_DESCENDENT = DestinationFilter.ANY_DESCENDENT;
047        protected static final String ANY_CHILD = DestinationFilter.ANY_CHILD;
048        private Set nullAnswer = new HashSet();
049    
050        /**
051         * Looks up the value(s) matching the given Destination key. For simple destinations
052         * this is typically a List of one single value, for wildcards or composite destinations this will typically be
053         * a List of matching values.
054         *
055         * @param key the destination to lookup
056         * @return a List of matching values or an empty list if there are no matching values.
057         */
058        public synchronized Set get(ActiveMQDestination key) {
059            if (key.isComposite()) {
060                List childDestinations = key.getChildDestinations();
061                Set answer = new HashSet(childDestinations.size());
062                for (Iterator iter = childDestinations.iterator(); iter.hasNext();) {
063                    ActiveMQDestination childDestination = (ActiveMQDestination) iter.next();
064                    Object value = get(childDestination);
065                    if (value instanceof Set) {
066                        answer.addAll((Set) value);
067                    }
068                    else if (value != null) {
069                        answer.add(value);
070                    }
071                    return answer;
072                }
073            }
074            return findWildcardMatches(key);
075        }
076    
077        /**
078         * add destination to the map
079         * @param key
080         * @param value
081         */
082        public synchronized void put(ActiveMQDestination key, Object value) {
083            if (key.isComposite()) {
084                List childDestinations = key.getChildDestinations();
085                for (Iterator iter = childDestinations.iterator(); iter.hasNext();) {
086                    ActiveMQDestination childDestination = (ActiveMQDestination) iter.next();
087                    put(childDestination, value);
088                }
089                return;
090            }
091            String[] paths = key.getDestinationPaths();
092            rootNode.add(paths, 0, value);
093            if (key.isWildcard()){
094                containsWildCards = true;
095            }
096            addToVanillaDestinations(key, value);
097        }
098    
099        /**
100         * Removes the value from the associated destination
101         * @param key
102         * @param value
103         */
104        public synchronized void remove(ActiveMQDestination key, Object value) {
105            if (key.isComposite()) {
106                List childDestinations = key.getChildDestinations();
107                for (Iterator iter = childDestinations.iterator(); iter.hasNext();) {
108                    ActiveMQDestination childDestination = (ActiveMQDestination) iter.next();
109                    remove(childDestination, value);
110                }
111                return;
112            }
113            String[] paths = key.getDestinationPaths();
114            rootNode.remove(paths, 0, value);
115            removeFromVanillaDestinations(key,value);
116            
117    
118        }
119    
120        // Implementation methods
121        //-------------------------------------------------------------------------
122        protected Set findWildcardMatches(ActiveMQDestination key) {
123            Set answer = nullAnswer;
124            if (!containsWildCards && !key.isWildcard()){
125                answer = getFromVanillaDestinations(key);
126            }else {
127                answer = new HashSet();
128                String[] paths = key.getDestinationPaths();
129                rootNode.appendMatchingValues(answer, paths, 0);
130            }
131            return answer;
132        }
133    
134        /**
135         * remove all destinations associated with a key
136         * @param key
137         */
138        public void removeAll(ActiveMQDestination key) {
139            if (key.isComposite()) {
140                List childDestinations = key.getChildDestinations();
141                for (Iterator iter = childDestinations.iterator(); iter.hasNext();) {
142                    ActiveMQDestination childDestination = (ActiveMQDestination) iter.next();
143                    removeAll(childDestination);
144                }
145                return;
146            }
147            String[] paths = key.getDestinationPaths();
148            rootNode.removeAll(paths, 0);
149            removeAllFromVanillaDestinations(key);
150        }
151        
152        synchronized private void addToVanillaDestinations(ActiveMQDestination key, Object value) {
153            Set set = new HashSet();
154            Set oldSet = (Set)vanillaDestinations.get(key);
155            if (oldSet != null) {
156                set.addAll(oldSet);
157            }
158            set.add(value);
159            vanillaDestinations.put(key,set);
160        }
161        
162        synchronized private void removeFromVanillaDestinations(ActiveMQDestination key,Object value){
163    
164            Set oldSet = (Set)vanillaDestinations.get(key);
165            if (oldSet != null) {
166                Set set = new HashSet(oldSet);
167                set.remove(value);
168                if (set.isEmpty()){
169                    vanillaDestinations.remove(key);
170                }
171                vanillaDestinations.put(key,set);
172            }
173            
174            validateContainsWildCards(key);
175        }
176        
177        private void removeAllFromVanillaDestinations(ActiveMQDestination key){
178            vanillaDestinations.remove(key);
179            validateContainsWildCards(key);
180        }
181        
182        private Set getFromVanillaDestinations(ActiveMQDestination key){
183            Set answer = (Set)vanillaDestinations.get(key);
184            return answer != null ? answer : nullAnswer;
185        }
186        
187        private void validateContainsWildCards(ActiveMQDestination key){
188            if (containsWildCards && key.isWildcard()){
189                containsWildCards = false;
190                for (Iterator i = vanillaDestinations.keySet().iterator(); i.hasNext();){
191                    ActiveMQDestination dest = (ActiveMQDestination)i.next();
192                    if (dest.isWildcard()){
193                        containsWildCards = true;
194                        break;
195                    }
196                }
197            }
198        }
199    
200    }