zoukankan      html  css  js  c++  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="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);
      }
    }
  • 相关阅读:
    一步步学习SPD2010--附录C--使用SP2010管理任务(5)--配置外部内容类型的权限
    一步步学习SPD2010--附录C--使用SP2010管理任务(4)--在Web应用程序层次限制SPD2010使用
    一步步学习SPD2010--附录C--使用SP2010管理任务(3)--在SP Server网站激活所有网站模板
    一步步学习SPD2010--附录C--使用SP2010管理任务(2)--创建网站集
    一步步学习SPD2010--附录C--使用SP2010管理任务(1)--安装SP Foundation2010
    一步步学习SPD2010--附录C--使用SP2010管理任务
    一步步学习SPD2010--附录B--创建新的批准流程
    指数分布与泊松分布
    宋浩《概率论与数理统计》笔记---2.2.3、指数分布
    宋浩《概率论与数理统计》笔记---2.2.3、均匀分布
  • 原文地址:https://www.cnblogs.com/a198720/p/4216428.html
Copyright © 2011-2022 走看看