Ticket #21139: 21139.patch

File 21139.patch, 4.5 KB (added by taylor.smock, 3 years ago)

Reset all static AbstractProperty preferences fields to the new Config.

  • test/unit/org/openstreetmap/josm/testutils/annotations/BasicPreferences.java

    diff --git a/test/unit/org/openstreetmap/josm/testutils/annotations/BasicPreferences.java b/test/unit/org/openstreetmap/josm/testutils/annotations/BasicPreferences.java
    index 1ee93ed28..f1bfba2fd 100644
    a b import java.lang.annotation.ElementType;  
    66import java.lang.annotation.Retention;
    77import java.lang.annotation.RetentionPolicy;
    88import java.lang.annotation.Target;
     9import java.lang.reflect.Field;
     10import java.lang.reflect.Modifier;
     11import java.util.Vector;
     12import java.util.stream.Collectors;
    913
    1014import org.junit.jupiter.api.extension.AfterAllCallback;
    1115import org.junit.jupiter.api.extension.AfterEachCallback;
    import org.junit.jupiter.api.extension.ExtendWith;  
    1519import org.junit.jupiter.api.extension.ExtensionContext;
    1620import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
    1721import org.openstreetmap.josm.data.Preferences;
     22import org.openstreetmap.josm.data.preferences.AbstractProperty;
    1823import org.openstreetmap.josm.data.preferences.JosmBaseDirectories;
    1924import org.openstreetmap.josm.data.preferences.JosmUrls;
    2025import org.openstreetmap.josm.spi.preferences.Config;
    public @interface BasicPreferences {  
    6469
    6570            // Store the pref for other extensions
    6671            context.getStore(Namespace.create(BasicPreferencesExtension.class)).put("preferences", pref);
     72
     73            resetConfigVariables(pref);
    6774        }
    6875
    6976        @Override
    public @interface BasicPreferences {  
    7279                this.beforeAll(context);
    7380            }
    7481        }
     82
     83        /**
     84         * Reset all {@link AbstractProperty} preferences to the new preference
     85         * @param pref The preference to use
     86         * @throws ReflectiveOperationException if reflection fails
     87         */
     88        private void resetConfigVariables(Preferences pref) throws ReflectiveOperationException {
     89            final Field classField = ClassLoader.class.getDeclaredField("classes");
     90            classField.setAccessible(true);
     91            final Field configField = AbstractProperty.class.getDeclaredField("preferences");
     92            final boolean configIsFinal = Modifier.isFinal(configField.getModifiers());
     93            try {
     94                if (configIsFinal) {
     95                    makeFinalFieldModifiable(configField, true);
     96                }
     97                // We are pretty much requiring that this doesn't change.
     98                @SuppressWarnings("unchecked")
     99                Vector<Class<?>> classes = (Vector<Class<?>>) classField.get(this.getClass().getClassLoader());
     100                // We need to copy to another list (ConcurrentModificationException)
     101                for (Class<?> clazz : classes.stream().collect(Collectors.toList())) {
     102                    for (Field field : clazz.getDeclaredFields()) {
     103                        if (Modifier.isStatic(field.getModifiers()) && AbstractProperty.class.isInstance(field.getType())) {
     104                            configField.set(field.get(null), pref);
     105                        }
     106                    }
     107                }
     108            } finally {
     109                if (configIsFinal) {
     110                    makeFinalFieldModifiable(configField, false);
     111                }
     112            }
     113        }
     114
     115        /**
     116         * Make a final field modifiable. This should always be called twice (once prior to modification, once after)
     117         * @param field The field to make modifiable
     118         * @param modifiable {@code false} will make the field final
     119         * @throws ReflectiveOperationException If the reflection operations fail
     120         */
     121        private static void makeFinalFieldModifiable(Field field, boolean modifiable) throws ReflectiveOperationException {
     122            Field modifiersField = Field.class.getDeclaredField("modifiers");
     123            boolean isModifierAccessible = modifiersField.isAccessible();
     124            try {
     125                if (!isModifierAccessible) {
     126                    modifiersField.setAccessible(true);
     127                }
     128                if (modifiable) {
     129                    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
     130                } else {
     131                    modifiersField.setInt(field, field.getModifiers() | Modifier.FINAL);
     132                }
     133            } finally {
     134                if (!isModifierAccessible) {
     135                    modifiersField.setAccessible(false);
     136                }
     137            }
     138        }
    75139    }
    76140}