All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.streamsets.pipeline.api.Field Maven / Gradle / Ivy

There is a newer version: 5.12.0
Show newest version
/*
 * Copyright 2017 StreamSets Inc.
 *
 * 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.streamsets.pipeline.api;

import com.streamsets.pipeline.api.impl.BooleanTypeSupport;
import com.streamsets.pipeline.api.impl.ByteArrayTypeSupport;
import com.streamsets.pipeline.api.impl.ByteTypeSupport;
import com.streamsets.pipeline.api.impl.CharTypeSupport;
import com.streamsets.pipeline.api.impl.CreateByRef;
import com.streamsets.pipeline.api.impl.DateTypeSupport;
import com.streamsets.pipeline.api.impl.DecimalTypeSupport;
import com.streamsets.pipeline.api.impl.DoubleTypeSupport;
import com.streamsets.pipeline.api.impl.FloatTypeSupport;
import com.streamsets.pipeline.api.impl.IntegerTypeSupport;
import com.streamsets.pipeline.api.impl.ListMapTypeSupport;
import com.streamsets.pipeline.api.impl.ListTypeSupport;
import com.streamsets.pipeline.api.impl.LongTypeSupport;
import com.streamsets.pipeline.api.impl.MapTypeSupport;
import com.streamsets.pipeline.api.impl.FileRefTypeSupport;
import com.streamsets.pipeline.api.impl.ShortTypeSupport;
import com.streamsets.pipeline.api.impl.StringTypeSupport;
import com.streamsets.pipeline.api.impl.TypeSupport;
import com.streamsets.pipeline.api.impl.Utils;
import com.streamsets.pipeline.api.impl.ZonedDateTimeTypeSupport;

import java.math.BigDecimal;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * A Field is the basic building block to represent data within Data Collector stages. Stages receive and
 * produce batches of {@link Record}s, a Record as a Field to hold its data.
 * 

* A Field is a data structure construct consisting of a type/value pair. It supports basic types * (i.e. numbers, strings, dates) as well as structure oriented types (i.e. Maps and Lists). *

* Field values can be automatically converted to compatible Java primitive types and classes. * Field values can be NULL (while preserving the type). *

* The {@link Type} enumeration defines all the supported types. *

* Except for the Collection based types, Field values are immutable. Collection Field values * can be modified using the corresponding Collection manipulation API. *

* NOTE: Java Date and byte[] are not immutable. Field makes immutable * by performing a copy on create() and on get(). This means that if a Date or * byte[] instance obtained from a Field is modified, the actual value stored in the * Field is not modified. *

* The {@link #hashCode}, {@link #equals} and {@link #clone} methods work in deep operation mode on the * Field. * * @see Record */ public class Field implements Cloneable { private Type type; private Object value; private Map attributes; /** * Enum defining all Field types. */ public enum Type { BOOLEAN(new BooleanTypeSupport()), CHAR(new CharTypeSupport()), BYTE(new ByteTypeSupport()), SHORT(new ShortTypeSupport()), INTEGER(new IntegerTypeSupport()), LONG(new LongTypeSupport()), FLOAT(new FloatTypeSupport()), DOUBLE(new DoubleTypeSupport()), DATE(new DateTypeSupport()), DATETIME(new DateTypeSupport()), TIME(new DateTypeSupport()), DECIMAL(new DecimalTypeSupport()), STRING(new StringTypeSupport()), FILE_REF(new FileRefTypeSupport()), BYTE_ARRAY(new ByteArrayTypeSupport()), MAP(new MapTypeSupport()), LIST(new ListTypeSupport()), LIST_MAP(new ListMapTypeSupport()), ZONED_DATETIME(new ZonedDateTimeTypeSupport()), ; final transient TypeSupport supporter; Type(TypeSupport supporter) { this.supporter = supporter; } private Object convert(Object value) { return (value != null) ? supporter.convert(value) : null; } private Object convert(Object value, Type targetType) { return (value != null) ? supporter.convert(value, targetType.supporter) : null; } private boolean equals(Object value1, Object value2) { return supporter.equals(value1, value2); } @SuppressWarnings("unchecked") private T constructorCopy(T value) { return (value != null) ? (T) supporter.create(value) : null; } @SuppressWarnings("unchecked") private T getReference(T value) { return (value != null) ? (T) supporter.get(value) : null; } private String toString(Object value) { // ZonedDateTime.toString() should not be used as it requires offset and zone id to match (which does not during // DST changes. So make sure we use the same mechanism as converting it to a STRING field, which is always // deterministic. return Utils.format( "Field[{}:{}]", this, this.supporter.getClass().equals(ZonedDateTimeTypeSupport.class) ? Utils.format((ZonedDateTime) value) : value ); } public boolean isOneOf(Type ...types) { if(types == null) { return false; } for(Type t : types) { if(this == t) return true; } return false; } } // need default constructor for deserialization purposes (Kryo) private Field() { } private Field(Type type, Object value) { this(type, value, null); } private Field(Type type, Object value, Map attributes) { this.type = type; this.value = CreateByRef.isByRef() ? value : type.constructorCopy(value); if (attributes != null) { this.attributes = new LinkedHashMap<>(attributes); } } /** * @deprecated DO NOT USE, TO BE REMOVED */ @Deprecated public static Field create(Field field, T value) { return create(Utils.checkNotNull(field, "field").getType(), value); } /** * Creates a BOOLEAN field. * * @param v value. * * @return a boolean Field with the given value. */ public static Field create(boolean v) { return new Field(Type.BOOLEAN, v); } /** * Creates a CHAR field. * * @param v value. * * @return a char Field with the given value. */ public static Field create(char v) { return new Field(Type.CHAR, v); } /** * Creates a BYTE field. * * @param v value. * * @return a byte Field with the given value. */ public static Field create(byte v) { return new Field(Type.BYTE, v); } /** * Creates a SHORT field. * * @param v value. * * @return a short Field with the given value. */ public static Field create(short v) { return new Field(Type.SHORT, v); } /** * Creates a INTEGER field. * * @param v value. * * @return a int Field with the given value. */ public static Field create(int v) { return new Field(Type.INTEGER, v); } /** * Creates a LONG field. * * @param v value. * * @return a long Field with the given value. */ public static Field create(long v) { return new Field(Type.LONG, v); } /** * Creates a FLOAT field. * * @param v value. * * @return a float Field with the given value. */ public static Field create(float v) { return new Field(Type.FLOAT, v); } /** * Creates a DOUBLE field. * * @param v value. * * @return a double Field with the given value. */ public static Field create(double v) { return new Field(Type.DOUBLE, v); } /** * Creates a DECIMAL Field. * * @param v value. * * @return a BigDecimal Field with the given value. */ public static Field create(BigDecimal v) { return new Field(Type.DECIMAL, v); } /** * Creates a STRING field. * * @param v value. * * @return a String Field with the given value. */ public static Field create(String v) { return new Field(Type.STRING, v); } /** * Creates a BYTE_ARRAY field. * * @param v value. * * @return a byte array Field with the given value. */ public static Field create(byte[] v) { return new Field(Type.BYTE_ARRAY, v); } /** * Creates a DATE field. * * @param v value. * * @return a Date Field with the given value. */ public static Field createDate(Date v) { return new Field(Type.DATE, v); } /** * Creates a DATETIME field. *

* Java does not have Datetime field, the intent of the {@link Type#DATETIME} type is to use only the year, month * and day information of the Date. * * @param v value. * * @return a Datetime Field with the given value. */ public static Field createDatetime(Date v) { return new Field(Type.DATETIME, v); } /** * Creates a TIME field. *

* Java does not have Time field, the intent of the {@link Type#DATETIME} type is to use only the hour, minute, * and second information of the Date. * * @param v value. * * @return a Time Field with the given value. */ public static Field createTime(Date v) { return new Field(Type.TIME, v); } /** * Creates a ZONED_DATETIME field. * * @param v value. * * @return a Zoned Datetime Field with the given value. */ public static Field createZonedDateTime(ZonedDateTime v) { return new Field(Type.ZONED_DATETIME, v); } /** * Creates a LIST field. The keys of the Map must be of String type and the values must be * of Field type, NULL values are not allowed as key or values. *

* This method performs a deep copy of the given Map. * * @param v value. * * @return a Map Field with the given value. */ public static Field create(Map v) { return new Field(Type.MAP, v); } /** * Creates a LIST field. The values of the List must be of Field type, NULL * values are not allowed. *

* This method performs a deep copy of the given List. *

* * @param v value. * * @return a List Field with the given value. */ public static Field create(List v) { return new Field(Type.LIST, v); } /** * Creates a LIST_MAP field. The keys of the ordered Map must be of String type and the * values must be of Field type, NULL values are not allowed as key or values. *

* This method performs a deep copy of the ordered Map. * * @param v value. * * @return a List-Map Field with the given value. */ public static Field createListMap(LinkedHashMap v) { return new Field(Type.LIST_MAP, v); } /** * Creates a FILE_REF field. * * @param v value. * * @return a FILE_REF Field with the given value. */ public static Field create(FileRef v) { return new Field(Type.FILE_REF, v); } /** * Creates a field of a given type, the value is converted to the specified type. The attributes are set to null. *

* If the type is MAP, LIST or LIST_MAP this method performs a deep copy of * the value. * * @param type the type of the field to create. * @param value the value to set in the field. * @return the created Field. * @throws IllegalArgumentException if the value cannot be converted to the specified type. */ public static Field create(Type type, T value) { return create(type, value, null); } /** * Creates a field of a given type, the value is converted to the specified type. *

* If the type is MAP, LIST or LIST_MAP this method performs a deep copy of * the value. * * @param type the type of the field to create. * @param value the value to set in the field. * @param attributes the field-level attributes (can be null) * @return the created Field. * @throws IllegalArgumentException if the value cannot be converted to the specified type. */ public static Field create(Type type, T value, Map attributes) { return new Field(Utils.checkNotNull(type, "type"), type.convert(value), attributes); } /** * Returns the type of the field. * * @return the type of the field. */ public Type getType() { return type; } /** * Returns the value of the field. *

* For fields with DATE, DATETIME or BYTE_ARRAY type, a copy of the value is * returned. If the returned instance is modified, the changes are not reflected in the field value itself. *

* For fields with a collection type (MAP, LIST and LIST_MAP), a reference to * the value is returned, any changes to the returned collection object will be reflected in the field value itself. *

* For all other types, the values are immutable. *

* @return the value of the field. */ public Object getValue() { return type.getReference(value); } /** * Returns the boolean value of the field. * * @return the boolean value of the field. * @throws IllegalArgumentException if the value cannot be converted to boolean. */ public boolean getValueAsBoolean() { return (boolean) type.convert(getValue(), Type.BOOLEAN); } /** * Returns the char value of the field. * * @return the char value of the field. * @throws IllegalArgumentException if the value cannot be converted to char. */ public char getValueAsChar() { return (char) type.convert(getValue(), Type.CHAR); } /** * Returns the byte value of the field. * * @return the byte value of the field. * @throws IllegalArgumentException if the value cannot be converted to byte. */ public byte getValueAsByte() { return (byte) type.convert(getValue(), Type.BYTE); } /** * Returns the short value of the field. * * @return the short value of the field. * @throws IllegalArgumentException if the value cannot be converted to short. */ public short getValueAsShort() { return (short) type.convert(getValue(), Type.SHORT); } /** * Returns the int value of the field. * * @return the int value of the field. * @throws IllegalArgumentException if the value cannot be converted to int. */ public int getValueAsInteger() { return (int) type.convert(getValue(), Type.INTEGER); } /** * Returns the long value of the field. * * @return the long value of the field. * @throws IllegalArgumentException if the value cannot be converted to long. */ public long getValueAsLong() { return (long) type.convert(getValue(), Type.LONG); } /** * Returns the float value of the field. * * @return the float value of the field. * @throws IllegalArgumentException if the value cannot be converted to float. */ public float getValueAsFloat() { return (float) type.convert(getValue(), Type.FLOAT); } /** * Returns the double value of the field. * * @return the double value of the field. * @throws IllegalArgumentException if the value cannot be converted to double. */ public double getValueAsDouble() { return (double) type.convert(getValue(), Type.DOUBLE); } /** * Returns the Date value of the field. * * @return the Date value of the field. It returns a copy of the value. * @throws IllegalArgumentException if the value cannot be converted to Date. */ public Date getValueAsDate() { return (Date) type.convert(getValue(), Type.DATE); } /** * Returns the Date value of the field. * * @return the Date value of the field. it returns a copy of the value. * @throws IllegalArgumentException if the value cannot be converted to Date. */ public Date getValueAsDatetime() { return (Date) type.convert(getValue(), Type.DATE); } /** * Returns the Date value of the field. * * @return the Date value of the field. it returns a copy of the value. * @throws IllegalArgumentException if the value cannot be converted to Date. */ public Date getValueAsTime() { return (Date) type.convert(getValue(), Type.TIME); } /** * Returns value of the Field as a {@linkplain ZonedDateTime}. * * @return Value of the field as a {@linkplain ZonedDateTime}. * @throws IllegalArgumentException if the value cannot be converted to ZonedDateTime. */ public ZonedDateTime getValueAsZonedDateTime() { return (ZonedDateTime) type.convert(getValue(), Type.ZONED_DATETIME); } /** * Returns the BigDecimal value of the field. * * @return the BigDecimal value of the field. * @throws IllegalArgumentException if the value cannot be converted to BigDecimal. */ public BigDecimal getValueAsDecimal() { return (BigDecimal) type.convert(getValue(), Type.DECIMAL); } /** * Returns the String value of the field. * * @return the String value of the field. * @throws IllegalArgumentException if the value cannot be converted to String. */ public String getValueAsString() { return (String) type.convert(getValue(), Type.STRING); } /** * Returns the byte array value of the field. * * @return the byte array value of the field. It returns a copy of the value. * @throws IllegalArgumentException if the value cannot be converted to byte array. */ public byte[] getValueAsByteArray() { return (byte[]) type.convert(getValue(), Type.BYTE_ARRAY); } /** * Returns the Map value of the field. * * @return the Map value of the field. It returns a reference of the value both for MAP and * LIST_MAP. * @throws IllegalArgumentException if the value cannot be converted to Map. */ @SuppressWarnings("unchecked") public Map getValueAsMap() { return (Map) type.convert(getValue(), Type.MAP); } /** * Returns the List value of the field. * * @return the List value of the field. It returns a reference of the value if the type is LIST, if * the type is LIST_MAP it returns a copy of the value. * @throws IllegalArgumentException if the value cannot be converted to List. */ @SuppressWarnings("unchecked") public List getValueAsList() { return (List) type.convert(getValue(), Type.LIST); } /** * Returns the ordered Map value of the field. * * @return the ordered Map value of the field. It returns a reference of the value. * @throws IllegalArgumentException if the value cannot be converted to ordered Map. */ @SuppressWarnings("unchecked") public LinkedHashMap getValueAsListMap() { return (LinkedHashMap) type.convert(getValue(), Type.LIST_MAP); } /** * Returns the {@link FileRef} value of the field. * * @return the {@link FileRef} value of the field. * @throws IllegalArgumentException if the value cannot be converted to {@link FileRef}. */ @SuppressWarnings("unchecked") public FileRef getValueAsFileRef() { return (FileRef) type.convert(getValue(), Type.FILE_REF); } /** * Returns the list of user defined attribute names. * * @return the list of user defined attribute names, if there are none it returns an empty set. */ public Set getAttributeNames() { if (attributes == null) { return Collections.emptySet(); } else { return Collections.unmodifiableSet(attributes.keySet()); } } /** * Returns the value of the specified attribute. * * @param name attribute name. * @return the value of the specified attribute, or NULL if the attribute does not exist. */ public String getAttribute(String name) { if (attributes == null) { return null; } else { return attributes.get(name); } } /** * Sets an attribute. * * @param name attribute name. * @param value attribute value, it cannot be NULL. */ public void setAttribute(String name, String value) { if (attributes == null) { attributes = new HashMap<>(); } attributes.put(name, value); } /** * Sets all attributes from the given map. * * The performed operation is "upsert" - if the given map contains an attribute that does not exist yet, it will * be crated. If the attribute exists already, the value will be updated. All existing attributes that are not part * of the new map will remain untouched. * * @param attributes */ public void setAttributes(Map attributes) { if(this.attributes == null) { this.attributes = new HashMap<>(attributes); } else { this.attributes.putAll(attributes); } } /** * Deletes an attribute. * * @param name the attribute to delete. */ public void deleteAttribute(String name) { if (attributes == null) { return; } else { attributes.remove(name); } } /** * Get all field attributes in an unmodifiable Map, or null if no attributes have been added * * @return all field attributes, or NULL if none exist */ public Map getAttributes() { if (attributes == null) { return null; } else { return Collections.unmodifiableMap(attributes); } } /** * Returns the string representation of the field. * * @return the string representation of the field. */ @Override public String toString() { return type.toString(value); } /** * Returns the hashcode of the field. *

* The hashcode is value based and it is computed in a deep fashion. * @return the hashcode of the field. */ @Override public int hashCode() { return (value != null) ? value.hashCode() : 0; } /** * Compares if the field is equal to another field. *

* The comparison is done in a deep fashion. * @return if the field is equal to another field. */ @Override public boolean equals(Object obj) { boolean eq = false; if (obj != null && obj instanceof Field) { Field other = (Field) obj; if (type == other.type) { eq = (value == other.value) || type.equals(value, other.value); } if (attributes == null) { eq &= other.attributes == null; } else { eq &= attributes.equals(other.attributes); } } return eq; } /** *

* Returns a clone of the field. *

* * @return a clone of the field (deep copy). */ @Override public Field clone() { return new Field(type, value, attributes); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy