/* ### * IP: GHIDRA * * 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 db; import java.io.IOException; import db.buffers.DataBuffer; /** *

Field is an abstract data wrapper for use with Records. * Note that when comparing two Field instances both must be of the same * class.

* *

Fields may take on a null state. In the case of {@link FixedField} * and {@link PrimitiveField} this state is distinct from value and only * applies when used for a sparse column within a {@link SparseRecord}. * In this sparse column situation the {@link SparseRecord#setField(int, Field)} * method may be passed a null Field argument. Sparse columns with a * null value/state will not be indexed within a {@link Table}. * *

Stored Schema Field Type Encoding:

* *

8-bit Legacy Field Type Encoding (I....FFF)

* Supported encodings: 0x00..0x06 and 0x80..0x86, * where: *
 *     FFF  - indexed field type (0..6)
 *     I    - index field indicator (only long primary keys were supported)
 * 
* *

8-bit Field Type Encoding (PPPPFFFF)

* (Reserved for future field extensions: 0x88 and 0xf0..0xff) *
 *     0xff - see {@link Schema#FIELD_EXTENSION_INDICATOR}
 * 
* where: *
 *     FFFF - normal/indexed field type
 *     PPPP - indexed table primary key type (1000b: LegacyIndexField)  
 * 
*/ public abstract class Field implements Comparable { public static final Field[] EMPTY_ARRAY = new Field[0]; /** * Field type for ByteField * @see db.ByteField */ static final byte BYTE_TYPE = 0; /** * Field type for ShortField * @see db.ShortField */ static final byte SHORT_TYPE = 1; /** * Field type for IntField * @see db.IntField */ static final byte INT_TYPE = 2; /** * Field type for LongField * @see db.LongField */ static final byte LONG_TYPE = 3; /** * Field type for StringField * @see db.StringField */ static final byte STRING_TYPE = 4; /** * Field type for BinaryField * @see db.BinaryField */ static final byte BINARY_OBJ_TYPE = 5; /** * Field type for BooleanField * @see db.BooleanField */ static final byte BOOLEAN_TYPE = 6; /** * Field type for 10-byte binary FixedField(10) * @see db.FixedField */ static final byte FIXED_10_TYPE = 7; /** * Legacy Index Primary Key Field type for LongField * which was previously a boolean indicator for an index * field with assumed long primary key. Applies only * to upper-nibble. This value in the lower-nibble * is reserved for use in the special-purpose byte value 0x88. * (see {@link LegacyIndexField}) */ static final byte LEGACY_INDEX_LONG_TYPE = 8; // Available field types (6): 0x9..0xE /** * Reserved field encoding. Intended for special purpose * schema used (e.g. */ static final byte FIELD_RESERVED_15_TYPE = 0xf; /** * Field base type mask */ static final byte FIELD_TYPE_MASK = (byte) 0x0F; /** * Field index primary key type mask */ static final byte INDEX_PRIMARY_KEY_TYPE_MASK = (byte) ~FIELD_TYPE_MASK; /** * Index Primary Key Field Type Shift */ static final int INDEX_FIELD_TYPE_SHIFT = 4; private final boolean immutable; /** * Abstract Field Constructor for a mutable instance */ Field() { immutable = false; } /** * Abstract Field Constructor * @param immutable true if field value is immutable */ Field(boolean immutable) { this.immutable = immutable; } void checkImmutable() { if (immutable) { throw new IllegalFieldAccessException("immutable field instance"); } } /** * Get field as a long value. * All fixed-length field objects must implement this method * @return long value * @throws IllegalFieldAccessException thrown if method is not supported by specific * Field instance. */ public long getLongValue() { throw new IllegalFieldAccessException(); } /** * Set field's long value. * All fixed-length field objects must implement this method * @param value long value * @throws IllegalFieldAccessException thrown if method is not supported by specific * Field instance. */ public void setLongValue(long value) { throw new IllegalFieldAccessException(); } /** * Get field as an integer value. * @return integer value * @throws IllegalFieldAccessException thrown if method is not supported by specific * Field instance. */ public int getIntValue() { throw new IllegalFieldAccessException(); } /** * Set field's integer value. * @param value integer value * @throws IllegalFieldAccessException thrown if method is not supported by specific * Field instance. */ public void setIntValue(int value) { throw new IllegalFieldAccessException(); } /** * Get field as a short value. * @return short value * @throws IllegalFieldAccessException thrown if method is not supported by specific * Field instance. */ public short getShortValue() { throw new IllegalFieldAccessException(); } /** * Set field's short value. * @param value short value * @throws IllegalFieldAccessException thrown if method is not supported by specific * Field instance. */ public void setShortValue(short value) { throw new IllegalFieldAccessException(); } /** * Get field as a byte value. * @return byte value * @throws IllegalFieldAccessException thrown if method is not supported by specific * Field instance. */ public byte getByteValue() { throw new IllegalFieldAccessException(); } /** * Set field's byte value. * @param value byte value * @throws IllegalFieldAccessException thrown if method is not supported by specific * Field instance. */ public void setByteValue(byte value) { throw new IllegalFieldAccessException(); } /** * Get field as a boolean value. * @return boolean value * @throws IllegalFieldAccessException thrown if method is not supported by specific * Field instance. */ public boolean getBooleanValue() { throw new IllegalFieldAccessException(); } /** * Set field's boolean value. * @param value boolean value * @throws IllegalFieldAccessException thrown if method is not supported by specific * Field instance. */ public void setBooleanValue(boolean value) { throw new IllegalFieldAccessException(); } /** * Get data as a byte array. * @return byte[] */ abstract public byte[] getBinaryData(); /** * Set data from binary byte array. * All variable-length fields must implement this method. * @param bytes field data * @throws IllegalFieldAccessException if error occurs while reading bytes * into field which will generally be caused by the incorrect number of * bytes provided to a fixed-length field. */ abstract public void setBinaryData(byte[] bytes); /** * Get field as a String value. * @return String value * @throws IllegalFieldAccessException thrown if method is not supported by specific * Field instance. */ public String getString() { throw new IllegalFieldAccessException(); } /** * Set field's String value. * @param str String value * @throws IllegalFieldAccessException thrown if method is not supported by specific * Field instance. */ public void setString(String str) { throw new IllegalFieldAccessException(); } /** * Truncate a variable length field to the specified length. * If current length is shorterm, this method has no affect. * @param length truncated length */ void truncate(int length) { throw new UnsupportedOperationException("Field may not be truncated"); } /** * @return true if a Field instance is variable length, else false. */ public boolean isVariableLength() { return false; } /** * Determine if specified field is same type as this field * @param field a Field instance * @return true if field is same type as this field */ public boolean isSameType(Field field) { return field != null && field.getClass() == getClass(); } /** * Create new instance of this field with the same value. * @return new field instance with same value */ public abstract Field copyField(); /** * Create new instance of this field type. * @return new field instance with undefined initial value */ public abstract Field newField(); /** * Return Field instance type as an integer value. * @return encoded field type */ abstract byte getFieldType(); /** * Write the field to buf at the specified offset. When writing variable length * fields, the length precedes the actual data. * @param buf data buffer * @param offset data offset * @return next available Field offset within buffer, or -1 if end of buffer reached. * @throws IndexOutOfBoundsException if invalid offset is specified * @throws IOException thrown if IO error occurs */ abstract int write(Buffer buf, int offset) throws IndexOutOfBoundsException, IOException; /** * Read the field value from buf at the specified offset. When reading variable length * fields, the length precedes the actual data. * @param buf data buffer * @param offset data offset * @return next Field offset within buffer, or -1 if end of buffer reached. * @throws IndexOutOfBoundsException if invalid offset is specified * @throws IOException thrown if IO error occurs */ abstract int read(Buffer buf, int offset) throws IndexOutOfBoundsException, IOException; /** * Get the total number of bytes which will be read from the buffer * for this field. For variable-length fields, only the length * portion of the data is examined within the buffer. This method is intended * to be used instead of the read method when only interested in the data * length. * @param buf data buffer * @param offset data offset * @return total number of bytes for this field stored within buf * @throws IndexOutOfBoundsException if invalid offset is specified * @throws IOException thrown if IO error occurs */ abstract int readLength(Buffer buf, int offset) throws IndexOutOfBoundsException, IOException; /** * Get the number of bytes required to store this field value. * For a variable length fields, this value also accounts for a 4-byte * length prefix. Additionally, this method should not be invoked when * working with stored data until after the read method has been invoked. * @return total storage length */ abstract int length(); /** * Determine if the specified Object is another Field which has the same * type and value as this Field. When comparing a {@link PrimitiveField}, * with a null state, a value of zero (0) is used. * @param obj another object * @return true if this field equals obj */ @Override public abstract boolean equals(Object obj); @Override public abstract int hashCode(); /** * Get field value as a formatted string * @return field value string */ public abstract String getValueAsString(); /** * Get minimum field value. * * Supported for fixed-length fields only. * @return minimum value * @throws UnsupportedOperationException if field is not fixed-length */ abstract Field getMinValue(); /** * Get maximum field value. * * Supported for fixed-length fields only. * @return maximum value * @throws UnsupportedOperationException if field is not fixed-length */ abstract Field getMaxValue(); /** * Determine if the field has been set to a null-state or value. * @return true if field has been set to a null state or value, else false */ public abstract boolean isNull(); /** * Set this field to its null-state. For variable-length field this will * generally correspond to a null value, while primitive and fixed-length * fields will be set to a zero (0) value. This method may only be invoked * on a sparse column field. * @throws IllegalFieldAccessException thrown if this field is immutable or is an index field */ abstract void setNull(); /** * Performs a fast in-place comparison of this field value with another * field value stored within the specified buffer at the the specified offset. * NOTE: This method will treat all null primitives as 0 although is not intended * to support such use. * @param buffer data buffer * @param offset field value offset within buffer * @return comparison value, zero if equal, -1 if this field has a value * less than the stored field, or +1 if this field has a value greater than * the stored field located at keyIndex. * @throws IndexOutOfBoundsException if invalid offset is specified */ abstract int compareTo(DataBuffer buffer, int offset) throws IndexOutOfBoundsException; /** * Compares this Field with another Field for order. Returns a * negative integer, zero, or a positive integer as this object is less * than, equal to, or greater than the specified Field. *
* NOTE: Field objects do not fully comply with the Comparable interface. * Only the same Field implementations may be compared. In addition, the * null state is not considered when comparing {@link PrimitiveField}s which have a * zero (0) value. * @param otherField another Field which is the same type as this Field * @return field comparison result (see {@link Comparable#compareTo(Object)}). * @throws ClassCastException if an attempt to compare dissimilar Fields (e.g., * an IntField may not be compared with a ShortField). */ @Override public abstract int compareTo(Field otherField); /** * Get the field associated with the specified type value. * @param fieldType field type index * @return Field field instance which corresponds to the specified fieldType * @throws UnsupportedFieldException if unsupported fieldType specified */ static Field getField(byte fieldType) throws UnsupportedFieldException { if (fieldType == 0x88) { // 0x88 - Reserved value (future expanded Field encoding) throw new UnsupportedFieldException(fieldType); } if ((fieldType & INDEX_PRIMARY_KEY_TYPE_MASK) == 0) { switch (fieldType & FIELD_TYPE_MASK) { case LONG_TYPE: return LongField.INSTANCE; case INT_TYPE: return IntField.INSTANCE; case STRING_TYPE: return StringField.INSTANCE; case SHORT_TYPE: return ShortField.INSTANCE; case BYTE_TYPE: return ByteField.INSTANCE; case BOOLEAN_TYPE: return BooleanField.INSTANCE; case BINARY_OBJ_TYPE: return BinaryField.INSTANCE; case FIXED_10_TYPE: return FixedField10.INSTANCE; } } else { return IndexField.getIndexField(fieldType); } throw new UnsupportedFieldException(fieldType); } public static class UnsupportedFieldException extends IOException { UnsupportedFieldException(byte fieldType) { super("Unsupported DB field type: 0x" + Integer.toHexString(fieldType & 0xff)); } UnsupportedFieldException(String msg) { super(msg); } } /** * Get the type index value of the FixedField type which corresponds * to the specified fixed-length; * @param fixedLength fixed length (currently only 10 is supported) * @return FixedLength field type index * @throws IllegalArgumentException if unsupported fixedLength is specified */ static byte getFixedType(int fixedLength) { if (fixedLength == 10) { return FIXED_10_TYPE; } throw new IllegalArgumentException( "Unsupported fixed-length binary type size: " + fixedLength); } /** * Get a fixed-length field of the specified size * @param size fixed-field length (supported sizes: 1, 4, 8, 10) * @return fixed field instance * @throws IllegalArgumentException if unsupported fixed field length */ static Field getFixedField(int size) { switch (size) { case 1: return new ByteField(); case 4: return new IntField(); case 8: return new LongField(); case 10: return new FixedField10(); } throw new IllegalArgumentException("Unsupported fixed-field length: " + size); } /** * Determine if a specified field instance may be indexed * @param field field to be checked * @return true if field can be indexed */ public static boolean canIndex(Field field) { if (field == null) { return false; } if (field instanceof IndexField) { return false; } return !field.isSameType(BooleanField.INSTANCE) && !field.isSameType(ByteField.INSTANCE); } }