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

org.hpccsystems.commons.ecl.FieldDef Maven / Gradle / Ivy

/*******************************************************************************
 * HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
 *
 * 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 org.hpccsystems.commons.ecl;

import java.io.Serializable;
import java.util.Iterator;

/**
 * The name and field type for an item from the HPCC environment. The types may be single scalar types or may be arrays
 * or structures.
 *
 */

public class FieldDef implements Serializable
{
    private static final long serialVersionUID   = 1L;
    private static final int  MASK_32_LOWER_HALF = 0xffff;
    private static final int  MASK_32_UPPER_HALF = 0xffff0000;

    private String            fieldName          = "";
    private FieldType         fieldType          = FieldType.UNKNOWN;
    private String            typeName           = FieldType.UNKNOWN.description();
    private FieldDef[]        defs               = new FieldDef[0];
    private HpccSrcType       srcType            = HpccSrcType.UNKNOWN;
    private long              len                = 0;
    private boolean           fixedLength        = false;
    private boolean           isUnsigned         = false;
    private boolean           isBiased           = false;
    private int               additionalFlags    = 0;

    /**
     * Instantiates a new field def.
     *
     * @param rhs
     *            FieldDef to be copied
     */
    public FieldDef(FieldDef rhs)
    {
        this.fieldName = rhs.fieldName;
        this.fieldType = rhs.fieldType;
        this.typeName = rhs.typeName;
        this.defs = rhs.defs;
        this.srcType = rhs.srcType;
        this.len = rhs.len;
        this.fixedLength = rhs.fixedLength;
        this.isUnsigned = rhs.isUnsigned;
        this.isBiased = rhs.isBiased;
        this.additionalFlags = rhs.additionalFlags;
    }

    /**
     * Instantiates a new field def.
     *
     * @param fieldName
     *            the name of the field
     * @param fieldType
     *            the FieldType value
     * @param typeName
     *            the name of this composite type
     * @param len
     *            the field length
     * @param isFixedLength
     *            len may be non-zero and variable
     * @param isUnsigned
     *            Only applies to integers and decimal fields. Ignored otherwise
     * @param sourceType
     *            the source type
     * @param childDefs
     *            Child field defs. Only used for Records, Sets and Datasets. Sets and Dataset should have a single child. Null otherwise.
     */
    public FieldDef(String fieldName, FieldType fieldType, String typeName, long len, boolean isFixedLength, boolean isUnsigned,
            HpccSrcType sourceType, FieldDef[] childDefs)
    {
        this(fieldName, fieldType, typeName, len, isFixedLength, isUnsigned, 0, sourceType, childDefs);
    }

    /**
     * Instantiates a new field def.
     *
     * @param fieldName
     *            the name of the field
     * @param fieldType
     *            the FieldType value
     * @param typeName
     *            the name of this composite type
     * @param len
     *            the field length
     * @param isFixedLength
     *            len may be non-zero and variable
     * @param isUnsigned
     *            Only applies to integers and decimal fields. Ignored otherwise
     * @param additionalFlags
     *            Additional flags. Primarily used to retain layout information in HPCC records during conversion.
     * @param sourceType
     *            the source type
     * @param childDefs
     *            Child field defs. Only used for Records, Sets and Datasets. Sets and Dataset should have a single child. Null otherwise.
     */
    public FieldDef(String fieldName, FieldType fieldType, String typeName, long len, boolean isFixedLength, boolean isUnsigned, int additionalFlags,
            HpccSrcType sourceType, FieldDef[] childDefs)
    {
        this.fieldName = fieldName;
        this.fieldType = fieldType;
        this.typeName = typeName;
        this.additionalFlags = additionalFlags;
        this.defs = childDefs;
        if (this.defs == null)
        {
            this.defs = new FieldDef[0];
        }

        this.srcType = sourceType;
        this.fixedLength = isFixedLength;
        this.isUnsigned = isUnsigned;
        this.len = len;

        if (this.fieldType == FieldType.VAR_STRING && (sourceType.isUTF16() == false && sourceType != HpccSrcType.SINGLE_BYTE_CHAR))
        {
            throw new IllegalArgumentException("Invalid field defintion for: " + fieldName + "VarStrings must be encoded in either UTF16 or ASCII");
        }

    }

