首页 > 代码库 > solrj:org.apache.solr.common.util.NamedList.java

solrj:org.apache.solr.common.util.NamedList.java

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.Iterator;import java.util.List;import java.util.Map;import org.apache.solr.common.SolrException;/** * 一个简单的容器类  用来模拟一个有序的 name/value 键值对列表 * <p> *  不同于 Maps: * </p> * <ul> *  <li>Names可以重复</li> *  <li>元素具有顺序性</li> *  <li>元素可以通过数值索引来访问</li> *  <li>Names和 Values可以都为 null</li> * </ul> * * <p> * A NamedList provides fast access by element number, but not by name. * </p> * <p> * NamedList被序列化后,元素的顺序就比较重要了.所以ResponseWriters输出格式如JSON通常会选择一个容易在不同客户端保存并持有顺序的数据结构. *  * 如果通过key访问比序列化更重要是,参考{@link SimpleOrderedMap},或者简单使用常规的{@link Map} * </p> * */public class NamedList<T> implements Cloneable, Serializable, Iterable<Map.Entry<String,T>> {  private static final long serialVersionUID = 1957981902839867821L;  protected final List<Object> nvPairs;  /** Creates an empty instance */  public NamedList() {    nvPairs = new ArrayList<Object>();  }  /**   *创建一个实例,支持Map.Entry<String, ? extends T>[]类型    *   * <p>   * 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)}.   * </p>   *   * @param nameValuePairs the name value pairs   */  public NamedList(Map.Entry<String, ? extends T>[] nameValuePairs) {    nvPairs = nameValueMapToList(nameValuePairs);  }  /**   *创建一个实例,支持明确的name/value配对键值.   * <p>   * When using this constructor, runtime type safety is only guaranteed if   * all even numbered elements of the input list are of type "T".   * </p>   *   * @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<Object> nameValuePairs) {    nvPairs=nameValuePairs;  }  /**   *    * 序列化Map.Entry&lt;String,?&gt; 为一个List.这个List中索引为(0,2,4. ..etc)的是String,奇数元素(1,3,5...etc)为"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&lt;String, ?&gt;.   * @see <a href="http://www.mamicode.com/https://issues.apache.org/jira/browse/SOLR-912">SOLR-912</a>   */  @Deprecated  private List<Object> nameValueMapToList(Map.Entry<String, ? extends T>[] nameValuePairs) {    List<Object> result = new ArrayList<Object>();    for (Map.Entry<String, ?> 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);  }  /**   * 在list的末端添加 name/value键值对.   */  public void add(String name, T val) {    nvPairs.add(name);    nvPairs.add(val);  }  /**   * 修改指定索引处的键值对的name值.   */  public void setName(int idx, String name) {    nvPairs.set(idx<<1, name);  }  /**   *修改指定索引处的键值对的value值.   *   * @return 老的对于索引的value值   */  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;  }  /**   *删除指定索引处的键值对的name/value值.   *   * @return 删除的键值对的value值   */  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;  }  /**   * 扫描指定索引处开始的List列表,并返回第一处name为指定名字的键值对的索引   *   * @param name 查询的name,可能为null   * @param start 搜索查询起始索引   * @return 第一处匹配键值的索引,如果不匹配,返回-1   */  public int indexOf(String name, int start) {    int sz = size();    for (int i=start; i<sz; i++) {      String n = getName(i);      if (name==null) {        if (n==null) return i; // matched null      } else if (name.equals(n)) {        return i;      }    }    return -1;  }  /**   * 返回第一个name为指定值的实例的value值.   *    * <p>   * 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.   * <p>   * 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<sz; i++) {      String n = getName(i);      if (name==null) {        if (n==null) return getVal(i);      } else if (name.equals(n)) {        return getVal(i);      }    }    return null;  }  /**   * Gets the values for the the specified name   *   * @param name Name   * @return List of values   */  public List<T> getAll(String name) {    List<T> result = new ArrayList<T>();    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);      }    }  }    /**   * 递归解析NameList结构到一个指定的元素中.随着NameList树的解析,最后一个元素可以是任何类型,包括NameList,   * 但前面所有元素必须NamedList对象本身.如果指定的层次结构不存在,那么返回为null.NameList是允许null值的,   * 所以最后返回的值也可以是null;   *    * 这个方法对解析solr响应的/admin/mbeans 句柄特别有用,当然也同样用于其他复杂结构的工作处理.   *    * 推荐明确抛出返回值,一个比较安全的选择及时接受Object对象的返回值,然后去确认它的类型.   *    * 使用示例:   *    * 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];      /*       * 第一次循环,currentList为null,所以我们把 NameList这个对象分配给currentList.       * 然后我们检索这个列表的第一个key,然后把key对应的对象值 赋值给value 变量.       *        * 第二次循环遍历时,首先确认上一次我们获得的value是否是一个NameList.       * 如果是NameList对象,那么将该对象赋值给currentList,抓取下一个key对应的value值,并开始遍历.       * 如果不是一个NameList对象,重置value值为null,中断遍历.       *        * 赋值value为null,然后结束循环遍历,看起来做的不正确,但是有一个非常简单的原因,       * 它的工作原理:如果循环到最后一个key,在检索到对应的value值时会自然结束循环遍历,并且这段代码永远不会执行的.       *        */      if (currentList == null) {        currentList = this;      } else {        if (value instanceof NamedList) {          currentList = (NamedList<?>) value;        } else {          value = null;          break;        }      }      /*       * 这里不再需要验证currentList是否为null.如果当前list为null的话,上面代码value instanceof NamedList       * 的检查会失败的.如果这种情况发生的话,循环就会在这之前结束.       *        */      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<sz; i++) {      if (i != 0) sb.append(‘,‘);      sb.append(getName(i));      sb.append(‘=‘);      sb.append(getVal(i));    }    sb.append(‘}‘);    return sb.toString();  }  /**   *    * 帮助类实现 Map.Entry&lt;String, T&gt;用来存储 NamedList中的key-value关系.   */  public static final class NamedListEntry<T> implements Map.Entry<String,T> {        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;  }  /**   * 遍历Map,依次增加了它的键/值对   *    */  public boolean addAll(Map<String,T> args) {    for (Map.Entry<String, T> entry : args.entrySet() ) {      add(entry.getKey(), entry.getValue());    }    return args.size()>0;  }  /**将给定的NameList的元素添加到当前NameList对象中 */  public boolean addAll(NamedList<T> nl) {    nvPairs.addAll(nl.nvPairs);    return nl.size()>0;  }  /**   * 生成一个<i>浅拷贝</i>的NameList.   */  @Override  public NamedList<T> clone() {    ArrayList<Object> newList = new ArrayList<Object>(nvPairs.size());    newList.addAll(nvPairs);    return new NamedList<T>(newList);  }  //----------------------------------------------------------------------------  // Iterable 接口  //----------------------------------------------------------------------------  /**   * 支持Iterable接口   */  @Override  public Iterator<Map.Entry<String,T>> iterator() {    final NamedList<T> list = this;    Iterator<Map.Entry<String,T>> iter = new Iterator<Map.Entry<String,T>>() {      int idx = 0;      @Override      public boolean hasNext() {        return idx < list.size();      }      @Override      public Map.Entry<String,T> next() {        final int index = idx++;        Map.Entry<String,T> nv = new Map.Entry<String,T>() {          @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: 线性时间执行 (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;  }  /**   * 删除并返回指定name的所有values.如果没有匹配,返回null.这个方法返回所有匹配对象,不考虑数据类型.   * 如果解析solr配置选项,{@link #removeConfigArgs(String)} 或者 {@link #removeBooleanArg(String)}   * 方法是一个更好的选择.   *   * @param name Name   * @return List of values   */  public List<T> removeAll(String name) {    List<T> result = new ArrayList<T>();    result = getAll(name);    if (result.size() > 0 ) {      killAll(name);      return result;    }    return null;  }  /**   * 用来从NameList对象中获取一个boolean参数.如果name不存在,返回null.如果对应name有多个value,   * 或者这个值不是Boolean或者String类型,会抛出一个异常.如果只有一个值存在,并且是 一个Boolean或者String类型   * 这个value值就会删除并返回一个Boolean值.如果抛出异常,NamedList将不会改变.   * 参考 {@link #removeAll(String)}和 {@link #removeConfigArgs(String)}的更多方式:从NamedList收集的配置信息   *    * @param name NameList中要查询的key值.   *             * @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;    List<T> 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 ‘bool‘ or ‘str‘; found " + o.getClass());    }    remove(name);    return bool;  }    /**   * 从NamedList对象中获取一个或者多个参数用来保存配置参数.找到所有匹配给定name的entries.   * 如果都是字符串或者字符串数组,从NameList中删除它们,并返回{@link Collection}.   *    * 如果返回的集合是一个{@link ArrayList},那么参数顺序将被保存.如果关联name的value值不是字符串或者是字符串数组,   * 那么抛出一个SolrException异常.异常抛出,NameList不会更改.如果没有匹配的值,返回一个空的集合.如果需要删除,   * 并在检索到所有匹配的条目时不考虑数据类型,那么使用 {@link #removeAll(String)} 替代.   * {@link #removeBooleanArg(String)} 方法用来检索一个boolean参数.   *    * @param name  NameList中要查询的key值   * @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<String> removeConfigArgs(final String name)      throws SolrException {    List<T> objects = getAll(name);    List<String> collection = new ArrayList<String>(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);  }}

测试类:

package org.apache.solr.common.util;import java.util.ArrayList;import java.util.List;import org.apache.lucene.util.LuceneTestCase;import org.apache.solr.common.SolrException;public class NamedListTest extends LuceneTestCase {  public void testRemove() {    NamedList<String> nl = new NamedList<String>();    nl.add("key1", "value1");    nl.add("key2", "value2");    assertEquals(2, nl.size());    String value = null;    value = nl.remove(0);    assertEquals("value1", value);    assertEquals(1, nl.size());    value = nl.remove("key2");    assertEquals("value2", value);    assertEquals(0, nl.size());  }    public void testRemoveAll() {    NamedList<String> nl = new NamedList<String>();    nl.add("key1", "value1-1");    nl.add("key2", "value2-1");    nl.add("key1", "value1-2");    nl.add("key2", "value2-2");    nl.add("key1", "value1-3");    nl.add("key2", "value2-3");    nl.add("key1", "value1-4");    nl.add("key2", "value2-4");    nl.add("key1", "value1-5");    nl.add("key2", "value2-5");    nl.add("key1", "value1-6");    assertEquals(11, nl.size());    List<String> values = null;    values = nl.removeAll("key1");    assertEquals("value1-1", values.get(0));    assertEquals("value1-3", values.get(2));    assertEquals(6, values.size());    assertEquals(5, nl.size());    values = nl.removeAll("key2");    assertEquals(5, values.size());    assertEquals(0, nl.size());  }    public void testRemoveArgs() {    NamedList<Object> nl = new NamedList<Object>();    nl.add("key1", "value1-1");    nl.add("key2", "value2-1");    nl.add("key1", "value1-2");    nl.add("key2", "value2-2");    nl.add("key1", "value1-3");    nl.add("key2", "value2-3");    nl.add("key1", "value1-4");    nl.add("key2", "value2-4");    nl.add("key1", "value1-5");    nl.add("key2", "value2-5");    nl.add("key1", "value1-6");    nl.add("key2", 0);    nl.add("key2", "value2-7");    assertEquals(13, nl.size());    List<String> values = (ArrayList<String>) nl.removeConfigArgs("key1");    assertEquals("value1-1", values.get(0));    assertEquals("value1-3", values.get(2));    assertEquals(6, values.size());    assertEquals(7, nl.size());    try {      values = (ArrayList<String>) nl.removeConfigArgs("key2");      fail();    }    catch(SolrException e) {      // Expected exception.      assertTrue(true);    }    // nl should be unmodified when removeArgs throws an exception.    assertEquals(7, nl.size());  }    public void testRecursive() {      /**       * NL结构说明       */        // key1        // key2        // - key2a        // - key2b        // --- key2b1        // --- key2b2        // - key2c        // - k2int1        // key3        // - key3a        // --- key3a1        // --- key3a2        // --- key3a3        // - key3b        // - key3c        // 实例化一个多样的NL结构.    NamedList<String> nl2b = new NamedList<String>();    nl2b.add("key2b1", "value2b1");    nl2b.add("key2b2", "value2b2");    NamedList<String> nl3a = new NamedList<String>();    nl3a.add("key3a1", "value3a1");    nl3a.add("key3a2", "value3a2");    nl3a.add("key3a3", "value3a3");    NamedList<Object> nl2 = new NamedList<Object>();    nl2.add("key2a", "value2a");    nl2.add("key2b", nl2b);    nl2.add("k2int1", (int) 5);    NamedList<Object> nl3 = new NamedList<Object>();    nl3.add("key3a", nl3a);    nl3.add("key3b", "value3b");    nl3.add("key3c", "value3c");    nl3.add("key3c", "value3c2");    NamedList<Object> nl = new NamedList<Object>();    nl.add("key1", "value1");    nl.add("key2", nl2);    nl.add("key3", nl3);        // 简单的三级检查.    String test1 = (String) nl.findRecursive("key2", "key2b", "key2b2");    assertEquals("value2b2", test1);    String test2 = (String) nl.findRecursive("key3", "key3a", "key3a3");    assertEquals("value3a3", test2);    // 二级检查    String test3 = (String) nl.findRecursive("key3", "key3c");    assertEquals("value3c", test3);    // 检查无效值返回null.    String test4 = (String) nl.findRecursive("key3", "key3c", "invalid");    assertEquals(null, test4);    String test5 = (String) nl.findRecursive("key3", "invalid", "invalid");    assertEquals(null, test5);    String test6 = (String) nl.findRecursive("invalid", "key3c");    assertEquals(null, test6);    // 验证检索NamedList对象具有正确的类型.    Object test7 = nl.findRecursive("key2", "key2b");    assertTrue(test7 instanceof NamedList);    // Integer检查.    int test8 = (Integer) nl.findRecursive("key2", "k2int1");    assertEquals(5, test8);    // Check that a single argument works the same as get(String).    String test9 = (String) nl.findRecursive("key1");    assertEquals("value1", test9);    // enl == 明确嵌套列表    //    // key1    // - key1a    // - key1b    // key2 (null list)    NamedList<NamedList<String>> enl = new NamedList<NamedList<String>>();    NamedList<String> enlkey1 = new NamedList<String>();    NamedList<String> enlkey2 = null;    enlkey1.add("key1a", "value1a");    enlkey1.add("key1b", "value1b");    enl.add("key1", enlkey1);    enl.add("key2", enlkey2);        // 和上面的测试很类似, 只是重复了明确嵌套的对象类型.    String enltest1 = (String) enl.findRecursive("key1", "key1a");    assertEquals("value1a", enltest1);    String enltest2 = (String) enl.findRecursive("key1", "key1b");    assertEquals("value1b", enltest2);    // 验证:在存储一个null值时,get方法返回的是一个null,那么验证这个递归方法.    Object enltest3 = enl.get("key2");    assertNull(enltest3);    Object enltest4 = enl.findRecursive("key2");    assertNull(enltest4);  }}

 

solrj:org.apache.solr.common.util.NamedList.java