/* * 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.task.map; import com.addthis.bundle.core.Bundle; import com.addthis.bundle.util.AutoField; import com.addthis.bundle.util.CachingField; import com.addthis.bundle.util.FullAutoField; import com.addthis.bundle.value.ValueObject; import com.addthis.hydra.data.filter.value.ValueFilter; import com.fasterxml.jackson.annotation.JsonProperty; /** * This section specifies how fields of the input source are transformed into a mapped bundle. * <p/> * <p>Fields are moved from a specified field in the job {@link StreamMapper#source source} * to a destination field in the mapped bundle. By default null values are not written into * the mapped bundle. This behavior can be changed by setting the toNull field to true.</p> * <p/> * <p>Example:</p> * <pre>fields:[ * "TIME" * "SOURCE" * {from:"INITIAL_NAME", to:"NEW_NAME"} * ]</pre> * * @user-reference */ public final class FieldFilter { /** The name of the bundle field source. This is required. */ final AutoField from; /** The name of the bundle field destination. */ final AutoField to; /** Optionally apply a filter onto the field. */ @JsonProperty ValueFilter filter; /** If true then emit null values to the destination field. The default is false. */ @JsonProperty boolean toNull; // Default constructor required for codec deserialization @SuppressWarnings("unused") private FieldFilter(@JsonProperty(value = "from", required = true) AutoField from, @JsonProperty(value = "to") AutoField to) { this.from = from; if (to == null) { this.to = cloneFrom(from); } else { this.to = to; } } // can't find a good way to copy the json value for "from", copy constructors fail for abstract types, // and clone has its own host of problems. We could just re-use the same object, but that would be even // more wasteful than the caching we perform for unchanging formats. This is a decent stop-gap solution. private static AutoField cloneFrom(AutoField from) { if (from instanceof FullAutoField) { return new FullAutoField(((FullAutoField) from).baseAutoField, ((FullAutoField) from).subNames); } else if (from instanceof CachingField) { return new CachingField(((CachingField) from).name); } else { throw new IllegalArgumentException("can not use implicit relation (from = to) for AutoField type: " + from.getClass()); } } public FieldFilter(String copyFieldName) { this.from = AutoField.newAutoField(copyFieldName); this.to = AutoField.newAutoField(copyFieldName); } public void mapField(Bundle in, Bundle out) { ValueObject inVal = from.getValue(in); if (filter != null) { inVal = filter.filter(inVal, in); } if (inVal != null || toNull) { to.setValue(out, inVal); } } }