    /**
     * the name of the field.
     *
     * @return the name
     */
    public String getFieldName()
    {
        return fieldName;
    }

    /**
     * Sets the field name.
     *
     * @param newFieldName
     *            the new field name
     */
    public void setFieldName(String newFieldName)
    {
        this.fieldName = newFieldName;
    }

    /**
     * the type of the field using the FieldType ENUM type.
     *
     * @return the type as an enumeration value
     */
    public FieldType getFieldType()
    {
        return fieldType;
    }

    /**
     * Data type on the HPCC cluster.
     *
     * @return type enumeration
     */
    public HpccSrcType getSourceType()
    {
        return this.srcType;
    }

    /**
     * Length of data or minimum length if variable.
     *
     * @param dataLen
     *            the new data len
     */
    public void setDataLen(long dataLen)
    {
        this.len = dataLen;
    }

    /**
     * Length of the data or minimum length if variable.
     *
     * @return length
     */
    public long getDataLen()
    {
        if (this.fieldType != FieldType.DECIMAL)
        {
            return this.len;
        }

        if (this.isUnsigned())
        {
            int precision = (int) (this.len & MASK_32_LOWER_HALF);
            int dataLen = (precision + 1) / 2;
            return dataLen;
        }
        else
        {
            int precision = (int) (this.len & MASK_32_LOWER_HALF);
            int dataLen = (precision + 2) / 2;
            return dataLen;
        }
    }

    /**
     * Returns precision for decimal fields. Return 0 if this field is not a decimal
     *
     * @return precision
     */
    public int getPrecision()
    {
        if (this.fieldType != FieldType.DECIMAL)
        {
            return 0;
        }

        return (int) (this.len & MASK_32_LOWER_HALF);
    }

    /**
     * Sets precision for decimal fields.
     *
     * @param precision
     *            the new precision
     */
    public void setPrecision(int precision)
    {
        if (this.fieldType != FieldType.DECIMAL)
        {
            return;
        }

        if (precision > 64)
        {
            precision = 64;
        }

        this.len &= MASK_32_UPPER_HALF;
        this.len |= (precision & MASK_32_LOWER_HALF);
    }

    /**
     * Returns scale for decimal fields. Return 0 if this field is not a decimal
     *
     * @return scale
     */
    public int getScale()
    {
        if (this.fieldType != FieldType.DECIMAL)
        {
            return 0;
        }

        int scale = (int) (this.len >> 16);
        return scale;
    }

    /**
     * Sets scale for decimal fields.
     *
     * @param scale
     *            the new scale
     */
    public void setScale(int scale)
    {
        if (this.fieldType != FieldType.DECIMAL)
        {
            return;
        }

        if (scale > 32)
        {
            scale = 32;
        }

        this.len &= MASK_32_LOWER_HALF;
        this.len |= (scale << 16);
    }

    /**
     * Fixed or variable length.
     *
     * @return true when fixed length
     */
    public boolean isFixed()
    {
        return this.fixedLength;
    }

    /**
     * Is the underlying value type unsigned?.
     *
     * @return true when unsigned
     */
    public boolean isUnsigned()
    {
        return this.isUnsigned;
    }

    /**
     * Is the underlying value biased?
     *
     * @return true when biased 
     */
    public boolean isBiased()
    {
        return this.isBiased;
    }

    void setIsBiased(boolean biased)
    {
        this.isBiased = biased;
    }

    /**
     * Get the additional flags for this fields.
     *
     * @return the additional flags
     */
    public int getAdditionalFlags()
    {
        return this.additionalFlags;
    }

    /**
     * Set the additional flags for this fields.
     *
     * @param flags
     *            the new additional flags
     */
    public void setAdditionalFlags(int flags)
    {
        this.additionalFlags = flags;
    }

