You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by el...@apache.org on 2013/05/20 09:39:55 UTC

svn commit: r1484386 - in /lucene/dev/trunk/solr/solrj/src: java/org/apache/solr/common/util/NamedList.java test/org/apache/solr/common/util/NamedListTest.java

Author: elyograg
Date: Mon May 20 07:39:55 2013
New Revision: 1484386

URL: http://svn.apache.org/r1484386
Log:
SOLR-4048: improve and clean up findRecursive

Modified:
    lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java
    lucene/dev/trunk/solr/solrj/src/test/org/apache/solr/common/util/NamedListTest.java

Modified: lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java?rev=1484386&r1=1484385&r2=1484386&view=diff
==============================================================================
--- lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java (original)
+++ lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/util/NamedList.java Mon May 20 07:39:55 2013
@@ -23,8 +23,6 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
-
-
 /**
  * A simple container class for modeling an ordered list of name/value pairs.
  *
@@ -50,9 +48,10 @@ import java.util.Map;
  * or simply use a regular {@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 */
@@ -60,7 +59,6 @@ public class NamedList<T> implements Clo
     nvPairs = new ArrayList<Object>();
   }
 
-
   /**
    * Creates a NamedList instance containing the "name,value" pairs contained in the
    * Entry[].
@@ -83,8 +81,8 @@ public class NamedList<T> implements Clo
    * pairwise names/values.
    *
    * <p>
-   * When using this constructor, runtime typesafety is only guaranteed if the all
-   * even numbered elements of the input list are of type "T".
+   * 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
@@ -156,6 +154,7 @@ public class NamedList<T> implements Clo
 
   /**
    * 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) {
@@ -168,6 +167,7 @@ public class NamedList<T> implements Clo
 
   /**
    * Removes the name/value pair at the specified index.
+   *
    * @return the value at the index removed
    */
   public T remove(int idx) {
@@ -206,10 +206,11 @@ public class NamedList<T> implements Clo
    * 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);
@@ -222,6 +223,7 @@ public class NamedList<T> implements Clo
    * 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
    */
@@ -240,6 +242,7 @@ public class NamedList<T> implements Clo
 
   /**
    * Gets the values for the the specified name
+   *
    * @param name Name
    * @return List of values
    */
@@ -256,42 +259,66 @@ public class NamedList<T> implements Clo
   }
 
   /**
-   * 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 ojects themselves.
-   * This method is particularly useful for parsing the response
-   * from Solr's /admin/mbeans handler. 
+   * 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.
    * 
-   * Explicit casts are recommended.
    * 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 tree, null if not found.
-   */
-  public T findRecursive(String... args) {
-    NamedList<T> list = null;
-    T value = null;
-    for (String key : args) {
-      /* First pass: this list.  Later passes: previous value. */
-      if (list == null) {
-        list = this;
-      }
-      else
-      {
-        if (NamedList.class.isInstance(value)) {
-          list = (NamedList<T>) value;
+   * 
+   * 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;
         }
       }
-      value = list.get(key);
+      /*
+       * 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;
   }
@@ -313,12 +340,12 @@ public class NamedList<T> implements Clo
   }
 
   /**
-   *
+   * 
    * Helper class implementing Map.Entry&lt;String, T&gt; to store the key-value
-   * relationship in NamedList (the keys of which are String-s) 
+   * relationship in NamedList (the keys of which are String-s)
    */
-  public static final class NamedListEntry<T> implements Map.Entry<String, T> {
-
+  public static final class NamedListEntry<T> implements Map.Entry<String,T> {
+    
     public NamedListEntry() {
 
     }
@@ -335,7 +362,7 @@ public class NamedList<T> implements Clo
 
     @Override
     public T getValue() {
-      return  value;
+      return value;
     }
 
     @Override
@@ -354,8 +381,8 @@ public class NamedList<T> implements Clo
    * Iterates over the Map and sequentially adds it's key/value pairs
    */
   public boolean addAll(Map<String,T> args) {
-    for( Map.Entry<String, T> entry : args.entrySet() ) {
-      add( entry.getKey(), entry.getValue() );
+    for (Map.Entry<String, T> entry : args.entrySet() ) {
+      add(entry.getKey(), entry.getValue());
     }
     return args.size()>0;
   }
@@ -376,7 +403,6 @@ public class NamedList<T> implements Clo
     return new NamedList<T>(newList);
   }
 
-
   //----------------------------------------------------------------------------
   // Iterable interface
   //----------------------------------------------------------------------------
@@ -408,14 +434,12 @@ public class NamedList<T> implements Clo
           }
 
           @Override
-          @SuppressWarnings("unchecked")
           public T getValue() {
             return list.getVal( index );
           }
 
           @Override
-          public String toString()
-          {
+          public String toString() {
             return getKey()+"="+getValue();
           }
 
@@ -435,7 +459,7 @@ public class NamedList<T> implements Clo
     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).
@@ -446,7 +470,6 @@ public class NamedList<T> implements Clo
     return null;
   }
 
-
   public void clear() {
     nvPairs.clear();
   }
@@ -459,7 +482,7 @@ public class NamedList<T> implements Clo
   @Override
   public boolean equals(Object obj) {
     if (!(obj instanceof NamedList)) return false;
-    NamedList nl = (NamedList) obj;
+    NamedList<?> nl = (NamedList<?>) obj;
     return this.nvPairs.equals(nl.nvPairs);
   }
 }

Modified: lucene/dev/trunk/solr/solrj/src/test/org/apache/solr/common/util/NamedListTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/solrj/src/test/org/apache/solr/common/util/NamedListTest.java?rev=1484386&r1=1484385&r2=1484386&view=diff
==============================================================================
--- lucene/dev/trunk/solr/solrj/src/test/org/apache/solr/common/util/NamedListTest.java (original)
+++ lucene/dev/trunk/solr/solrj/src/test/org/apache/solr/common/util/NamedListTest.java Mon May 20 07:39:55 2013
@@ -29,6 +29,7 @@ public class NamedListTest extends Lucen
     assertEquals("value1", value);
     assertEquals(1, nl.size());
   }
