001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. 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 */ 017package org.apache.activemq.filter; 018 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026 027/** 028 * An implementation class used to implement {@link DestinationMap} 029 * 030 * 031 */ 032public class DestinationMapNode implements DestinationNode { 033 protected static final String ANY_CHILD = DestinationMap.ANY_CHILD; 034 protected static final String ANY_DESCENDENT = DestinationMap.ANY_DESCENDENT; 035 036 // we synchronize at the DestinationMap level 037 private DestinationMapNode parent; 038 private List<Object> values = new ArrayList<Object>(); 039 private Map<String, DestinationNode> childNodes = new HashMap<String, DestinationNode>(); 040 private String path = "Root"; 041 // private DestinationMapNode anyChild; 042 private int pathLength; 043 044 public DestinationMapNode(DestinationMapNode parent) { 045 this.parent = parent; 046 if (parent == null) { 047 pathLength = 0; 048 } else { 049 pathLength = parent.pathLength + 1; 050 } 051 } 052 053 /** 054 * Returns the child node for the given named path or null if it does not 055 * exist 056 */ 057 public DestinationNode getChild(String path) { 058 return childNodes.get(path); 059 } 060 061 /** 062 * Returns the child nodes 063 */ 064 public Collection<DestinationNode> getChildren() { 065 return childNodes.values(); 066 } 067 068 public int getChildCount() { 069 return childNodes.size(); 070 } 071 072 /** 073 * Returns the child node for the given named path, lazily creating one if 074 * it does not yet exist 075 */ 076 public DestinationMapNode getChildOrCreate(String path) { 077 DestinationMapNode answer = (DestinationMapNode)childNodes.get(path); 078 if (answer == null) { 079 answer = createChildNode(); 080 answer.path = path; 081 childNodes.put(path, answer); 082 } 083 return answer; 084 } 085 086 /** 087 * Returns a mutable List of the values available at this node in the tree 088 */ 089 @SuppressWarnings({ "rawtypes", "unchecked" }) 090 public List getValues() { 091 return values; 092 } 093 094 /** 095 * Removes values available at this node in the tree 096 */ 097 @SuppressWarnings({ "rawtypes", "unchecked" }) 098 public List removeValues() { 099 ArrayList v = new ArrayList(values); 100 // parent.getAnyChildNode().getValues().removeAll(v); 101 values.clear(); 102 pruneIfEmpty(); 103 return v; 104 } 105 106 @SuppressWarnings({ "rawtypes", "unchecked" }) 107 public Set removeDesendentValues() { 108 Set answer = new HashSet(); 109 removeDesendentValues(answer); 110 return answer; 111 } 112 113 @SuppressWarnings({ "rawtypes", "unchecked" }) 114 protected void removeDesendentValues(Set answer) { 115 for (Map.Entry<String, DestinationNode> child : childNodes.entrySet()) { 116 // remove all the values from the child 117 answer.addAll(child.getValue().removeValues()); 118 answer.addAll(child.getValue().removeDesendentValues()); 119 } 120 } 121 122 /** 123 * Returns a list of all the values from this node down the tree 124 */ 125 @SuppressWarnings({ "rawtypes", "unchecked" }) 126 public Set getDesendentValues() { 127 Set answer = new HashSet(); 128 appendDescendantValues(answer); 129 return answer; 130 } 131 132 public void add(String[] paths, int idx, Object value) { 133 if (idx >= paths.length) { 134 values.add(value); 135 } else { 136 getChildOrCreate(paths[idx]).add(paths, idx + 1, value); 137 } 138 } 139 140 public void set(String[] paths, int idx, Object value) { 141 if (idx >= paths.length) { 142 values.clear(); 143 values.add(value); 144 } else { 145 getChildOrCreate(paths[idx]).set(paths, idx + 1, value); 146 } 147 } 148 149 public void remove(String[] paths, int idx, Object value) { 150 if (idx >= paths.length) { 151 values.remove(value); 152 pruneIfEmpty(); 153 } else { 154 getChildOrCreate(paths[idx]).remove(paths, ++idx, value); 155 } 156 } 157 158 public void removeAll(Set<DestinationNode> answer, String[] paths, int startIndex) { 159 DestinationNode node = this; 160 int size = paths.length; 161 for (int i = startIndex; i < size && node != null; i++) { 162 163 String path = paths[i]; 164 if (path.equals(ANY_DESCENDENT)) { 165 answer.addAll(node.removeDesendentValues()); 166 break; 167 } 168 169 // TODO is this correct, we are appending wildcard values here??? 170 node.appendMatchingWildcards(answer, paths, i); 171 if (path.equals(ANY_CHILD)) { 172 // node = node.getAnyChildNode(); 173 node = new AnyChildDestinationNode(node); 174 } else { 175 node = node.getChild(path); 176 } 177 } 178 179 if (node != null) { 180 answer.addAll(node.removeValues()); 181 } 182 183 } 184 185 @SuppressWarnings({ "rawtypes", "unchecked" }) 186 public void appendDescendantValues(Set answer) { 187 // add children values, then recursively add their children 188 for(DestinationNode child : childNodes.values()) { 189 answer.addAll(child.getValues()); 190 child.appendDescendantValues(answer); 191 } 192 } 193 194 /** 195 * Factory method to create a child node 196 */ 197 protected DestinationMapNode createChildNode() { 198 return new DestinationMapNode(this); 199 } 200 201 /** 202 * Matches any entries in the map containing wildcards 203 */ 204 @SuppressWarnings({ "rawtypes", "unchecked" }) 205 public void appendMatchingWildcards(Set answer, String[] paths, int idx) { 206 if (idx - 1 > pathLength) { 207 return; 208 } 209 DestinationNode wildCardNode = getChild(ANY_CHILD); 210 if (wildCardNode != null) { 211 wildCardNode.appendMatchingValues(answer, paths, idx + 1); 212 } 213 wildCardNode = getChild(ANY_DESCENDENT); 214 if (wildCardNode != null) { 215 // for a wildcard Node match, add all values of the descendant node 216 answer.addAll(wildCardNode.getValues()); 217 // and all descendants for paths like ">.>" 218 answer.addAll(wildCardNode.getDesendentValues()); 219 } 220 } 221 222 public void appendMatchingValues(Set<DestinationNode> answer, String[] paths, int startIndex) { 223 DestinationNode node = this; 224 boolean couldMatchAny = true; 225 int size = paths.length; 226 for (int i = startIndex; i < size && node != null; i++) { 227 String path = paths[i]; 228 if (path.equals(ANY_DESCENDENT)) { 229 answer.addAll(node.getDesendentValues()); 230 couldMatchAny = false; 231 break; 232 } 233 234 node.appendMatchingWildcards(answer, paths, i); 235 236 if (path.equals(ANY_CHILD)) { 237 node = new AnyChildDestinationNode(node); 238 } else { 239 node = node.getChild(path); 240 } 241 } 242 if (node != null) { 243 answer.addAll(node.getValues()); 244 if (couldMatchAny) { 245 // lets allow FOO.BAR to match the FOO.BAR.> entry in the map 246 DestinationNode child = node.getChild(ANY_DESCENDENT); 247 if (child != null) { 248 answer.addAll(child.getValues()); 249 } 250 } 251 } 252 } 253 254 public String getPath() { 255 return path; 256 } 257 258 public boolean isEmpty(){ 259 return childNodes.isEmpty(); 260 } 261 262 protected void pruneIfEmpty() { 263 if (parent != null && childNodes.isEmpty() && values.isEmpty()) { 264 parent.removeChild(this); 265 } 266 } 267 268 protected void removeChild(DestinationMapNode node) { 269 childNodes.remove(node.getPath()); 270 pruneIfEmpty(); 271 } 272}