/*
* 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.
*/
package com.addthis.hydra.data.filter.value;
import com.addthis.bundle.value.ValueFactory;
import com.addthis.bundle.value.ValueMap;
import com.addthis.bundle.value.ValueMapEntry;
import com.addthis.bundle.value.ValueObject;
import com.addthis.bundle.value.ValueString;
import com.addthis.bundle.value.ValueTranslationException;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This {@link AbstractValueFilter ValueFilter} <span class="hydra-summary">accepts a map as input and then performs
* filtering operations on the input</span>.
* <p/>
* <p>If the {@link #whitelist whitelist} field is non-null, then any keys outside
* of the whitelist are discarded. If the {@link #blacklist blacklist} field is non-null,
* then any keys that are in the blacklist are discarded. By default a map is returned
* as output. The {@link #toString toString} field can be used to emit a string as output.
* <p/>
* <p>Example:</p>
* <pre>
* // assume PARAM="k1=v1,k2=v2,k3=v3"
*
* {from:"PARAM", split {split:"=", keySplit:","}}
*
* // this will give you the subset of the map
* // returns MAP[k1 -> v1, k2 -> v2 ]
*
* {from:"PARAM", to:"MAP", map-subset.whitelist:["k1,"k2"]}
*
* // this will give you the subset of the map as a string
* // returns "k1=v1,k2=v2"
*
* {from:"PARAM", to:"MAP", map-subset {whitelist:["k1","k2"], toString:true, keySep:"=", valueSep:","}}
* </pre>
*
* @user-reference
*/
public class ValueFilterMapSubset extends AbstractValueFilter {
private static final Logger log = LoggerFactory.getLogger(ValueFilterMapSubset.class);
/** Set of keys that are preserved by the filter. */
@JsonProperty private String[] whitelist;
/** Set of keys that are excluded by the filter. */
@JsonProperty private String[] blacklist;
/** If true, then convert the output to a string. Default is false. */
@JsonProperty private boolean toString;
/** If toString is true, then use this field as the delimiter between a key and a value. */
@JsonProperty private String keySep = "=";
/** If toString is true, then use this field as the deliminator between two (key,value) pairs. */
@JsonProperty private String valueSep = ",";
@Override
public ValueObject filterValue(ValueObject value) {
if (value == null) {
return null;
}
ValueMap map;
try {
map = value.asMap();
} catch (ValueTranslationException vte) {
log.warn("Error extracting map from value: {}", value);
return null;
}
ValueMap subsetMap;
if ((whitelist != null) && (whitelist.length > 0)) {
subsetMap = ValueFactory.createMap();
for (String key : whitelist) {
ValueObject valueObject = map.get(key);
if (valueObject != null) {
subsetMap.put(key, valueObject);
}
}
} else {
subsetMap = map;
}
if ((blacklist != null) && (blacklist.length > 0)) {
for (String key : blacklist) {
subsetMap.remove(key);
}
}
if (toString) {
return toString(subsetMap);
} else {
return subsetMap;
}
}
private ValueString toString(ValueMap subsetMap) {
StringBuilder sb = new StringBuilder();
for (ValueMapEntry valueMapEntry : subsetMap) {
if (sb.length() == 0) {
sb.append(valueMapEntry.getKey())
.append(keySep)
.append(valueMapEntry.getValue());
} else {
sb.append(valueSep)
.append(valueMapEntry.getKey())
.append(keySep)
.append(valueMapEntry.getValue());
}
}
return ValueFactory.create(sb.toString());
}
}