Add a method to apply delta to user preferences config

This method can be used with the proto config
CachedPreferences#asUserPreferencesProto().  The caller should first
convert the proto to a java class usign one of the
UserPreferencesConverters

Make all UserPreferencesConverter classes public so that they can be
used in this way.

Change-Id: I0e4829ea94133abf48f2f86e763dd1ae622b63cd
Google-Bug-Id: b/289357382
Release-Notes:skip
diff --git a/java/com/google/gerrit/server/config/ConfigUtil.java b/java/com/google/gerrit/server/config/ConfigUtil.java
index 5d94255..22c3d99 100644
--- a/java/com/google/gerrit/server/config/ConfigUtil.java
+++ b/java/com/google/gerrit/server/config/ConfigUtil.java
@@ -17,6 +17,9 @@
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.UsedAt;
+import com.google.gerrit.common.UsedAt.Project;
+import java.io.IOException;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Modifier;
@@ -391,6 +394,41 @@
     return s;
   }
 
+  /**
+   * Update user config by applying the specified delta
+   *
+   * <p>As opposed to {@link com.google.gerrit.server.config.ConfigUtil#storeSection}, this method
+   * does not unset a variable that are set to default, because it is expected that the input {@code
+   * original} is the raw user config value (does not include the defaults)
+   *
+   * <p>To use this method with the proto config (see {@link
+   * CachedPreferences#asUserPreferencesProto()}), the caller can first convert the proto to a java
+   * class usign one of the {@link UserPreferencesConverter} classes.
+   *
+   * <p>Fields marked with final or transient modifiers are skipped.
+   *
+   * @param original the original current user config
+   * @param updateDelta instance of class with config values that need to be uplied to the original
+   *     config
+   */
+  @UsedAt(Project.GOOGLE)
+  public static <T> void updatePreferences(T original, T updateDelta) throws IOException {
+    try {
+      for (Field f : updateDelta.getClass().getDeclaredFields()) {
+        if (skipField(f)) {
+          continue;
+        }
+        f.setAccessible(true);
+        Object c = f.get(updateDelta);
+        if (c != null) {
+          f.set(original, c);
+        }
+      }
+    } catch (SecurityException | IllegalArgumentException | IllegalAccessException e) {
+      throw new IOException("cannot apply delta the original config", e);
+    }
+  }
+
   public static boolean skipField(Field field) {
     int modifiers = field.getModifiers();
     return Modifier.isFinal(modifiers) || Modifier.isTransient(modifiers);
diff --git a/java/com/google/gerrit/server/config/UserPreferencesConverter.java b/java/com/google/gerrit/server/config/UserPreferencesConverter.java
index bb611ad..2dccabd 100644
--- a/java/com/google/gerrit/server/config/UserPreferencesConverter.java
+++ b/java/com/google/gerrit/server/config/UserPreferencesConverter.java
@@ -31,8 +31,8 @@
  * <p>Upstream, we use java representations of the preference classes. Internally, we store proto
  * equivalents in Spanner.
  */
-final class UserPreferencesConverter {
-  static final class GeneralPreferencesInfoConverter {
+public final class UserPreferencesConverter {
+  public static final class GeneralPreferencesInfoConverter {
     public static UserPreferences.GeneralPreferencesInfo toProto(GeneralPreferencesInfo info) {
       UserPreferences.GeneralPreferencesInfo.Builder builder =
           UserPreferences.GeneralPreferencesInfo.newBuilder();
@@ -197,7 +197,7 @@
     private GeneralPreferencesInfoConverter() {}
   }
 
-  static final class DiffPreferencesInfoConverter {
+  public static final class DiffPreferencesInfoConverter {
     public static UserPreferences.DiffPreferencesInfo toProto(DiffPreferencesInfo info) {
       UserPreferences.DiffPreferencesInfo.Builder builder =
           UserPreferences.DiffPreferencesInfo.newBuilder();
@@ -272,7 +272,7 @@
     private DiffPreferencesInfoConverter() {}
   }
 
-  static final class EditPreferencesInfoConverter {
+  public static final class EditPreferencesInfoConverter {
     public static UserPreferences.EditPreferencesInfo toProto(EditPreferencesInfo info) {
       UserPreferences.EditPreferencesInfo.Builder builder =
           UserPreferences.EditPreferencesInfo.newBuilder();