diff options
Diffstat (limited to 'container-search/src/main/java/com/yahoo/search/query/properties/PropertyMap.java')
-rw-r--r-- | container-search/src/main/java/com/yahoo/search/query/properties/PropertyMap.java | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/container-search/src/main/java/com/yahoo/search/query/properties/PropertyMap.java b/container-search/src/main/java/com/yahoo/search/query/properties/PropertyMap.java new file mode 100644 index 00000000000..820c4fc8ea3 --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/query/properties/PropertyMap.java @@ -0,0 +1,132 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.query.properties; + +import com.yahoo.processing.request.CompoundName; +import com.yahoo.search.query.Properties; +import com.yahoo.search.result.Hit; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.logging.Logger; + +/** + * A Map backing of Properties. + * <p> + * When this is cloned it will deep copy not only the model object map, but also each + * clonable member inside the map. + * <p> + * Subclassing is supported, a hook can be implemented to provide conditional inclusion in the map. + * By default - all properties are accepted, so set is never propagated. + * <p> + * This class is not multithread safe. + * + * @author bratseth + */ +public class PropertyMap extends Properties { + + private static Logger log=Logger.getLogger(PropertyMap.class.getName()); + + /** The properties of this */ + private Map<CompoundName, Object> properties = new LinkedHashMap<>(); + + public void set(CompoundName name, Object value, Map<String,String> context) { + if (shouldSet(name, value)) + properties.put(name, value); + else + super.set(name, value, context); + } + + /** + * Return true if this value should be set in this map, false if the set should be propagated instead + * This default implementation always returns true. + */ + protected boolean shouldSet(CompoundName name,Object value) { return true; } + + public @Override Object get(CompoundName name, Map<String,String> context, + com.yahoo.processing.request.Properties substitution) { + if ( ! properties.containsKey(name)) return super.get(name,context,substitution); + return properties.get(name); + } + + /** + * Returns a direct reference to the map containing the properties set in this instance. + */ + public Map<CompoundName, Object> propertyMap() { + return properties; + } + + public @Override PropertyMap clone() { + PropertyMap clone = (PropertyMap)super.clone(); + clone.properties = new HashMap<>(); + for (Map.Entry<CompoundName, Object> entry : this.properties.entrySet()) { + Object cloneValue = clone(entry.getValue()); + if (cloneValue == null) + cloneValue = entry.getValue(); // Shallow copy objects which does not support cloning + clone.properties.put(entry.getKey(), cloneValue); + } + return clone; + } + + /** Clones this object if it is clonable, and the clone is public. Returns null if not */ + public static Object clone(Object object) { + if (object==null) return null; + if (! ( object instanceof Cloneable) ) return null; + if (object instanceof Object[]) + return arrayClone((Object[])object); + else + return objectClone(object); + } + + private static Object arrayClone(Object[] object) { + Object[] arrayClone= Arrays.copyOf(object, object.length); + // deep clone + for (int i=0; i<arrayClone.length; i++) { + Object elementClone=clone(arrayClone[i]); + if (elementClone!=null) + arrayClone[i]=elementClone; + } + return arrayClone; + } + + private static Object objectClone(Object object) { + if (object instanceof Hit) { + return ((Hit) object).clone(); + } else if (object instanceof LinkedList) { + return ((LinkedList) object).clone(); + } + try { + Method cloneMethod=object.getClass().getMethod("clone"); + return cloneMethod.invoke(object); + } + catch (NoSuchMethodException e) { + log.warning("'" + object + "' is Cloneable, but has no clone method - will use the same instance in all requests"); + return null; + } + catch (IllegalAccessException e) { + log.warning("'" + object + "' is Cloneable, but clone method cannot be accessed - will use the same instance in all requests"); + return null; + } + catch (InvocationTargetException e) { + throw new RuntimeException("Exception cloning '" + object + "'",e); + } + } + + @Override + public Map<String, Object> listProperties(CompoundName path, Map<String, String> context, com.yahoo.processing.request.Properties substitution) { + Map<String, Object> map = super.listProperties(path, context, substitution); + + for (Map.Entry<CompoundName, Object> entry : properties.entrySet()) { + if ( ! entry.getKey().hasPrefix(path)) continue; + CompoundName propertyName = entry.getKey().rest(path.size()); + if (propertyName.isEmpty()) continue; + map.put(propertyName.toString(), entry.getValue()); + } + return map; + } + +} |