aboutsummaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/search/query/properties/PropertyMap.java
diff options
context:
space:
mode:
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.java132
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;
+ }
+
+}