    /**
     * Number of field definitions. Zero if this is not a record
     *
     * @return number
     */
    public int getNumDefs()
    {
        return this.defs.length;
    }

    /**
     * Get the FieldDef at position. Will throw an array out of bounds exception.
     *
     * @param ndx
     *            index position
     * @return the FieldDef object
     */
    public FieldDef getDef(int ndx)
    {
        return this.defs[ndx];
    }

    /**
     * Set the Child FieldDefs.
     *
     * @param childDefs
     *            the new defs
     */
    public void setDefs(FieldDef[] childDefs)
    {
        if (childDefs == null)
        {
            childDefs = new FieldDef[0];
        }

        this.defs = childDefs;
        updateRecordMeta(this);
    }

    public int getDefIndexWithFieldName(String fieldName)
    {
        for (int i = 0; i < this.defs.length; i++)
        {
            if (this.defs[i].getFieldName().equals(fieldName))
            {
                return i;
            }
        }

        return -1;
    }

    /**
     * An iterator to walk though the type definitions that compose this type.
     *
     * @return an iterator returning FieldDef objects
     */
    public Iterator getDefinitions()
    {
        final FieldDef[] defRef = this.defs;
        Iterator rslt = new Iterator() {
            int        pos  = 0;
            FieldDef[] copy = defRef;

            @Override
            public boolean hasNext()
            {
                return (pos < copy.length) ? true : false;
            }

            @Override
            public FieldDef next()
            {
                return copy[pos++];
            }
        };
        return rslt;
    }

    /**
     * Update record meta.
     *
     * @param recordDef
     *            the record def
     */
    private static void updateRecordMeta(FieldDef recordDef)
    {
        updateRecordMinLength(recordDef);
    }

    /**
     * Update record min length.
     *
     * @param recordDef
     *            the record def
     */
    private static void updateRecordMinLength(FieldDef recordDef)
    {
        for (int i = 0; i < recordDef.getNumDefs(); i++)
        {
            FieldDef childDef = recordDef.getDef(i);
            if (childDef.getFieldType() == FieldType.RECORD)
            {
                updateRecordMinLength(childDef);
            }
        }

        long minDataLength = getMinLengthInBytes(recordDef);
        recordDef.setDataLen(minDataLength);
    }

    /**
     * Gets the min length in bytes.
     *
     * @param def
     *            the def
     * @return the min length in bytes
     */
    private static long getMinLengthInBytes(FieldDef def)
    {
        switch (def.getFieldType())
        {
            case RECORD:
            {
                long minDataLength = 0;
                for (int i = 0; i < def.getNumDefs(); i++)
                {
                    FieldDef childDef = def.getDef(i);
                    minDataLength += getMinLengthInBytes(childDef);
                }
                return minDataLength;
            }
            case SET:
            {
                // Sets include 4 byte integer dataLength and an additional byte
                return 5;
            }
            default:
            {
                long dataLength = 0;
                if (def.isFixed())
                {
                    // Var strings can be fixed length
                    dataLength = def.getDataLen();
                    if (def.getFieldType() == FieldType.VAR_STRING)
                    {
                        dataLength++;
                    }

                    // Unicode datalength is in code points not bytes
                    if (def.getSourceType().isUTF16())
                    {
                        dataLength *= 2;
                    }
                }
                else
                {
                    dataLength = 4;
                }
                return dataLength;
            }
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString()
    {
        String output = "";
        for (int fieldIndex = 0; fieldIndex < defs.length; fieldIndex++)
        {
            FieldDef currentFieldDef = defs[fieldIndex];
            long dataLen = currentFieldDef.getDataLen();
            output += currentFieldDef.getFieldName() + "(" + currentFieldDef.getFieldType()
                    + (dataLen > 0 ? " - " + currentFieldDef.getDataLen() : "") + ")";
            if (fieldIndex < defs.length - 1) output += " | ";
        }
        return output;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy