jetbrains.exodus.AbstractConfig Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sonarlint-core Show documentation
Show all versions of sonarlint-core Show documentation
Common library used by some SonarLint flavors
/**
* Copyright 2010 - 2022 JetBrains s.r.o.
*
* 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
*
* https://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 jetbrains.exodus;
import jetbrains.exodus.core.dataStructures.Pair;
import jetbrains.exodus.core.dataStructures.hash.HashMap;
import jetbrains.exodus.util.StringHashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Base class for {@linkplain jetbrains.exodus.env.EnvironmentConfig} and
* {@linkplain jetbrains.exodus.entitystore.PersistentEntityStoreConfig}.
*/
public abstract class AbstractConfig {
@NonNls
private static final String UNSUPPORTED_TYPE_ERROR_MSG = "Unsupported value type";
@NonNls
private final static ThreadLocal listenersSuppressed = ThreadLocal.withInitial(() -> false);
@NotNull
private final Map settings;
@NotNull
private final Set listeners;
private boolean isMutable;
protected AbstractConfig(@NotNull final Pair[] props, @NotNull final ConfigurationStrategy strategy) {
settings = new StringHashMap<>();
listeners = Collections.newSetFromMap(new ConcurrentHashMap<>());
isMutable = true;
for (final Pair prop : props) {
final String propName = prop.getFirst();
final Object defaultValue = prop.getSecond();
final Object value;
if (defaultValue == null) { // String is considered default property type
value = getString(strategy, propName, null);
} else {
final Class> clazz = defaultValue.getClass();
if (clazz == Boolean.class) {
value = getBoolean(strategy, propName, (Boolean) defaultValue);
} else if (clazz == Integer.class) {
value = getInteger(strategy, propName, (Integer) defaultValue);
} else if (clazz == Long.class) {
value = getLong(strategy, propName, (Long) defaultValue);
} else if (clazz == String.class) {
value = getString(strategy, propName, (String) defaultValue);
} else {
throw new ExodusException(UNSUPPORTED_TYPE_ERROR_MSG);
}
}
if (value != null) {
setSetting(propName, value);
}
}
}
public Object getSetting(@NotNull final String key) {
return settings.get(key);
}
public AbstractConfig setSetting(@NotNull final String key, @NotNull final Object value) {
checkIsMutable();
if (!value.equals(settings.get(key))) {
Map> listenerToContext = null;
final boolean listenersSuppressed = AbstractConfig.listenersSuppressed.get();
if (!listenersSuppressed) {
listenerToContext = new HashMap<>();
for (final ConfigSettingChangeListener listener : listeners) {
Map context = new HashMap<>();
listener.beforeSettingChanged(key, value, context);
listenerToContext.put(listener, context);
}
}
settings.put(key, value);
if (!listenersSuppressed) {
for (final ConfigSettingChangeListener listener : listeners) {
listener.afterSettingChanged(key, value, listenerToContext.get(listener));
}
}
}
return this;
}
public AbstractConfig removeSetting(@NotNull final String key) {
checkIsMutable();
settings.remove(key);
return this;
}
public Map getSettings() {
return Collections.unmodifiableMap(settings);
}
public void addChangedSettingsListener(@NotNull final ConfigSettingChangeListener listener) {
listeners.add(listener);
}
public void removeChangedSettingsListener(@NotNull final ConfigSettingChangeListener listener) {
listeners.remove(listener);
}
public void setSettings(@NotNull final Map settings) throws InvalidSettingException {
checkIsMutable();
final StringBuilder errorMessage = new StringBuilder();
for (final Map.Entry entry : settings.entrySet()) {
final String key = entry.getKey();
final Object oldValue = getSetting(key);
if (oldValue == null) {
appendLineFeed(errorMessage);
errorMessage.append("Unknown setting key: ");
errorMessage.append(key);
continue;
}
final String value = entry.getValue();
final Object newValue;
final Class> clazz = oldValue.getClass();
try {
if (clazz == Boolean.class) {
newValue = Boolean.valueOf(value);
} else if (clazz == Integer.class) {
newValue = Integer.decode(value);
} else if (clazz == Long.class) {
newValue = Long.decode(value);
} else if (clazz == String.class) {
newValue = value;
} else {
appendLineFeed(errorMessage);
errorMessage.append(UNSUPPORTED_TYPE_ERROR_MSG);
errorMessage.append(": ");
errorMessage.append(clazz);
continue;
}
setSetting(key, newValue);
} catch (NumberFormatException ignore) {
appendLineFeed(errorMessage);
errorMessage.append(UNSUPPORTED_TYPE_ERROR_MSG);
errorMessage.append(": ");
errorMessage.append(clazz);
}
}
if (errorMessage.length() > 0) {
throw new InvalidSettingException(errorMessage.toString());
}
}
public boolean isMutable() {
return isMutable;
}
public AbstractConfig setMutable(final boolean isMutable) {
this.isMutable = isMutable;
return this;
}
private void checkIsMutable() {
if (!isMutable) {
throw new ExodusException("Config is immutable");
}
}
public static void suppressConfigChangeListenersForThread() {
listenersSuppressed.set(true);
}
public static void resumeConfigChangeListenersForThread() {
listenersSuppressed.set(false);
}
private static boolean getBoolean(@NotNull final ConfigurationStrategy strategy,
@NotNull final String propName,
final boolean defaultValue) {
final String value = strategy.getProperty(propName);
//noinspection deprecation,UnnecessaryBoxing,BooleanConstructorCall
return value == null ? defaultValue : new Boolean("true".equalsIgnoreCase(value));
}
private static Integer getInteger(@NotNull final ConfigurationStrategy strategy,
@NotNull final String propName,
final Integer defaultValue) {
final String v = strategy.getProperty(propName);
if (v != null) {
try {
//noinspection CachedNumberConstructorCall,deprecation,BoxingBoxedValue
return new Integer(Integer.decode(v));
} catch (NumberFormatException ignored) {
}
}
return defaultValue;
}
private static Long getLong(@NotNull final ConfigurationStrategy strategy,
@NotNull final String propName,
final Long defaultValue) {
final String v = strategy.getProperty(propName);
if (v != null) {
try {
//noinspection CachedNumberConstructorCall,deprecation,BoxingBoxedValue
return new Long(Long.decode(v));
} catch (NumberFormatException ignored) {
}
}
return defaultValue;
}
private static String getString(@NotNull final ConfigurationStrategy strategy,
@NotNull final String propName,
final String defaultValue) {
final String v = strategy.getProperty(propName);
//noinspection StringOperationCanBeSimplified
return v == null ? defaultValue : new String(v);
}
private static void appendLineFeed(@NotNull final StringBuilder builder) {
if (builder.length() > 0) {
builder.append('\n');
}
}
}