/*
* Copyright 2011 Sonian Inc.
*
* 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 com.sonian.elasticsearch.http.jetty.security;
import org.elasticsearch.common.path.PathTrie;
import org.elasticsearch.rest.support.RestUtils;
import java.util.*;
/**
* This implementation is based on org.eclipse.jetty.http.PathMap but it
* is using ElasticSearch path specification instead of servlet spec.
*/
public class RestPathMap<T> extends HashMap<String, T> {
private volatile PathTrie<Entry<String, T>> pathTrie = newPathTrie();
private volatile Entry<String, T> defaultEntry = null;
public RestPathMap() {
super(11);
}
private PathTrie<Entry<String, T>> newPathTrie() {
return new PathTrie<Entry<String, T>>(RestUtils.REST_DECODER);
}
/* --------------------------------------------------------------- */
/**
* Add a single path match to the PathMap.
*
* @param pathSpec The path specification, or comma separated list of
* path specifications.
* @param object The object the path maps to
*/
@Override
public T put(String pathSpec, T object) {
T old = null;
if (!pathSpec.startsWith("/") && !pathSpec.equals("*"))
throw new IllegalArgumentException("PathSpec " + pathSpec + ". must start with '/'");
old = super.put(pathSpec, object);
// Make entry that was just created.
Entry<String, T> entry = new Entry<String, T>(pathSpec, object);
if (pathSpec.equals("*")) {
defaultEntry = entry;
} else {
pathTrie.insert(pathSpec, entry);
}
return old;
}
/* ------------------------------------------------------------ */
/**
* Get object matched by the path.
*
* @param path the path.
* @return Best matched object or null.
*/
public T match(String path) {
Map.Entry<String, T> entry = getMatch(path);
if (entry != null)
return entry.getValue();
return null;
}
/* --------------------------------------------------------------- */
/**
* Get the entry mapped by the best specification.
*
* @param path the path.
* @return Map.Entry of the best matched or null.
*/
public Entry<String, T> getMatch(String path) {
if (path == null)
return null;
Entry<String, T> match = pathTrie.retrieve(path);
if (match != null) {
return match;
}
return defaultEntry;
}
/* --------------------------------------------------------------- */
/**
* Return whether the path matches any entries in the PathMap,
* excluding the default entry
*
* @param path Path to match
* @return Whether the PathMap contains any entries that match this
*/
public boolean containsMatch(String path) {
Entry match = getMatch(path);
return match != null;
}
/* --------------------------------------------------------------- */
@Override
public void clear() {
pathTrie = newPathTrie();
defaultEntry = null;
super.clear();
}
public static class Entry<K, V> implements Map.Entry<K, V> {
private final K key;
private final V value;
private transient String string;
Entry(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public V setValue(V o) {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
if (string == null)
string = key + "=" + value;
return string;
}
}
}