+  
   public void testRecursive() {
     // key1
     // key2
@@ -37,6 +38,7 @@ public class NamedListTest extends Lucen
     // --- key2b1
     // --- key2b2
     // - key2c
+    // - k2int1
     // key3
     // - key3a
     // --- key3a1
@@ -44,50 +46,78 @@ public class NamedListTest extends Lucen
     // --- key3a3
     // - key3b
     // - key3c
-    NamedList<Object> nl2b = new NamedList<Object>();
+    
+    // this is a varied NL structure.
+    NamedList<String> nl2b = new NamedList<String>();
     nl2b.add("key2b1", "value2b1");
     nl2b.add("key2b2", "value2b2");
-    NamedList<Object> nl3a = new NamedList<Object>();
+    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);
-    int int1 = 5;
-    Integer int2 = 7;
-    int int3 = 48;
-    nl2.add("k2int1", int1);
-    nl2.add("k2int2", int2);
-    nl2.add("k2int3", int3);
+    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);
-
+    
+    // Simple three-level checks.
     String test1 = (String) nl.findRecursive("key2", "key2b", "key2b2");
-    assertEquals(test1, "value2b2");
+    assertEquals("value2b2", test1);
     String test2 = (String) nl.findRecursive("key3", "key3a", "key3a3");
-    assertEquals(test2, "value3a3");
+    assertEquals("value3a3", test2);
+    // Two-level check.
     String test3 = (String) nl.findRecursive("key3", "key3c");
-    assertEquals(test3, "value3c");
+    assertEquals("value3c", test3);
+    // Checking that invalid values return null.
     String test4 = (String) nl.findRecursive("key3", "key3c", "invalid");
-    assertEquals(test4, null);
+    assertEquals(null, test4);
     String test5 = (String) nl.findRecursive("key3", "invalid", "invalid");
-    assertEquals(test5, null);
+    assertEquals(null, test5);
     String test6 = (String) nl.findRecursive("invalid", "key3c");
-    assertEquals(test6, null);
-    Object nltest = nl.findRecursive("key2", "key2b");
-    assertTrue(nltest instanceof NamedList);
-    Integer int1test = (Integer) nl.findRecursive("key2", "k2int1");
-    assertEquals(int1test, (Integer) 5);
-    int int2test = (int) nl.findRecursive("key2", "k2int2");
-    assertEquals(int2test, 7);
-    int int3test = (int) nl.findRecursive("key2", "k2int3");
-    assertEquals(int3test, 48);
+    assertEquals(null, test6);
+    // Verify that retrieved NamedList objects have the right type.
+    Object test7 = nl.findRecursive("key2", "key2b");
+    assertTrue(test7 instanceof NamedList);
+    // Integer check.
+    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 == explicit nested list
+    //
+    // 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);
+    
+    // Tests that are very similar to the test above, just repeated
+    // on the explicitly nested object type.
+    String enltest1 = (String) enl.findRecursive("key1", "key1a");
+    assertEquals("value1a", enltest1);
+    String enltest2 = (String) enl.findRecursive("key1", "key1b");
+    assertEquals("value1b", enltest2);
+    // Verify that when a null value is stored, the standard get method
+    // says it is null, then check the recursive method.
+    Object enltest3 = enl.get("key2");
+    assertNull(enltest3);
+    Object enltest4 = enl.findRecursive("key2");
+    assertNull(enltest4);
   }
 }