Extract method from ChangeSchemas to build schema version map

Change-Id: I37779385858795933443f3f369df77acc393b70c
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/SchemaUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/SchemaUtil.java
new file mode 100644
index 0000000..f2cd341
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/SchemaUtil.java
@@ -0,0 +1,72 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// 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.google.gerrit.server.index;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.util.Collection;
+import java.util.Map;
+
+public class SchemaUtil {
+  public static <V> ImmutableMap<Integer, Schema<V>> schemasFromClass(
+      Class<?> schemasClass, Class<V> valueClass) {
+    Map<Integer, Schema<V>> schemas = Maps.newTreeMap();
+    for (Field f : schemasClass.getDeclaredFields()) {
+      if (Modifier.isStatic(f.getModifiers())
+          && Modifier.isFinal(f.getModifiers())
+          && Schema.class.isAssignableFrom(f.getType())) {
+        ParameterizedType t = (ParameterizedType) f.getGenericType();
+        if (t.getActualTypeArguments()[0] == valueClass) {
+          try {
+            f.setAccessible(true);
+            @SuppressWarnings("unchecked")
+            Schema<V> schema = (Schema<V>) f.get(null);
+            checkArgument(f.getName().startsWith("V"));
+            schema.setVersion(Integer.parseInt(f.getName().substring(1)));
+            schemas.put(schema.getVersion(), schema);
+          } catch (IllegalAccessException e) {
+            throw new IllegalArgumentException(e);
+          }
+        } else {
+          throw new IllegalArgumentException(
+              "non-" + schemasClass.getSimpleName() + " schema: " + f);
+        }
+      }
+    }
+    if (schemas.isEmpty()) {
+      throw new ExceptionInInitializerError("no ChangeSchemas found");
+    }
+    return ImmutableMap.copyOf(schemas);
+  }
+
+  public static <V> Schema<V> schema(Collection<FieldDef<V, ?>> fields) {
+    return new Schema<>(ImmutableList.copyOf(fields));
+  }
+
+  @SafeVarargs
+  public static <V> Schema<V> schema(FieldDef<V, ?>... fields) {
+    return schema(ImmutableList.copyOf(fields));
+  }
+
+  private SchemaUtil() {
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeSchemas.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeSchemas.java
index 2350575..60896d7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeSchemas.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeSchemas.java
@@ -15,21 +15,14 @@
 package com.google.gerrit.server.index.change;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.gerrit.server.index.SchemaUtil.schema;
 
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Maps;
-import com.google.gerrit.server.index.FieldDef;
 import com.google.gerrit.server.index.Schema;
+import com.google.gerrit.server.index.SchemaUtil;
 import com.google.gerrit.server.query.change.ChangeData;
 
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.ParameterizedType;
-import java.util.Collection;
-import java.util.Map;
-
 /** Secondary index schemas for changes. */
 public class ChangeSchemas {
   @Deprecated
@@ -109,16 +102,8 @@
 
   static final Schema<ChangeData> V27 = schema(V26.getFields().values());
 
-  private static Schema<ChangeData> schema(Collection<FieldDef<ChangeData, ?>> fields) {
-    return new Schema<>(ImmutableList.copyOf(fields));
-  }
-
-  @SafeVarargs
-  private static Schema<ChangeData> schema(FieldDef<ChangeData, ?>... fields) {
-    return schema(ImmutableList.copyOf(fields));
-  }
-
-  public static final ImmutableMap<Integer, Schema<ChangeData>> ALL;
+  public static final ImmutableMap<Integer, Schema<ChangeData>> ALL =
+      SchemaUtil.schemasFromClass(ChangeSchemas.class, ChangeData.class);
 
   public static Schema<ChangeData> get(int version) {
     Schema<ChangeData> schema = ALL.get(version);
@@ -129,33 +114,4 @@
   public static Schema<ChangeData> getLatest() {
     return Iterables.getLast(ALL.values());
   }
-
-  static {
-    Map<Integer, Schema<ChangeData>> all = Maps.newTreeMap();
-    for (Field f : ChangeSchemas.class.getDeclaredFields()) {
-      if (Modifier.isStatic(f.getModifiers())
-          && Modifier.isFinal(f.getModifiers())
-          && Schema.class.isAssignableFrom(f.getType())) {
-        ParameterizedType t = (ParameterizedType) f.getGenericType();
-        if (t.getActualTypeArguments()[0] == ChangeData.class) {
-          try {
-            @SuppressWarnings("unchecked")
-            Schema<ChangeData> schema = (Schema<ChangeData>) f.get(null);
-            checkArgument(f.getName().startsWith("V"));
-            schema.setVersion(Integer.parseInt(f.getName().substring(1)));
-            all.put(schema.getVersion(), schema);
-          } catch (IllegalArgumentException | IllegalAccessException e) {
-            throw new ExceptionInInitializerError(e);
-          }
-        } else {
-          throw new ExceptionInInitializerError(
-              "non-ChangeData schema: " + f);
-        }
-      }
-    }
-    if (all.isEmpty()) {
-      throw new ExceptionInInitializerError("no ChangeSchemas found");
-    }
-    ALL = ImmutableMap.copyOf(all);
-  }
 }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/SchemaUtilTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/SchemaUtilTest.java
new file mode 100644
index 0000000..ed62d77
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/SchemaUtilTest.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// 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.google.gerrit.server.index;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.index.SchemaUtil.schema;
+
+import com.google.gerrit.testutil.GerritBaseTests;
+
+import org.junit.Test;
+
+import java.util.Map;
+
+public class SchemaUtilTest extends GerritBaseTests {
+  static class TestSchemas {
+    static final Schema<String> V1 = schema();
+    static final Schema<String> V2 = schema();
+    static Schema<String> V3 = schema(); // Not final, ignored.
+    private static final Schema<String> V4 = schema();
+
+    // Ignored.
+    static Schema<String> V10 = schema();
+    final Schema<String> V11 = schema();
+  }
+
+  @Test
+  public void schemasFromClassBuildsMap() {
+    Map<Integer, Schema<String>> all =
+        SchemaUtil.schemasFromClass(TestSchemas.class, String.class);
+    assertThat(all.keySet()).containsExactly(1, 2, 4);
+    assertThat(all.get(1)).isEqualTo(TestSchemas.V1);
+    assertThat(all.get(2)).isEqualTo(TestSchemas.V2);
+    assertThat(all.get(4)).isEqualTo(TestSchemas.V4);
+
+    exception.expect(IllegalArgumentException.class);
+    SchemaUtil.schemasFromClass(TestSchemas.class, Object.class);
+  }
+}