blob: 9da93d483a361ae800ea05f4edb6a9c81a073214 [file] [log] [blame]
package org.apache.qpid.server.exchange.headers;
import org.apache.qpid.framing.AMQTypedValue;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.server.exchange.topic.TopicMatcherDFAState;
import org.apache.qpid.server.exchange.topic.TopicWord;
import org.apache.qpid.server.exchange.topic.TopicMatcherResult;
import java.util.*;
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*
*/
public class HeadersMatcherDFAState
{
private final Collection<HeaderMatcherResult> _results;
private final Map<HeaderKey, Map<AMQTypedValue,HeadersMatcherDFAState>> _nextStateMap;
private final HeaderKeyDictionary _dictionary;
public HeadersMatcherDFAState(Map<HeaderKey, Map<AMQTypedValue,HeadersMatcherDFAState>> nextStateMap,
Collection<HeaderMatcherResult> results,
HeaderKeyDictionary dictionary)
{
_nextStateMap = nextStateMap;
_results = results;
_dictionary = dictionary;
}
public Collection<HeaderMatcherResult> match(final FieldTable table)
{
return match(table.iterator());
}
public Collection<HeaderMatcherResult> match(Iterator<Map.Entry<AMQShortString,AMQTypedValue>> fieldTableIterator)
{
if(_nextStateMap.isEmpty())
{
return _results;
}
while(fieldTableIterator.hasNext())
{
Map.Entry<AMQShortString, AMQTypedValue> fieldTableEntry = fieldTableIterator.next();
HeaderKey key = _dictionary.get(fieldTableEntry.getKey());
if(key != HeaderKey.UNKNOWN)
{
Map<AMQTypedValue, HeadersMatcherDFAState> valueToStateMap = _nextStateMap.get(key);
if(valueToStateMap != null)
{
HeadersMatcherDFAState nextState = valueToStateMap.get(fieldTableEntry.getValue());
if(nextState == null)
{
nextState = valueToStateMap.get(null);
}
if(nextState != null && nextState != this)
{
return nextState.match(fieldTableIterator);
}
}
}
}
return _results;
}
HeadersMatcherDFAState mergeStateMachines(HeadersMatcherDFAState otherStateMachine)
{
assert(otherStateMachine._dictionary == _dictionary);
Map<Set<HeadersMatcherDFAState>, HeadersMatcherDFAState> newStateMap= new HashMap<Set<HeadersMatcherDFAState>, HeadersMatcherDFAState>();
Collection<HeaderMatcherResult> results;
if(_results.isEmpty())
{
results = otherStateMachine._results;
}
else if(otherStateMachine._results.isEmpty())
{
results = _results;
}
else
{
results = new HashSet<HeaderMatcherResult>(_results);
results.addAll(otherStateMachine._results);
}
final Map<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> newNextStateMap = new HashMap<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>>();
HeadersMatcherDFAState newState = new HeadersMatcherDFAState(newNextStateMap, results, _dictionary);
Set<HeadersMatcherDFAState> oldStates = new HashSet<HeadersMatcherDFAState>();
oldStates.add(this);
oldStates.add(otherStateMachine);
newStateMap.put(oldStates, newState);
mergeStateMachines(oldStates, newNextStateMap, newStateMap);
return newState;
}
private void mergeStateMachines(final Set<HeadersMatcherDFAState> oldStates,
final Map<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> newNextStateMap,
final Map<Set<HeadersMatcherDFAState>, HeadersMatcherDFAState> newStateMap)
{
Map<HeaderKey, Map<AMQTypedValue, Set<HeadersMatcherDFAState>>> nfaMap = new HashMap<HeaderKey, Map<AMQTypedValue, Set<HeadersMatcherDFAState>>>();
Set<HeaderKey> distinctKeys = new HashSet<HeaderKey>();
for(HeadersMatcherDFAState state : oldStates)
{
Map<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> map = state._nextStateMap;
for(Map.Entry<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> entry : map.entrySet())
{
Map<AMQTypedValue, Set<HeadersMatcherDFAState>> valueToStatesMap = nfaMap.get(entry.getKey());
if(valueToStatesMap == null)
{
valueToStatesMap = new HashMap<AMQTypedValue, Set<HeadersMatcherDFAState>>();
nfaMap.put(entry.getKey(), valueToStatesMap);
}
for(Map.Entry<AMQTypedValue, HeadersMatcherDFAState> valueToStateEntry : entry.getValue().entrySet())
{
Set<HeadersMatcherDFAState> states = valueToStatesMap.get(valueToStateEntry.getKey());
if(states == null)
{
states = new HashSet<HeadersMatcherDFAState>();
valueToStatesMap.put(valueToStateEntry.getKey(),states);
}
states.add(valueToStateEntry.getValue());
}
distinctKeys.add(entry.getKey());
}
}
Map<HeaderKey, Set<HeadersMatcherDFAState>> anyValueStates = new HashMap<HeaderKey, Set<HeadersMatcherDFAState>>();
for(HeaderKey distinctKey : distinctKeys)
{
Map<AMQTypedValue, Set<HeadersMatcherDFAState>> valueToStateMap = nfaMap.get(distinctKey);
if(valueToStateMap != null)
{
Set<HeadersMatcherDFAState> statesForKeyDefault = valueToStateMap.get(null);
if(statesForKeyDefault != null)
{
anyValueStates.put(distinctKey, statesForKeyDefault);
}
}
}
// add the defaults for "null" to all other specified values of a given header key
for( Map.Entry<HeaderKey,Map<AMQTypedValue,Set<HeadersMatcherDFAState>>> entry : nfaMap.entrySet())
{
Map<AMQTypedValue, Set<HeadersMatcherDFAState>> valueToStatesMap = entry.getValue();
for(Map.Entry<AMQTypedValue, Set<HeadersMatcherDFAState>> valueToStates : valueToStatesMap.entrySet())
{
if(valueToStates.getKey() != null)
{
Set<HeadersMatcherDFAState> defaults = anyValueStates.get(entry.getKey());
if(defaults != null)
{
valueToStates.getValue().addAll(defaults);
}
}
}
}
// if a given header key is not mentioned in the map of a machine; then that machine would stay at the same state
// for that key.
for(HeaderKey distinctKey : distinctKeys)
{
Map<AMQTypedValue, Set<HeadersMatcherDFAState>> valueToStatesMap = nfaMap.get(distinctKey);
for(HeadersMatcherDFAState oldState : oldStates)
{
if(!oldState._nextStateMap.containsKey(distinctKey))
{
for(Set<HeadersMatcherDFAState> endStates : valueToStatesMap.values())
{
endStates.add(oldState);
}
}
}
}
for(Map.Entry<HeaderKey,Map<AMQTypedValue,Set<HeadersMatcherDFAState>>> transitionClass : nfaMap.entrySet())
{
Map<AMQTypedValue, HeadersMatcherDFAState> valueToDFAState = newNextStateMap.get(transitionClass.getKey());
if(valueToDFAState == null)
{
valueToDFAState = new HashMap<AMQTypedValue, HeadersMatcherDFAState>();
newNextStateMap.put(transitionClass.getKey(), valueToDFAState);
}
for(Map.Entry<AMQTypedValue,Set<HeadersMatcherDFAState>> transition : transitionClass.getValue().entrySet())
{
Set<HeadersMatcherDFAState> destinations = transition.getValue();
HeadersMatcherDFAState nextState = newStateMap.get(destinations);
if(nextState == null)
{
if(destinations.size() == 1)
{
nextState = destinations.iterator().next();
newStateMap.put(destinations, nextState);
}
else
{
Collection<HeaderMatcherResult> results;
Set<Collection<HeaderMatcherResult>> resultSets = new HashSet<Collection<HeaderMatcherResult>>();
for(HeadersMatcherDFAState destination : destinations)
{
resultSets.add(destination._results);
}
resultSets.remove(Collections.EMPTY_SET);
if(resultSets.size() == 0)
{
results = Collections.EMPTY_SET;
}
else if(resultSets.size() == 1)
{
results = resultSets.iterator().next();
}
else
{
results = new HashSet<HeaderMatcherResult>();
for(Collection<HeaderMatcherResult> oldResult : resultSets)
{
results.addAll(oldResult);
}
}
final Map<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> nextStateMap = new HashMap<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>>();
nextState = new HeadersMatcherDFAState(nextStateMap, results, _dictionary);
newStateMap.put(destinations, nextState);
mergeStateMachines(
destinations,
nextStateMap,
newStateMap);
}
}
valueToDFAState.put(transition.getKey(),nextState);
}
}
final ArrayList<HeaderKey> removeKeyList = new ArrayList<HeaderKey>();
for(Map.Entry<HeaderKey,Map<AMQTypedValue,HeadersMatcherDFAState>> entry : _nextStateMap.entrySet())
{
final ArrayList<AMQTypedValue> removeValueList = new ArrayList<AMQTypedValue>();
for(Map.Entry<AMQTypedValue,HeadersMatcherDFAState> valueToDFAState : entry.getValue().entrySet())
{
if(valueToDFAState.getValue() == this)
{
HeadersMatcherDFAState defaultState = entry.getValue().get(null);
if(defaultState == null || defaultState == this)
{
removeValueList.add(valueToDFAState.getKey());
}
}
}
for(AMQTypedValue removeValue : removeValueList)
{
entry.getValue().remove(removeValue);
}
if(entry.getValue().isEmpty())
{
removeKeyList.add(entry.getKey());
}
}
for(HeaderKey removeKey : removeKeyList)
{
_nextStateMap.remove(removeKey);
}
}
}