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

org.firebirdsql.gds.ng.fields.RowValue Maven / Gradle / Ivy

The newest version!
/*
 * Firebird Open Source JDBC Driver
 *
 * Distributable under LGPL license.
 * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * LGPL License for more details.
 *
 * This file was created by members of the firebird development team.
 * All individual contributions remain the Copyright (C) of those
 * individuals.  Contributors to this file are either listed here or
 * can be obtained from a source control history command.
 *
 * All rights reserved.
 */
package org.firebirdsql.gds.ng.fields;

import java.util.Arrays;

/**
 * Collection of values of fields. Usually row or parameter values.
 *
 * @author Mark Rotteveel
 * @since 3.0
 */
public sealed class RowValue {

    /**
     * Marker array object for uninitialized fields
     */
    // NOTE: Do not assign ByteArrayHelper.empty(), as this must be a unique instance
    private static final byte[] NOT_INITIALIZED = new byte[0];

    public static final RowValue EMPTY_ROW_VALUE = new RowValue(0, false);

    private final byte[][] fieldData;

    /**
     * Creates an empty row value with {@code size} fields.
     * 

* Use {@link #EMPTY_ROW_VALUE} for size 0. *

* * @param size * Number of fields * @param markUninitialized * {@code true} to mark all fields as not initialized */ private RowValue(int size, boolean markUninitialized) { this.fieldData = new byte[size][]; if (markUninitialized) { Arrays.fill(this.fieldData, NOT_INITIALIZED); } } /** * @return The number of fields. */ public final int getCount() { return fieldData.length; } /** * Sets the data of the field with {@code index}. * * @param index * Index of the field * @param data * byte array with data for field, or {@code null} * @throws java.lang.IndexOutOfBoundsException * if index is not {@code 0 <= index > getCount()} */ public void setFieldData(int index, byte[] data) { fieldData[index] = data; } /** * Get the data of the field with {@code index}. *

* For uninitialized fields, returns {@code null}. To distinguish between uninitialized or initialized with * {@code null}, use {@link #isInitialized(int)}. *

* * @param index * Index of the field * @return byte array with data for field, or {@code null} * @throws java.lang.IndexOutOfBoundsException * if index is not {@code 0 <= index > getCount()} */ public final byte[] getFieldData(int index) { byte[] data = fieldData[index]; return data != NOT_INITIALIZED ? data : null; } /** * Resets the state of this row value to uninitialized. * * @since 4 */ public void reset() { Arrays.fill(fieldData, NOT_INITIALIZED); } /** * Does this row value serve as a deleted row marker. *

* This is not general purpose functionality, but exists solely to detect deleted rows in updatable result sets. *

* * @return {@code true} if this a deleted row marker, {@code false} otherwise * @since 5 */ public boolean isDeletedRowMarker() { return false; } /** * Initializes uninitialized fields with {@code null}. * * @since 5 */ public final void initializeFields() { for (int idx = 0; idx < fieldData.length; idx++) { if (fieldData[idx] == NOT_INITIALIZED) { fieldData[idx] = null; } } } /** * Is the field with {@code index} initialized. * * @param index * Index of the field * @return {@code true} if the field is initialized * @throws IndexOutOfBoundsException * if index is not {@code 0 <= index > getCount()} * @since 4 */ public final boolean isInitialized(int index) { return fieldData[index] != NOT_INITIALIZED; } /** * @return number of initialized fields in this row value * @since 6 */ public final int initializedCount() { int count = 0; for (byte[] fieldDatum : fieldData) { if (fieldDatum != NOT_INITIALIZED) { count++; } } return count; } /** * Convenience method for creating a default, uninitialized, row value for a {@link RowDescriptor}. * * @param rowDescriptor * The row descriptor * @return {@code RowValue} object * @since 4 */ public static RowValue defaultFor(RowDescriptor rowDescriptor) { int count = rowDescriptor.getCount(); return count == 0 ? EMPTY_ROW_VALUE : new RowValue(count, true); } /** * Convenience method for populating a row value from a RowDescriptor and byte arrays. *

* Note this method is mainly intended for use in {@link org.firebirdsql.jdbc.FBDatabaseMetaData}. *

*

* Compared to {@link #of(byte[][])}, this method has the advantage that it checks if the number of byte arrays * is consistent with the row descriptor. *

* * @param rowDescriptor * The row descriptor * @param rowData * An array of byte arrays with the field data. * @return new {@code RowValue} object * @throws IllegalArgumentException * If the {@code rowData} byte array count does not match field count of the row descriptor */ public static RowValue of(RowDescriptor rowDescriptor, byte[]... rowData) { final int size = rowDescriptor.getCount(); if (size != rowData.length) { throw new IllegalArgumentException("Expected RowDescriptor count and rowData length to be the same"); } if (size == 0) { return EMPTY_ROW_VALUE; } final RowValue rowValue = new RowValue(size, false); for (int i = 0; i < size; i++) { rowValue.setFieldData(i, rowData[i]); } return rowValue; } /** * Creates a row value that can serve as a deleted row marker. *

* All fields have a value of {@code null}, and updates will fail with an {@link UnsupportedOperationException}. *

* * @param count The number of columns * @return {@code RowValue} object * @since 5 */ public static RowValue deletedRowMarker(int count) { return new DeletedRowMarker(count); } /** * Convenience method for populating a row value from byte arrays. *

* This method is mainly intended for use with direct manipulation in the low-level gds-ng API. *

* * @param rowData * An array of byte arrays with the field data. * @return new {@code RowValue} object * @see #of(RowDescriptor, byte[][]) */ public static RowValue of(byte[]... rowData) { if (rowData.length == 0) { return EMPTY_ROW_VALUE; } RowValue newRowValue = new RowValue(rowData.length, false); for (int idx = 0; idx < rowData.length; idx++) { newRowValue.setFieldData(idx, rowData[idx]); } return newRowValue; } /** * Copies this {@code RowValue} and the values it contains. *

* As the field values are mutable, it is important to consider whether you need to be able * to see modifications to the field data, or if you need fields with the same original data. If the former, * pass the original, if the latter use this method to obtain a copy. *

* * @return Copy of this object with cloned field values, for empty rows (count is 0) {@link #EMPTY_ROW_VALUE}. */ public final RowValue deepCopy() { final int size = getCount(); if (this instanceof DeletedRowMarker) { return new DeletedRowMarker(size); } else if (size == 0) { return EMPTY_ROW_VALUE; } // Implementation note: I decided not to override clone here because it didn't "feel right" RowValue newRowValue = new RowValue(size, false); for (int idx = 0; idx < size; idx++) { byte[] value = fieldData[idx]; newRowValue.fieldData[idx] = value == null || value == NOT_INITIALIZED ? value : value.clone(); } return newRowValue; } /** * Marks a deleted row. */ private static final class DeletedRowMarker extends RowValue { private DeletedRowMarker(int size) { super(size, false); } @Override public void setFieldData(int index, byte[] data) { throw new UnsupportedOperationException("Deleted row marker should not be updated"); } @Override public void reset() { // do nothing } @Override public boolean isDeletedRowMarker() { return true; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy