/* * 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 javax.annotation.Nullable; import java.util.HashMap; import com.addthis.bundle.core.Bundle; import com.addthis.bundle.util.AutoField; import com.addthis.bundle.util.AutoParam; import com.addthis.bundle.util.ConstantField; import com.addthis.bundle.value.ValueFactory; import com.addthis.bundle.value.ValueMap; import com.addthis.bundle.value.ValueObject; import com.addthis.codec.codables.SuperCodable; import com.addthis.hydra.data.util.JSONFetcher; import com.google.common.annotations.VisibleForTesting; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonProperty; /** * This {@link AbstractValueFilter ValueFilter} <span class="hydra-summary">uses the input as a key and returns the value * associated with the input in a specified map</span>. * <p/> * <p>The map is specified with the {@link #map map} field. Alternatively a JSON map * can be fetched from the internet using the {@link #mapURL mapURL} field. When no value is associated * with the input the default behavior is to return the input value. This behavior * can be changed to return null using the {@link #toNull toNull} field or to return * a default value using the {@link #defaultValue defaultValue} field.</p> * <p/> * <p>Example:</p> * <pre> * // english to french map * {map: {"hello":"bonjour", "I":"Je", "computer" "ordinateur"}} * </pre> * * @user-reference */ @JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE) public class ValueFilterMap extends AbstractValueFilterContextual implements SuperCodable { /** The map used to search for the input key. */ @AutoParam private AutoField map; /** If true, then the filter returns null when no value is associated with the input. Default is false. */ @JsonProperty private boolean toNull; /** If nonnull, then the filter returns defaultValue when no value is associated with the input. Default is null. */ @JsonProperty private ValueObject defaultValue; /** Fetch the map from this URL. */ @JsonProperty private String mapURL; /** The timeout value when mapURL is used. */ @JsonProperty private int httpTimeout = 60000; @VisibleForTesting ValueFilterMap() {} public ValueFilterMap setMap(HashMap<String, String> map) { ValueMap newMap = ValueFactory.createMap(); map.forEach((k, v) -> { newMap.put(k, ValueFactory.create(v)); }); this.map = new ConstantField(newMap); return this; } public ValueFilterMap setToNull(boolean toNull) { this.toNull = toNull; return this; } public ValueFilterMap setMapURL(String url) { mapURL = url; return this; } public ValueFilterMap setHttpTimeout(int tmout) { httpTimeout = tmout; return this; } @Override public void postDecode() { if (map == null && mapURL != null) { setMap(new JSONFetcher(httpTimeout).loadMap(mapURL)); } } @Override public void preEncode() {} private static final class ValidationOnly extends ValueFilterMap { @Override public void postDecode() { // intentionally do nothing } @Nullable @Override public ValueObject filterValue(@Nullable ValueObject value, @Nullable Bundle context) { throw new UnsupportedOperationException("This class is only intended for use in construction validation."); } } @Nullable @Override public ValueObject filterValue(@Nullable ValueObject value, @Nullable Bundle context) { if (value != null) { if (map == null) { value = defaultValue != null ? defaultValue : toNull ? null : value; } else { ValueObject v = map.getValue(context).asMap().get(value.toString()); if (v != null) { value = v; } else if (defaultValue != null) { value = defaultValue; } else if (toNull) { value = null; } } return value; } else { return value; } } }