Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.vectorprint.configuration.Settings Maven / Gradle / Ivy
package com.vectorprint.configuration;
/*
* #%L
* VectorPrintConfig3.0
* %%
* Copyright (C) 2011 - 2013 VectorPrint
* %%
* 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.
* #L%
*/
import com.vectorprint.VectorPrintRuntimeException;
import com.vectorprint.configuration.annotation.Setting;
import com.vectorprint.configuration.annotation.SettingsAnnotationProcessor;
import com.vectorprint.configuration.annotation.SettingsField;
import com.vectorprint.configuration.binding.AbstractBindingHelperDecorator;
import com.vectorprint.configuration.binding.settings.EnhancedMapBindingFactory;
import com.vectorprint.configuration.binding.settings.SettingsBindingService;
import com.vectorprint.configuration.decoration.AbstractPropertiesDecorator;
import com.vectorprint.configuration.decoration.DecorationAware;
import java.awt.Color;
import java.io.File;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
/**
* Enhances Java Map with support for data types, debugging info, working with
* default values in code. Internally a backing Map is used which is a HashMap
* by default. You can provide your own backing map in {@link #Settings(java.util.Map)
* }, this may not be an EnhancedMap implementation. You cannot subclass this
* class, instead subclass {@link AbstractPropertiesDecorator} and wrap an
* instance of this class.
*
* @see EnhancedMapBindingFactory
* @see com.vectorprint.configuration.decoration
* @see SettingsAnnotationProcessor
* @see SettingsField
* @see Setting
*
* @author Eduard Drenth at VectorPrint.nl
*/
public final class Settings implements EnhancedMap, DecorationAware {
private static final long serialVersionUID = 1;
private static final Logger log = Logger.getLogger(Settings.class.getName());
private final Map backingMap;
@Override
public void listProperties(PrintStream ps) {
ps.println("settings with id " + getId() + ":");
ps.println();
backingMap.entrySet().forEach((entry) -> {
ps.println(entry.getKey() + "=" + (entry.getValue() != null ? Arrays.asList(entry.getValue()) : ""));
});
ps.println("");
ps.println("settings wrapped by " + decorators.toString());
}
private String id;
private final Map help = new HashMap<>(50);
private final List> decorators
= new ArrayList<>(3);
private AbstractPropertiesDecorator outermostDecorator;
/**
* Creates a backing map {@link HashMap#HashMap() ) }.
*
*/
public Settings() {
backingMap = new HashMap<>();
}
/**
* Creates a backing map {@link HashMap#HashMap(int, float) }.
*
* @param initialCapacity
* @param loadFactor
*/
public Settings(int initialCapacity, float loadFactor) {
backingMap = new HashMap<>(initialCapacity, loadFactor);
}
/**
* Creates a backing map {@link HashMap#HashMap(int) }.
*
* @param initialCapacity
*/
public Settings(int initialCapacity) {
backingMap = new HashMap<>(initialCapacity);
}
/**
* Uses the provided Map as backing map, throws an IllegalArgumentException
* if the map is an instance of EnhancedMap.
*
* @param map
*/
public Settings(Map map) {
Objects.requireNonNull(map);
if (map instanceof EnhancedMap) {
throw new IllegalArgumentException("instance of " + EnhancedMap.class.getName() + " not allowed");
}
backingMap = new HashMap<>(map);
}
private void debug(Object val, String... keys) {
debug(val, true, keys);
}
private void debug(Object val, boolean defaultVal, String... keys) {
if (log.isLoggable(Level.FINE)) {
StringBuilder s = new StringBuilder(String.valueOf(val));
if (val != null && val.getClass().isArray()) {
s = new StringBuilder("");
Class compType = val.getClass().getComponentType();
if (!compType.isPrimitive()) {
for (Object o : (Object[]) val) {
s.append(String.valueOf(o)).append("; ");
}
} else if (compType.equals(boolean.class)) {
for (boolean o : (boolean[]) val) {
s.append(String.valueOf(o)).append("; ");
}
} else if (compType.equals(char.class)) {
for (char o : (char[]) val) {
s.append(String.valueOf(o)).append("; ");
}
} else if (compType.equals(byte.class)) {
for (byte o : (byte[]) val) {
s.append(String.valueOf(o)).append("; ");
}
} else if (compType.equals(short.class)) {
for (short o : (short[]) val) {
s.append(String.valueOf(o)).append("; ");
}
} else if (compType.equals(int.class)) {
for (int o : (int[]) val) {
s.append(String.valueOf(o)).append("; ");
}
} else if (compType.equals(long.class)) {
for (long o : (long[]) val) {
s.append(String.valueOf(o)).append("; ");
}
} else if (compType.equals(float.class)) {
for (float o : (float[]) val) {
s.append(String.valueOf(o)).append("; ");
}
} else if (compType.equals(double.class)) {
for (double o : (double[]) val) {
s.append(String.valueOf(o)).append("; ");
}
}
}
log.fine(String.format("looking for property %s in %s, using value %s", Arrays.asList(keys), getId(), s.append((defaultVal) ? " (default)" : "").toString()));
}
}
@Override
public final String[] get(Object key) {
unused.remove(key);
return backingMap.get(key);
}
private String getFirst(String key) {
if (log.isLoggable(Level.FINE)) {
debug((key != null) ? backingMap.get(key) : null, false, key);
}
if (key == null) {
return null;
}
String[] l = get(key);
if (l != null) {
if (l.length > 1) {
throw new VectorPrintRuntimeException(String.format("more then one value (%s) for %s, expected one",
getFactory().getBindingHelper().serializeValue(l), key));
}
return l.length == 0 ? null : l[0];
} else {
return null;
}
}
/**
* When only one argument is given it is assumed to be a key and not a
* default value.
*
* @param defaultValue
* @param keys
* @return
*/
@Override
public String getProperty(String defaultValue, String... keys) {
boolean defaultIsKey = defaultValue != null && (keys == null || keys.length == 0);
String key = defaultIsKey
? getKey(null, defaultValue)
: getKey(defaultValue, keys);
return key == null ? defaultValue : getFirst(key);
}
@Override
public URL getURLProperty(URL defaultValue, String... keys) throws MalformedURLException {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : getFactory().getBindingHelper().convert(getFirst(key), URL.class);
}
@Override
public File getFileProperty(File defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : getFactory().getBindingHelper().convert(getFirst(key), File.class);
}
@Override
public float getFloatProperty(Float defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : getFactory().getBindingHelper().convert(getFirst(key), Float.class);
}
@Override
public boolean getBooleanProperty(Boolean defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : getFactory().getBindingHelper().convert(getFirst(key), Boolean.class);
}
/**
* determine the {@link #determineKey(java.lang.String...) key to be used}, call {@link #handleNoValue(java.lang.String...) } when no key is found
* and no default is given.
*
* @param defaultVal a default value
* @param keys the keys to look for
* @return the key to be used or null to use a default value
*/
private String getKey(Object defaultVal, String... keys) {
String key = determineKey(keys);
if (key == null) {
if (defaultVal == null) {
handleNoValue(keys);
} else {
debug(defaultVal, key);
}
} else {
return key;
}
return null;
}
/**
* throws a {@link NoValueException}
* @param keys
*/
private void handleNoValue(String... keys) {
throw new NoValueException(Arrays.asList(keys) + " not found and default is null");
}
/**
* Return the first key from the list of arguments present in the settings.
* Logs the key that is returned, maintains
* {@link #getKeysNotPresent() set of keys not present}.
*
* @see #keyNotPresent(java.lang.String)
* @see #keyPresent(java.lang.String)
*
* @param keys
* @return the first key found in settings or null
*/
private String determineKey(String... keys) {
if (keys == null || keys.length == 0 || keys[0] == null) {
throw new VectorPrintRuntimeException("You should provide at least one key");
}
if (keys.length == 1) {
if (containsKey(keys[0])) {
if (log.isLoggable(Level.FINE)) {
log.fine(String.format("Returning key \"%s\" from %s, it was first found in settings", keys[0], Arrays.asList(keys)));
}
if (notPresent.contains(keys[0])) {
notPresent.remove(keys[0]);
}
return keys[0];
} else {
if (!notPresent.contains(keys[0])) {
notPresent.add(keys[0]);
}
return null;
}
}
for (String k : keys) {
if (containsKey(k)) {
if (log.isLoggable(Level.FINE)) {
log.fine(String.format("Returning key \"%s\" from %s, it was first found in settings", k, Arrays.asList(keys)));
}
if (notPresent.contains(keys[0])) {
notPresent.remove(keys[0]);
}
return k;
} else if (!notPresent.contains(k)) {
notPresent.add(k);
}
}
if (log.isLoggable(Level.FINE)) {
log.fine(String.format("None of %s found in settings", Arrays.asList(keys)));
}
return null;
}
@Override
public double getDoubleProperty(Double defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : getFactory().getBindingHelper().convert(getFirst(key), Double.class);
}
@Override
public int getIntegerProperty(Integer defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : getFactory().getBindingHelper().convert(getFirst(key), Integer.class);
}
@Override
public short getShortProperty(Short defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : getFactory().getBindingHelper().convert(getFirst(key), Short.class);
}
@Override
public char getCharProperty(Character defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : getFactory().getBindingHelper().convert(getFirst(key), Character.class);
}
@Override
public byte getByteProperty(Byte defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : getFactory().getBindingHelper().convert(getFirst(key), Byte.class);
}
@Override
public long getLongProperty(Long defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : getFactory().getBindingHelper().convert(getFirst(key), Long.class);
}
@Override
public Color getColorProperty(Color defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : getFactory().getBindingHelper().convert(getFirst(key), Color.class);
}
@Override
public String[] getStringProperties(String[] defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
if (key == null) {
return defaultValue;
}
return get(key);
}
@Override
public URL[] getURLProperties(URL[] defaultValue, String... keys) throws MalformedURLException {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : AbstractBindingHelperDecorator.parseURLValues(get(key));
}
@Override
public File[] getFileProperties(File[] defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : AbstractBindingHelperDecorator.parseFileValues(get(key));
}
@Override
public float[] getFloatProperties(float[] defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : AbstractBindingHelperDecorator.parseFloatValues(get(key));
}
@Override
public char[] getCharProperties(char[] defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : AbstractBindingHelperDecorator.parseCharValues(get(key));
}
@Override
public short[] getShortProperties(short[] defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : AbstractBindingHelperDecorator.parseShortValues(get(key));
}
@Override
public byte[] getByteProperties(byte[] defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : AbstractBindingHelperDecorator.parseByteValues(get(key));
}
@Override
public double[] getDoubleProperties(double[] defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : AbstractBindingHelperDecorator.parseDoubleValues(get(key));
}
@Override
public int[] getIntegerProperties(int[] defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : AbstractBindingHelperDecorator.parseIntValues(get(key));
}
@Override
public boolean[] getBooleanProperties(boolean[] defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : AbstractBindingHelperDecorator.parseBooleanValues(get(key));
}
@Override
public Color[] getColorProperties(Color[] defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : AbstractBindingHelperDecorator.parseColorValues(get(key));
}
/**
* This implementation only includes key and value state, not the rest of
* the state (i.e. {@link #getUnusedKeys() }, {@link #getHelp()
* } and {@link #getKeysNotPresent() }). The {@link #getId() id's} of the
* objects must be both null or the same, otherwise false is returned.
*
* @param obj
* @return
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (getClass() != obj.getClass()) {
return false;
}
final Settings other = (Settings) obj;
if ((this.id == null) ? (other.id != null) : !this.id.equals(other.id)) {
return false;
}
return Objects.equals(backingMap, other.backingMap);
}
@Override
public PropertyHelp getHelp(String key) {
if (help.containsKey(key)) {
return help.get(key);
} else {
return new PropertyHelpImpl("no help configured for " + key);
}
}
/**
* initializes help for properties
*
* @param help
* @see #getHelp(java.lang.String)
* @see #getHelp()
*/
@Override
public void setHelp(Map help) {
this.help.clear();
this.help.putAll(help);
}
@Override
public Map getHelp() {
return help;
}
@Override
public String printHelp() {
StringBuilder sb = new StringBuilder(1024);
help.entrySet().forEach((h) -> {
sb.append(h.getKey()).append(": ").append(h.getValue().getType())
.append("; ")
.append(h.getValue().getExplanation())
.append(System.getProperty("line.separator"));
});
return sb.toString();
}
@Override
public long[] getLongProperties(long[] defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : AbstractBindingHelperDecorator.parseLongValues(get(key));
}
@Override
public final String[] put(String key, String[] value) {
if (!backingMap.containsKey(key)) unused.add(key);
return backingMap.put(key, value);
}
@Override
public final void clear() {
unused.clear();
notPresent.clear();
help.clear();
decorators.clear();
backingMap.clear();
}
@Override
public final String[] remove(Object key) {
unused.remove(key);
return backingMap.remove(key);
}
private void init(Settings vp) {
vp.help.putAll(help);
vp.unused.addAll(keySet());
vp.id = id;
vp.notPresent.addAll(notPresent);
vp.decorators.addAll(decorators);
vp.outermostDecorator = outermostDecorator;
}
/**
* Creates a new identical Settings object. Note that the id of the clone
* will equal that of the original. The backing Map will be cloned by
* calling clone when it is Cloneable, otherwise newInstance and putAll are
* used to clone the backing Map. The
* {@link #getOutermostDecorator() outermostDecorator} will be copied, not
* cloned.
*
* @return an identical copy of these Settings.
*/
@Override
public Settings clone() {
Settings vp;
if (backingMap instanceof Cloneable) {
try {
Method m = backingMap.getClass().getMethod("clone");
vp = new Settings((Map) m.invoke(backingMap, null));
} catch (InvocationTargetException | IllegalArgumentException | IllegalAccessException | NoSuchMethodException | SecurityException ex) {
throw new VectorPrintRuntimeException(ex);
}
} else {
try {
vp = new Settings(backingMap.getClass().newInstance());
} catch (IllegalAccessException | InstantiationException ex) {
throw new VectorPrintRuntimeException(ex);
}
vp.backingMap.putAll(backingMap);
}
init(vp);
return vp;
}
/**
*
* @return the id of the properties or null
*/
@Override
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
/**
* this implementation supports all primitives and their wrappers, Color,
* Date, URL, Class and arrays of those types. Calls {@link #getGenericProperty(java.lang.String, java.lang.Object, java.lang.Class)
* } if one of the keys is present in the settings.
*
* @param
* @param keys
* @param defaultValue
* @param clazz
* @return value of the setting or the default value
* @throws VectorPrintRuntimeException when no value is found and
* defaultValue is null
*/
public T getGenericProperty(T defaultValue, Class clazz, String... keys) {
String key = getKey(defaultValue, keys);
return getGenericProperty(key, defaultValue, clazz);
}
/**
* this implementation supports all primitives and their wrappers, Color,
* Date, URL, Class and arrays of those types.
*
* @param
* @param key
* @param defaultValue
* @param clazz
* @return value of the setting or the default value
* defaultValue is null
*/
private T getGenericProperty(String key, T defaultValue, Class clazz) {
if (key == null) {
return defaultValue;
} else if (clazz.isArray()) {
if (String[].class.equals(clazz)) {
return (T) get(key);
}
return key == null ? defaultValue : getFactory().getBindingHelper().convert(get(key), clazz);
} else {
if (String.class.equals(clazz)) {
return (T) getFirst(key);
}
return key == null ? defaultValue : getFactory().getBindingHelper().convert(getFirst(key), clazz);
}
}
@Override
public Date getDateProperty(Date defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : getFactory().getBindingHelper().convert(getFirst(key), Date.class);
}
@Override
public Date[] getDateProperties(Date[] defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : AbstractBindingHelperDecorator.parseDateValues(get(key));
}
private final Collection unused = new HashSet<>(25);
@Override
public Collection getUnusedKeys() {
for (Iterator it = unused.iterator(); it.hasNext();) {
if (!containsKey(it.next())) {
it.remove();
}
}
return Collections.unmodifiableCollection(unused);
}
private final Set notPresent = new HashSet<>(25);
@Override
public Collection getKeysNotPresent() {
return Collections.unmodifiableCollection(notPresent);
}
@Override
protected void finalize() throws Throwable {
log.warning("not used, possibly obsolete settings in: " + this + ": " + getUnusedKeys());
super.finalize();
}
@Override
public String toString() {
return "Settings{" + "id=" + id + ", decorators=" + decorators + '}';
}
/**
*
* @param defaultValue the value of defaultValue
* @param keys the value of keys
* @return
* @throws ClassNotFoundException
*/
@Override
public Class getClassProperty(Class defaultValue, String... keys) throws ClassNotFoundException {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : getFactory().getBindingHelper().convert(getFirst(key), Class.class);
}
@Override
public Pattern getRegexProperty(Pattern defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : getFactory().getBindingHelper().convert(getFirst(key), Pattern.class);
}
@Override
public Pattern[] getRegexProperties(Pattern[] defaultValue, String... keys) {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : AbstractBindingHelperDecorator.parseRegexValues(get(key));
}
/**
*
* @param defaultValue the value of defaultValue
* @param keys the value of keys
* @return
* @throws ClassNotFoundException
*/
@Override
public Class[] getClassProperties(Class[] defaultValue, String... keys) throws ClassNotFoundException {
String key = getKey(defaultValue, keys);
return key == null ? defaultValue : AbstractBindingHelperDecorator.parseClassValues(get(key));
}
@Override
public List> getDecorators() {
return decorators;
}
@Override
public void addDecorator(Class extends AbstractPropertiesDecorator> clazz) {
decorators.add(clazz);
}
@Override
public AbstractPropertiesDecorator getOutermostDecorator() {
return outermostDecorator;
}
@Override
public void setOutermostDecorator(AbstractPropertiesDecorator outermostDecorator) {
this.outermostDecorator = outermostDecorator;
log.warning(String.format("NB! Settings wrapped by %s, you should use this instead of %s", outermostDecorator.getClass().getName(),
getClass().getName()));
}
@Override
public String[] put(String key, String value) {
return put(key, new String[]{value});
}
@Override
public boolean containsValue(Object value) {
if (value == null) {
return backingMap.containsValue(null);
}
if (String[].class.equals(value.getClass())) {
if (entrySet().stream().anyMatch((e) -> (Arrays.equals(e.getValue(), (String[]) value)))) {
return true;
}
}
return false;
}
private EnhancedMapBindingFactory getFactory() {
return SettingsBindingService.getInstance().getFactory();
}
@Override
public int size() {
return backingMap.size();
}
@Override
public boolean isEmpty() {
return backingMap.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return backingMap.containsKey(key);
}
@Override
public void putAll(Map extends String, ? extends String[]> m) {
m.forEach((k, v) -> {
if (!backingMap.containsKey(k)) unused.add(k);
});
backingMap.putAll(m);
}
@Override
public void put(Map m) {
m.forEach((k, v) -> {
put(k, v);
});
}
@Override
public Set keySet() {
return backingMap.keySet();
}
@Override
public Collection values() {
return backingMap.values();
}
@Override
public Set> entrySet() {
return backingMap.entrySet();
}
}