/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2013 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* 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.pentaho.di.core.row;
/**
* This class of static methods can be used to manipulate rows: add, delete, resize, etc... That way, when we want to go
* for a metadata driven system with hiding deletes, over sized arrays etc, we can change these methods to find
* occurrences.
* <p>
* For example, a step adding a field to the row should always call
*
* <pre>
* <a href="">public static Object[] resizeArray(Object[] objects, int newSize)</a></i>
* </pre>
*
* which will either physically resize the array or return the original row, in case it was over-allocated and has
* enough slots. If a step needs to create new rows from scratch, it should use allocateRowData() which will return a
* somewhat over-allocated object array to fit the desired number of fields.
*
* @author Matt
*/
public class RowDataUtil {
public static int OVER_ALLOCATE_SIZE = 10;
/**
* Allocate a new Object array. However, over allocate by a constant factor to make adding values faster.
*
* @param size
* the minimum size to allocate.
* @return the newly allocated object array
*/
public static Object[] allocateRowData( int size ) {
return new Object[size + OVER_ALLOCATE_SIZE];
}
/**
* Resize an object array making it bigger, over allocate, return the original array if there's enough room.
*
* @param objects
* @param newSize
* @return A new object array, resized.
*/
public static Object[] resizeArray( Object[] objects, int newSize ) {
if ( objects != null && objects.length >= newSize ) {
return objects;
}
Object[] newObjects = new Object[newSize + OVER_ALLOCATE_SIZE];
if ( objects != null ) {
System.arraycopy( objects, 0, newObjects, 0, objects.length );
}
return newObjects;
}
/**
* Resize an object array making it bigger, over allocate, always create a copy of the original array, even if there's
* enough room in the old one.
*
* @param objects
* the original row
* @param newSize
* the new size
* @return A new object array, resized.
*/
public static Object[] createResizedCopy( Object[] objects, int newSize ) {
Object[] newObjects;
if ( objects.length < newSize ) {
newObjects = new Object[newSize + OVER_ALLOCATE_SIZE];
} else {
newObjects = new Object[objects.length];
}
if ( objects != null ) {
System.arraycopy( objects, 0, newObjects, 0, objects.length );
}
return newObjects;
}
/**
* This method concatenates data from an array of rows, each with their own specific length.
*
* @param objects
* @param lengths
* @return The concatenated array of objects.
*/
public static Object[] createResizedCopy( Object[][] objects, int[] lengths ) {
int size = 0;
if ( objects != null ) {
for ( int i = 0; i < objects.length; i++ ) {
size += lengths[i];
}
}
Object[] newObjects = allocateRowData( size );
if ( objects != null ) {
size = 0;
for ( int i = 0; i < lengths.length; i++ ) {
System.arraycopy( objects[i], 0, newObjects, size, lengths[i] );
size += lengths[i];
}
}
return newObjects;
}
/**
* Remove an item from an Object array. This is a slow operation, later we want to just flag this object and discard
* it at the next resize. The question is of-course if it makes that much of a difference in the end.
*
* @param objects
* @param index
* @return
*/
public static Object[] removeItem( Object[] objects, int index ) {
Object[] newObjects = new Object[objects.length - 1];
System.arraycopy( objects, 0, newObjects, 0, index );
System.arraycopy( objects, index + 1, newObjects, index, objects.length - index - 1 );
return newObjects;
}
/**
* Add two arrays and make one new one.
*
* @param one
* The first array
* @param the
* length of the row data or of it's longer, the location of the new extra value in the returned data row
* @param two
* The second array
* @return a new Array containing all elements from one and two after one another
*/
public static Object[] addRowData( Object[] one, int sourceLength, Object[] two ) {
Object[] result = resizeArray( one, sourceLength + two.length );
System.arraycopy( two, 0, result, sourceLength, two.length );
return result;
}
/**
* Add a single value to a row of data
*
* @param rowData
* The original row of data
* @param the
* length of the row data or of it's longer, the location of the new extra value in the returned data row
* @param extra
* The extra value to add
* @return a new Array containing all elements, including the extra one
*/
public static Object[] addValueData( Object[] rowData, int length, Object extra ) {
Object[] result = resizeArray( rowData, length + 1 );
result[length] = extra;
return result;
}
/**
* Remove a number of items in a row of data.
*
* @param rowData
* the row of data to remove from
* @param index
* the index of all the items in the source table to remove. We don't check if the same index gets deleted
* twice!
*/
public static Object[] removeItems( Object[] rowData, int[] index ) {
Object[] data = new Object[rowData.length - index.length];
int count = data.length - 1;
int removenr = index.length - 1;
for ( int i = rowData.length - 1; i >= 0; i-- ) {
if ( removenr >= 0 && i == index[removenr] ) {
removenr--;
} else {
data[count] = rowData[i];
count--;
}
}
return data;
}
}