Skip to content
Snippets Groups Projects
Commit 222490a7 authored by lmihalkovic's avatar lmihalkovic
Browse files

OO-2007: extends ModuleConfiguration with fragments (subset with shared key...

OO-2007: extends ModuleConfiguration with fragments (subset with shared key prefix) and strongly typed API
parent 611be92c
No related branches found
No related tags found
No related merge requests found
/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Initial code contributed and copyrighted by<br>
* frentix GmbH, http://www.frentix.com
* <p>
*/
package org.olat.modules;
import org.olat.modules.ModuleProperty.ModulePropertyValue;
/**
* A simple interface for dealing with sets of key/value pairs supporting the following
* <ul>
* <li>subsets based on a common key prefix (eg: sendToUsers, sendToOwners --> prefix: sendTo)
* <li>strongly typed properties (the definition of "keyName" is associated with a java type)
* </ul>
*
* <p>Initial date: May 6, 2016
* @author lmihalkovic, http://www.frentix.com
*/
public interface IModuleConfiguration {
/**
* Factory method for creating a {@link IModuleConfiguration} instance with a given
* property name prefix, backed by a given instance of {@link ModuleConfiguration}.
* This method allows a custom prefix/name separator to be specified. Passing {@code null}
* as a separator is equivalent to passing an empty string {@code ""}.
*
* @param fragmentName
* @param sep
* @param config
* @return
*/
public static IModuleConfiguration fragment(String fragmentName, String sep, ModuleConfiguration config) {
return new ModuleconfigurationFragment(fragmentName, sep, config);
}
/**
* Factory method for creating a {@link IModuleConfiguration} instance with a given
* property name prefix, backed by a given instance of {@link ModuleConfiguration}.
* By default the property name will follow the pattern: prefix_XXXXX, where XXXXX
* is the name passed as a parameter to the methods in this interface
*
*
* @param fragmentName
* @param config
* @return
*/
public static IModuleConfiguration fragment(String fragmentName, ModuleConfiguration config) {
return new ModuleconfigurationFragment(fragmentName, "_", config);
}
// ------------------------------------------------------------------------
public default boolean has(String configKey) {
return get(configKey) != null;
}
public default boolean hasAnyOf(String...configKeys) {
for(String key : configKeys) {
if (get(key) != null) return true;
}
return false;
}
public default boolean allTrue(String... configKeys) {
boolean rc = false;
for(String key : configKeys) {
rc = rc & getBooleanSafe(key);
if (!rc) break;
}
return rc;
}
public default boolean anyTrue(String... configKeys) {
boolean rc = false;
for(String key : configKeys) {
rc = getBooleanSafe(key);
if (rc) break;
}
return rc;
}
public boolean getBooleanSafe(String configKey);
public void setBooleanEntry(String configKey, boolean value);
public void set(String configKey, Object value);
public Object get(String configKey);
@SuppressWarnings("unchecked")
default public <T> T getAs(String configKey) {
Object val = get(configKey);
return val != null ? (T)val : null;
}
// ------------------------------------------------------------------------
// Strongly typed API
public <X> ModulePropertyValue<X> get(ModuleProperty<X> key);
public default <X> X val(ModuleProperty<X> key) {
return get(key).val();
}
public <X> void set(ModulePropertyValue<X> value);
public <X> void set(ModuleProperty<X> key, X value);
public default boolean has(ModuleProperty<?> key) {
ModulePropertyValue<?> val = get(key);
return val.isSet();
}
public boolean hasAnyOf(ModuleProperty<?>...keys);
public boolean anyTrue(ModuleProperty<Boolean> key);
public boolean anyTrue(ModuleProperty<Boolean> key1, ModuleProperty<Boolean> key2);
public boolean anyTrue(ModuleProperty<Boolean> key1, ModuleProperty<Boolean> key2, ModuleProperty<Boolean> key3);
public boolean anyTrue(ModuleProperty<Boolean> key1, ModuleProperty<Boolean> key2, ModuleProperty<Boolean> key3, ModuleProperty<Boolean> key4);
public boolean anyTrue(ModuleProperty<Boolean> key1, ModuleProperty<Boolean> key2, ModuleProperty<Boolean> key3, ModuleProperty<Boolean> key4, ModuleProperty<Boolean> key5);
public boolean allTrue(ModuleProperty<Boolean> key);
public boolean allTrue(ModuleProperty<Boolean> key1, ModuleProperty<Boolean> key2);
public boolean allTrue(ModuleProperty<Boolean> key1, ModuleProperty<Boolean> key2, ModuleProperty<Boolean> key3);
public boolean allTrue(ModuleProperty<Boolean> key1, ModuleProperty<Boolean> key2, ModuleProperty<Boolean> key3, ModuleProperty<Boolean> key4);
public boolean allTrue(ModuleProperty<Boolean> key1, ModuleProperty<Boolean> key2, ModuleProperty<Boolean> key3, ModuleProperty<Boolean> key4, ModuleProperty<Boolean> key5);
}
/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Initial code contributed and copyrighted by<br>
* frentix GmbH, http://www.frentix.com
* <p>
*/
package org.olat.modules;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
/**
* A simple implementation of a strongly typed named property
*
* <p>Initial date: May 6, 2016
* @author lmihalkovic, http://www.frentix.com
*/
public abstract class ModuleProperty<T> {
/**
* A strongly typed property value
*
* Initial date: May 6, 2016<br>
* @author lmihalkovic, http://www.frentix.com
*
*/
public static final class ModulePropertyValue<X> {
private X value;
private final ModuleProperty<X> def;
protected ModulePropertyValue(X value, ModuleProperty<X> def) {
this.value = value;
this.def = def;
}
public X val() {
if(!isSet()) return getDefault();
return value;
}
public void val(X val) {
this.value = val;
}
public X getDefault() {
return def.getDefault();
}
public boolean isSet() {
return this.value != null;
}
public String name() {
return def.name;
}
}
private final String name;
private final T defaultValue;
private final Type type;
public ModuleProperty(String name) {
this(name, null);
}
public ModuleProperty(String name, T defaultValue) {
this.name = name;
this.defaultValue = defaultValue;
this.type = getType();
}
public ModulePropertyValue<T> val(T value) {
return new ModulePropertyValue<T>(value, this);
}
public String name() {
return this.name;
}
public boolean hasDefault() {
return defaultValue != null;
}
T getDefault() {
return this.defaultValue;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[")
.append(name())
.append(":")
.append(type);
if (defaultValue!= null) {
sb.append(" {").append(defaultValue).append("}");
}
sb.append("]");
return sb.toString();
}
// ------------------------------
// Internal
private Type getType() {
Type superclass = getClass().getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("Missing type parameter.");
}
return ((ParameterizedType) superclass).getActualTypeArguments()[0];
}
@SuppressWarnings("unchecked")
Class<T> rawType() {
// this is ok or leads to a CCE later if the types do not match
return (Class<T>) getRawType(type);
}
private static Class<?> getRawType(Type type) {
if (type instanceof Class<?>) {
// type is a class
return (Class<?>) type;
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Type rawType = parameterizedType.getRawType();
checkArgument(rawType instanceof Class);
return (Class<?>) rawType;
} else if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) type).getGenericComponentType();
return Array.newInstance(getRawType(componentType), 0).getClass();
} else if (type instanceof TypeVariable) {
return Object.class;
} else if (type instanceof WildcardType) {
return getRawType(((WildcardType) type).getUpperBounds()[0]);
} else {
String className = type == null ? "null" : type.getClass().getName();
throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " + "GenericArrayType, but <"
+ type + "> is of type " + className);
}
}
private static void checkArgument(boolean condition) {
if (!condition) {
throw new IllegalArgumentException();
}
}
}
/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Initial code contributed and copyrighted by<br>
* frentix GmbH, http://www.frentix.com
* <p>
*/
package org.olat.modules;
import java.util.Date;
import java.util.List;
import org.olat.modules.ModuleProperty.ModulePropertyValue;
/**
* A sub-section of a standard module configuration. This is particularly
* useful in case a block of key/value pairs would be repeated, with a
* different prefix.
*
* <p>Initial date: May 6, 2016
* @author lmihalkovic, http://www.frentix.com
*/
public class ModuleconfigurationFragment implements IModuleConfiguration {
private final ModuleConfiguration config;
private final String fragmentName;
private final String sep;
protected ModuleconfigurationFragment(String fragmentName, String separator, ModuleConfiguration config) {
this.config = config;
this.fragmentName = fragmentName;
this.sep = separator == null ? "" : separator;
}
protected final String key(String configKey) {
return fragmentName + sep + configKey;
}
@Override
public boolean getBooleanSafe(String configKey) {
return config.getBooleanSafe(key(configKey));
}
@Override
public void setBooleanEntry(String configKey, boolean value) {
config.setBooleanEntry(key(configKey), value);
}
@Override
public void set(String configKey, Object value) {
config.set(key(configKey), value);
}
@Override
public Object get(String configKey) {
return config.get(key(configKey));
}
public <U> List<U> getList(String configKey, Class<U> cl) {
return config.getList(key(configKey), cl);
}
@Override
public boolean anyTrue(ModuleProperty<Boolean> key) {
return _anyTrue(key);
}
@Override
public boolean anyTrue(ModuleProperty<Boolean> key1, ModuleProperty<Boolean> key2) {
return _anyTrue(key1, key2);
}
@Override
public boolean anyTrue(ModuleProperty<Boolean> key1, ModuleProperty<Boolean> key2, ModuleProperty<Boolean> key3) {
return _anyTrue(key1, key2, key3);
}
@Override
public boolean anyTrue(ModuleProperty<Boolean> key1, ModuleProperty<Boolean> key2, ModuleProperty<Boolean> key3, ModuleProperty<Boolean> key4) {
return _anyTrue(key1, key2, key3, key4);
}
@Override
public boolean anyTrue(ModuleProperty<Boolean> key1, ModuleProperty<Boolean> key2, ModuleProperty<Boolean> key3, ModuleProperty<Boolean> key4, ModuleProperty<Boolean> key5) {
return _anyTrue(key1, key2, key3, key4, key5);
}
@SafeVarargs
protected final boolean _anyTrue(ModuleProperty<Boolean>... keys) {
boolean rc = false;
for(ModuleProperty<Boolean> key : keys) {
rc = getBooleanSafe(key.name());
if (rc) return true;
}
return rc;
}
@Override
public boolean allTrue(ModuleProperty<Boolean> key) {
return _allTrue(key);
}
@Override
public boolean allTrue(ModuleProperty<Boolean> key1, ModuleProperty<Boolean> key2) {
return _allTrue(key1, key2);
}
@Override
public boolean allTrue(ModuleProperty<Boolean> key1, ModuleProperty<Boolean> key2, ModuleProperty<Boolean> key3) {
return _allTrue(key1, key2, key3);
}
@Override
public boolean allTrue(ModuleProperty<Boolean> key1, ModuleProperty<Boolean> key2, ModuleProperty<Boolean> key3, ModuleProperty<Boolean> key4) {
return _allTrue(key1, key2, key3, key3);
}
@Override
public boolean allTrue(ModuleProperty<Boolean> key1, ModuleProperty<Boolean> key2, ModuleProperty<Boolean> key3, ModuleProperty<Boolean> key4, ModuleProperty<Boolean> key5) {
return _allTrue(key1, key2, key3, key4, key5);
}
@SafeVarargs
protected final boolean _allTrue(ModuleProperty<Boolean>... keys) {
for(ModuleProperty<Boolean> key : keys) {
boolean rc = getBooleanSafe(key.name());
if (!rc) return false;
}
return true;
}
@Override
public <X> ModulePropertyValue<X> get(ModuleProperty<X> key) {
ModulePropertyValue<X> value = valueOf(key);
return value;
}
@Override
public <X> void set(ModulePropertyValue<X> val) {
X value = val.val();
config.set(key(val.name()), value);
}
@Override
public <X> void set(ModuleProperty<X> key, X value) {
config.set(key(key.name()), value);
}
@Override
public boolean hasAnyOf(ModuleProperty<?>... keys) {
for(ModuleProperty<?> key : keys) {
ModulePropertyValue<?> val = get(key);
if (val.isSet()) return true;
}
return false;
}
protected <X> ModulePropertyValue<X> valueOf(ModuleProperty<X> key) {
Class<X> klass = key.rawType();
X val = null;
String name = key.name();
if(klass == Boolean.class) {
Boolean b = (key.hasDefault() ? config.getBooleanSafe(key(name), (boolean)(key.getDefault())) : config.getBooleanEntry(key(name)));
val = klass.cast(b);
} else if (klass == Float.class) {
Float f = config.getFloatEntry(key(name));
val = klass.cast(f);
} else if (klass == Integer.class) {
if(!key.hasDefault()) {
throw new IllegalArgumentException("Integer keys MUST define a default value");
}
Integer i = config.getIntegerSafe(key(name), (int)key.getDefault());
val = klass.cast(i);
} else if (klass == Date.class) {
Date d = config.getDateValue(key(name));
val = klass.cast(d);
} else {
// This is no different than the normal CCE that would happen
// in the calling code when doing
// SomeType val = (SomeType) config.get("keyName");
val = klass.cast(get(name));
}
return new ModulePropertyValue<X>(val, key);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment