Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.apache.solr.common.util.NamedList Maven / Gradle / Ivy
/*
* 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.
*/
package org.apache.solr.common.util;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.solr.common.SolrException;
/**
* A simple container class for modeling an ordered list of name/value pairs.
*
*
* Unlike Maps:
*
*
* Names may be repeated
* Order of elements is maintained
* Elements may be accessed by numeric index
* Names and Values can both be null
*
*
*
* A NamedList provides fast access by element number, but not by name.
*
*
* When a NamedList is serialized, order is considered more important than access
* by key, so ResponseWriters that output to a format such as JSON will normally
* choose a data structure that allows order to be easily preserved in various
* clients (i.e. not a straight map).
* If access by key is more important for serialization, see {@link SimpleOrderedMap},
* or simply use a regular {@link Map}
*
*
*/
public class NamedList implements Cloneable, Serializable, Iterable> {
private static final long serialVersionUID = 1957981902839867821L;
protected final List nvPairs;
/** Creates an empty instance */
public NamedList() {
nvPairs = new ArrayList<>();
}
public NamedList(int sz) {
nvPairs = new ArrayList<>(sz<<1);
}
/**
* Creates a NamedList instance containing the "name,value" pairs contained in the
* Entry[].
*
*
* Modifying the contents of the Entry[] after calling this constructor may change
* the NamedList (in future versions of Solr), but this is not guaranteed and should
* not be relied upon. To modify the NamedList, refer to {@link #add(String, Object)}
* or {@link #remove(String)}.
*
*
* @param nameValuePairs the name value pairs
*/
public NamedList(Map.Entry[] nameValuePairs) {
nvPairs = nameValueMapToList(nameValuePairs);
}
/**
* Creates a NamedList instance containing the "name,value" pairs contained in the
* Map.
*
*
* Modifying the contents of the Map after calling this constructor may change
* the NamedList (in future versions of Solr), but this is not guaranteed and should
* not be relied upon. To modify the NamedList, refer to {@link #add(String, Object)}
* or {@link #remove(String)}.
*
*
* @param nameValueMap the name value pairs
*/
public NamedList(Map nameValueMap) {
if (null == nameValueMap) {
nvPairs = new ArrayList<>();
} else {
nvPairs = new ArrayList<>(nameValueMap.size());
for (Map.Entry ent : nameValueMap.entrySet()) {
nvPairs.add(ent.getKey());
nvPairs.add(ent.getValue());
}
}
}
/**
* Creates an instance backed by an explicitly specified list of
* pairwise names/values.
*
*
* When using this constructor, runtime type safety is only guaranteed if
* all even numbered elements of the input list are of type "T".
*
*
* @param nameValuePairs underlying List which should be used to implement a NamedList
* @deprecated Use {@link #NamedList(java.util.Map.Entry[])} for the NamedList instantiation
*/
@Deprecated
public NamedList(List nameValuePairs) {
nvPairs=nameValuePairs;
}
/**
* Method to serialize Map.Entry<String, ?> to a List in which the even
* indexed elements (0,2,4. ..etc) are Strings and odd elements (1,3,5,) are of
* the type "T".
*
* @return Modified List as per the above description
* @deprecated This a temporary placeholder method until the guts of the class
* are actually replaced by List<String, ?>.
* @see SOLR-912
*/
@Deprecated
private List nameValueMapToList(Map.Entry[] nameValuePairs) {
List result = new ArrayList<>();
for (Map.Entry ent : nameValuePairs) {
result.add(ent.getKey());
result.add(ent.getValue());
}
return result;
}
/** The total number of name/value pairs */
public int size() {
return nvPairs.size() >> 1;
}
/**
* The name of the pair at the specified List index
*
* @return null if no name exists
*/
public String getName(int idx) {
return (String)nvPairs.get(idx << 1);
}
/**
* The value of the pair at the specified List index
*
* @return may be null
*/
@SuppressWarnings("unchecked")
public T getVal(int idx) {
return (T)nvPairs.get((idx << 1) + 1);
}
/**
* Adds a name/value pair to the end of the list.
*/
public void add(String name, T val) {
nvPairs.add(name);
nvPairs.add(val);
}
/**
* Modifies the name of the pair at the specified index.
*/
public void setName(int idx, String name) {
nvPairs.set(idx<<1, name);
}
/**
* Modifies the value of the pair at the specified index.
*
* @return the value that used to be at index
*/
public T setVal(int idx, T val) {
int index = (idx<<1)+1;
@SuppressWarnings("unchecked")
T old = (T)nvPairs.get( index );
nvPairs.set(index, val);
return old;
}
/**
* Removes the name/value pair at the specified index.
*
* @return the value at the index removed
*/
public T remove(int idx) {
int index = (idx<<1);
nvPairs.remove(index);
@SuppressWarnings("unchecked")
T result = (T)nvPairs.remove(index); // same index, as things shifted in previous remove
return result;
}
/**
* Scans the list sequentially beginning at the specified index and
* returns the index of the first pair with the specified name.
*
* @param name name to look for, may be null
* @param start index to begin searching from
* @return The index of the first matching pair, -1 if no match
*/
public int indexOf(String name, int start) {
int sz = size();
for (int i=start; i
* NOTE: this runs in linear time (it scans starting at the
* beginning of the list until it finds the first pair with
* the specified name).
*
* @return null if not found or if the value stored was null.
* @see #indexOf
* @see #get(String,int)
*
*/
public T get(String name) {
return get(name,0);
}
/**
* Gets the value for the first instance of the specified name
* found starting at the specified index.
*
* NOTE: this runs in linear time (it scans starting at the
* specified position until it finds the first pair with
* the specified name).
*
* @return null if not found or if the value stored was null.
* @see #indexOf
*/
public T get(String name, int start) {
int sz = size();
for (int i=start; i getAll(String name) {
List result = new ArrayList<>();
int sz = size();
for (int i = 0; i < sz; i++) {
String n = getName(i);
if (name==n || (name!=null && name.equals(n))) {
result.add(getVal(i));
}
}
return result;
}
/**
* Removes all values matching the specified name
*
* @param name Name
*/
private void killAll(String name) {
int sz = size();
// Go through the list backwards, removing matches as found.
for (int i = sz - 1; i >= 0; i--) {
String n = getName(i);
if (name==n || (name!=null && name.equals(n))) {
remove(i);
}
}
}
/**
* Recursively parses the NamedList structure to arrive at a specific element.
* As you descend the NamedList tree, the last element can be any type,
* including NamedList, but the previous elements MUST be NamedList objects
* themselves. A null value is returned if the indicated hierarchy doesn't
* exist, but NamedList allows null values so that could be the actual value
* at the end of the path.
*
* This method is particularly useful for parsing the response from Solr's
* /admin/mbeans handler, but it also works for any complex structure.
*
* Explicitly casting the return value is recommended. An even safer option is
* to accept the return value as an object and then check its type.
*
* Usage examples:
*
* String coreName = (String) response.findRecursive
* ("solr-mbeans", "CORE", "core", "stats", "coreName");
* long numDoc = (long) response.findRecursive
* ("solr-mbeans", "CORE", "searcher", "stats", "numDocs");
*
* @param args
* One or more strings specifying the tree to navigate.
* @return the last entry in the given path hierarchy, null if not found.
*/
public Object findRecursive(String... args) {
NamedList> currentList = null;
Object value = null;
for (int i = 0; i < args.length; i++) {
String key = args[i];
/*
* The first time through the loop, the current list is null, so we assign
* it to this list. Then we retrieve the first key from this list and
* assign it to value.
*
* On the next loop, we check whether the retrieved value is a NamedList.
* If it is, then we drop down to that NamedList, grab the value of the
* next key, and start the loop over. If it is not a NamedList, then we
* assign the value to null and break out of the loop.
*
* Assigning the value to null and then breaking out of the loop seems
* like the wrong thing to do, but there's a very simple reason that it
* works: If we have reached the last key, then the loop ends naturally
* after we retrieve the value, and that code is never executed.
*/
if (currentList == null) {
currentList = this;
} else {
if (value instanceof NamedList) {
currentList = (NamedList>) value;
} else {
value = null;
break;
}
}
/*
* We do not need to do a null check on currentList for the following
* assignment. The instanceof check above will fail if the current list is
* null, and if that happens, the loop will end before this point.
*/
value = currentList.get(key, 0);
}
return value;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('{');
int sz = size();
for (int i=0; i( Collections.unmodifiableList(copy.nvPairs));
}
public Map asMap(int maxDepth) {
LinkedHashMap result = new LinkedHashMap();
for(int i=0;i 0) {
//the maxDepth check is to avoid stack overflow due to infinite recursion
val = ((NamedList) val).asMap(maxDepth-1);
}
Object old = result.put(getName(i), val);
if(old!=null){
if (old instanceof List) {
List list = (List) old;
list.add(val);
result.put(getName(i),old);
} else {
ArrayList l = new ArrayList();
l.add(old);
l.add(val);
result.put(getName(i), l);
}
}
}
return result;
}
/**
*
* Helper class implementing Map.Entry<String, T> to store the key-value
* relationship in NamedList (the keys of which are String-s)
*/
public static final class NamedListEntry implements Map.Entry {
public NamedListEntry() {
}
public NamedListEntry(String _key, T _value) {
key = _key;
value = _value;
}
@Override
public String getKey() {
return key;
}
@Override
public T getValue() {
return value;
}
@Override
public T setValue(T _value) {
T oldValue = value;
value = _value;
return oldValue;
}
private String key;
private T value;
}
/**
* Iterates over the Map and sequentially adds its key/value pairs
*/
public boolean addAll(Map args) {
for (Map.Entry entry : args.entrySet() ) {
add(entry.getKey(), entry.getValue());
}
return args.size()>0;
}
/** Appends the elements of the given NamedList to this one. */
public boolean addAll(NamedList nl) {
nvPairs.addAll(nl.nvPairs);
return nl.size()>0;
}
/**
* Makes a shallow copy of the named list.
*/
@Override
public NamedList clone() {
ArrayList newList = new ArrayList<>(nvPairs.size());
newList.addAll(nvPairs);
return new NamedList<>(newList);
}
//----------------------------------------------------------------------------
// Iterable interface
//----------------------------------------------------------------------------
/**
* Support the Iterable interface
*/
@Override
public Iterator> iterator() {
final NamedList list = this;
Iterator> iter = new Iterator>() {
int idx = 0;
@Override
public boolean hasNext() {
return idx < list.size();
}
@Override
public Map.Entry next() {
final int index = idx++;
Map.Entry nv = new Map.Entry() {
@Override
public String getKey() {
return list.getName( index );
}
@Override
public T getValue() {
return list.getVal( index );
}
@Override
public String toString() {
return getKey()+"="+getValue();
}
@Override
public T setValue(T value) {
return list.setVal(index, value);
}
};
return nv;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
return iter;
}
/**
* NOTE: this runs in linear time (it scans starting at the
* beginning of the list until it finds the first pair with
* the specified name).
*/
public T remove(String name) {
int idx = indexOf(name, 0);
if(idx != -1) return remove(idx);
return null;
}
/**
* Removes and returns all values for the specified name. Returns null if
* no matches found. This method will return all matching objects,
* regardless of data type. If you are parsing Solr config options, the
* {@link #removeConfigArgs(String)} or {@link #removeBooleanArg(String)}
* methods will probably work better.
*
* @param name Name
* @return List of values
*/
public List removeAll(String name) {
List result = new ArrayList<>();
result = getAll(name);
if (result.size() > 0 ) {
killAll(name);
return result;
}
return null;
}
/**
* Used for getting a boolean argument from a NamedList object. If the name
* is not present, returns null. If there is more than one value with that
* name, or if the value found is not a Boolean or a String, throws an
* exception. If there is only one value present and it is a Boolean or a
* String, the value is removed and returned as a Boolean. If an exception
* is thrown, the NamedList is not modified. See {@link #removeAll(String)}
* and {@link #removeConfigArgs(String)} for additional ways of gathering
* configuration information from a NamedList.
*
* @param name
* The key to look up in the NamedList.
* @return The boolean value found.
* @throws SolrException
* If multiple values are found for the name or the value found is
* not a Boolean or a String.
*/
public Boolean removeBooleanArg(final String name) {
Boolean bool = getBooleanArg(name);
if (null != bool) {
remove(name);
}
return bool;
}
/**
* Used for getting a boolean argument from a NamedList object. If the name
* is not present, returns null. If there is more than one value with that
* name, or if the value found is not a Boolean or a String, throws an
* exception. If there is only one value present and it is a Boolean or a
* String, the value is returned as a Boolean. The NamedList is not
* modified. See {@link #remove(String)}, {@link #removeAll(String)}
* and {@link #removeConfigArgs(String)} for additional ways of gathering
* configuration information from a NamedList.
*
* @param name The key to look up in the NamedList.
* @return The boolean value found.
* @throws SolrException
* If multiple values are found for the name or the value found is
* not a Boolean or a String.
*/
public Boolean getBooleanArg(final String name) {
Boolean bool;
List values = getAll(name);
if (0 == values.size()) {
return null;
}
if (values.size() > 1) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
"Only one '" + name + "' is allowed");
}
Object o = get(name);
if (o instanceof Boolean) {
bool = (Boolean)o;
} else if (o instanceof CharSequence) {
bool = Boolean.parseBoolean(o.toString());
} else {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
"'" + name + "' must have type Boolean or CharSequence; found " + o.getClass());
}
return bool;
}
/**
* Used for getting one or many arguments from NamedList objects that hold
* configuration parameters. Finds all entries in the NamedList that match
* the given name. If they are all strings or arrays of strings, remove them
* from the NamedList and return the individual elements as a {@link Collection}.
* Parameter order will be preserved if the returned collection is handled as
* an {@link ArrayList}. Throws SolrException if any of the values associated
* with the name are not strings or arrays of strings. If exception is
* thrown, the NamedList is not modified. Returns an empty collection if no
* matches found. If you need to remove and retrieve all matching items from
* the NamedList regardless of data type, use {@link #removeAll(String)} instead.
* The {@link #removeBooleanArg(String)} method can be used for retrieving a
* boolean argument.
*
* @param name
* The key to look up in the NamedList.
* @return A collection of the values found.
* @throws SolrException
* If values are found for the input key that are not strings or
* arrays of strings.
*/
@SuppressWarnings("rawtypes")
public Collection removeConfigArgs(final String name)
throws SolrException {
List objects = getAll(name);
List collection = new ArrayList<>(size() / 2);
final String err = "init arg '" + name + "' must be a string "
+ "(ie: 'str'), or an array (ie: 'arr') containing strings; found: ";
for (Object o : objects) {
if (o instanceof String) {
collection.add((String) o);
continue;
}
// If it's an array, convert to List (which is a Collection).
if (o instanceof Object[]) {
o = Arrays.asList((Object[]) o);
}
// If it's a Collection, collect each value.
if (o instanceof Collection) {
for (Object item : (Collection) o) {
if (!(item instanceof String)) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, err + item.getClass());
}
collection.add((String) item);
}
continue;
}
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, err + o.getClass());
}
if (collection.size() > 0) {
killAll(name);
}
return collection;
}
public void clear() {
nvPairs.clear();
}
@Override
public int hashCode() {
return nvPairs.hashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof NamedList)) return false;
NamedList> nl = (NamedList>) obj;
return this.nvPairs.equals(nl.nvPairs);
}
}