/**
 * 
 * Copyright 2004 Protique Ltd
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 * 
 **/
package org.codehaus.activemq.filter;

import org.codehaus.activemq.message.ActiveMQDestination;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * A Map-like data structure allowing values to be indexed by {@link ActiveMQDestination}
 * and retrieved by destination - supporting both * and &gt; style of wildcard
 * as well as composite destinations.
 * <br>
 * This class assumes that the index changes rarely but that fast lookup into the index is required.
 * So this class maintains a pre-calculated index for destination steps. So looking up the values
 * for "TEST.*" or "*.TEST" will be pretty fast.
 * <br>
 * Looking up of a value could return a single value or a List of matching values if a wildcard or
 * composite destination is used.
 *
 * @version $Revision: 1.1 $
 */
public class DestinationMap {
    private DestinationMapNode rootNode = new DestinationMapNode();
    protected static final String ANY_DESCENDENT = DestinationFilter.ANY_DESCENDENT;
    protected static final String ANY_CHILD = DestinationFilter.ANY_CHILD;

    /**
     * Looks up the value(s) matching the given Destination key. For simple destinations
     * this is typically a List of one single value, for wildcards or composite destinations this will typically be
     * a List of matching values.
     *
     * @param key the destination to lookup
     * @return a List of matching values or an empty list if there are no matching values.
     */
    public synchronized Set get(ActiveMQDestination key) {
        if (key.isComposite()) {
            List childDestinations = key.getChildDestinations();
            Set answer = new HashSet(childDestinations.size());
            for (Iterator iter = childDestinations.iterator(); iter.hasNext();) {
                ActiveMQDestination childDestination = (ActiveMQDestination) iter.next();
                Object value = get(childDestination);
                if (value instanceof Set) {
                    answer.addAll((Set) value);
                }
                else if (value != null) {
                    answer.add(value);
                }
                return answer;
            }
        }
        return findWildcardMatches(key);
    }

    public synchronized void put(ActiveMQDestination key, Object value) {
        if (key.isComposite()) {
            List childDestinations = key.getChildDestinations();
            for (Iterator iter = childDestinations.iterator(); iter.hasNext();) {
                ActiveMQDestination childDestination = (ActiveMQDestination) iter.next();
                put(childDestination, value);
            }
            return;
        }
        String[] paths = key.getDestinationPaths();
        rootNode.add(paths, 0, value);
    }

    /**
     * Removes the value from the associated destination
     */
    public synchronized void remove(ActiveMQDestination key, Object value) {
        if (key.isComposite()) {
            List childDestinations = key.getChildDestinations();
            for (Iterator iter = childDestinations.iterator(); iter.hasNext();) {
                ActiveMQDestination childDestination = (ActiveMQDestination) iter.next();
                remove(childDestination, value);
            }
            return;
        }
        String[] paths = key.getDestinationPaths();
        rootNode.remove(paths, 0, value);

    }

    // Implementation methods
    //-------------------------------------------------------------------------
    protected Set findWildcardMatches(ActiveMQDestination key) {
        String[] paths = key.getDestinationPaths();
        Set answer = new HashSet();
        rootNode.appendMatchingValues(answer, paths, 0);
        return answer;
    }

}
