src.com.ibm.as400.access.DDMRecordCache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jt400-jdk8 Show documentation
Show all versions of jt400-jdk8 Show documentation
The Open Source version of the IBM Toolbox for Java
///////////////////////////////////////////////////////////////////////////////
//
// JTOpen (IBM Toolbox for Java - OSS version)
//
// Filename: DDMRecordCache.java
//
// The source code contained herein is licensed under the IBM Public License
// Version 1.0, which has been approved by the Open Source Initiative.
// Copyright (C) 1997-2000 International Business Machines Corporation and
// others. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////
package com.ibm.as400.access;
import java.io.UnsupportedEncodingException;
/**
*Class providing caching services to the AS400File classes.
**/
class DDMRecordCache
{
private static final String copyright = "Copyright (C) 1997-2000 International Business Machines Corporation and others.";
// Constant indicating direction of BACKWARD for the cache
static final int BACKWARD = 1;
// Constant indicating direction of FORWARD for the cache
static final int FORWARD = 0;
// Indicates current direction of the cache. This is set whenever
// the cache is refreshed.
int currentDirection_ = FORWARD;
// Current record position in the cache
private int currentPosition_;
// Indicates if the first record of the cache is the first record of the file
private boolean containsFirstRecord_;
// Indicates if the last record of the cache is the last record of the file
private boolean containsLastRecord_;
// Indicates if the cache contains any records
private boolean isEmpty_;
// The records contained in this cache
private Record[] records_ = new Record[0];
// The current size of the cache (number of records cached currently)
private int size_;
/**
*Constructs a DDMRecordCache object.
**/
DDMRecordCache()
{
setIsEmpty();
}
/**
*Constructs a DDMRecordCache object.
*@param records the records with which to populate the cache. This value may be
*null.
*@param direction the direction in which to populate the cache. Valid values are
*FORWARD and BACKWARD. If direction is FORWARD, the records are stored
*in the cache in the order in which they occur in records. If
*direction is BACKWARD, the records are stored in the reverse order in which
*they occur in records.
*@param firstRecord indicates if the first record of the file is in records.
*@param lastRecord indicates if the last record of the file is in records.
**/
DDMRecordCache(Record[] records, int direction, boolean firstRecord, boolean lastRecord)
{
refresh(records, direction, firstRecord, lastRecord);
}
/**
*Adds a record to the cache.
*@param record the record to add.
*@param end indicates if the record is to be added to the end of the cache. If
*true, the record is added to the end of the cache; otherwise the record is added
*to the beginning of the cache.
**/
void add(Record record, boolean end)
{ // Create new record array to hold the old records plus the new record
Record[] newRecords = new Record[records_.length + 1];
if (end)
{ // Add the record to the end of the cache
System.arraycopy(records_, 0, newRecords, 0, records_.length);
newRecords[records_.length] = record;
}
else
{ // Add new record to the beginning of the cache
System.arraycopy(records_, 0, newRecords, 1, records_.length);
newRecords[0] = record;
// Need to update the current position
currentPosition_++;
}
// Update state variables
records_ = newRecords;
size_ = records_.length;
isEmpty_ = false;
}
/**
*Determines if the keys are the same.
*@param key key to compare
*@param recKey key of record being compared to
*@return true if the keys match; false otherwise
**/
boolean compareKeys(Object[] key, Object[] recKey)
{
if (key.length > recKey.length)
{ // Key is greater in length than recKey; not valid
return false;
}
// Key has possibilities - may be a partial key, or may be a full key
// so we only check matches up to the length of the "key" passed in.
boolean match = true; // Assume we have a match until proven otherwise
int keyIndex = 0;
int recKeyIndex = 0;
RecordFormat rf = records_[0].getRecordFormat(); // Record format for the records
// in the cache.
for (int j = 0; j < key.length && match; ++j)
{
if (key[j] instanceof byte[] && recKey[j] instanceof byte[])
{
if (((byte[])key[j]).length != ((byte[])recKey[j]).length)
{ // Key field length mismatch; keys don't match
match = false;
}
else
{ // Check field byte by byte
for (int k = 0; k < ((byte[])key[j]).length; ++k)
{
if (((byte[])key[j])[k] != ((byte[])recKey[j])[k])
{ // Key field mismatch; keys don't match, get out of loop
match = false;
break;
}
}
}
}
else if (rf.getKeyFieldDescription(j) instanceof VariableLengthFieldDescription)
{
if (((String)key[j]).equals("") && ((String)recKey[j]).equals(""))
{
// We have a match for this field
}
else
{
// @A1D
// Deleted the code that checks for variable length field.
// Strip the trailing blanks off the key and recKey for both
// the variable length & fixed length fields.
// if (((VariableLengthFieldDescription)rf.getKeyFieldDescription(j)).isVariableLength()) // @A1D
// { // @A1D
// For variable length character fields (i.e. not hex fields) we strip
// the trailing blanks from the keys prior to checking for a match.
// We do this because that is how DDM does it when retrieving from the file.
keyIndex = ((String)key[j]).length() - 1;
while (((String)key[j]).charAt(keyIndex) == ' ')
{
keyIndex--;
}
recKeyIndex = ((String)recKey[j]).length() - 1;
while (((String)recKey[j]).charAt(recKeyIndex) == ' ')
{
recKeyIndex--;
}
// Check stripped values
if (!((String)key[j]).substring(0, keyIndex + 1).equals(((String)recKey[j]).substring(0, recKeyIndex + 1)))
{
match = false;
}
// Start of @A1D
/*
}
else
{
if (!key[j].equals(recKey[j]))
{ // Key field mismatch; keys don't match
match = false;
}
}
*/
// End of @A1D
}
}
else
{ // Not variable length/not character field
if (!key[j].equals(recKey[j]))
{ // Key field mismatch; keys don't match
match = false;
}
}
}
return match;
}
/**
*Indicates if the cache contains the first record of the file.
*@return true if the cache contains the first record of the file; false otherwise.
**/
boolean containsFirstRecord()
{
return containsFirstRecord_;
}
/**
*Indicates if the cache contains the last record of the file.
*@return true if the cache contains the last record of the file; false otherwise.
**/
boolean containsLastRecord()
{
return containsLastRecord_;
}
/**
*Dump the contents of the cache to standard out.
**/
void dump()
{
System.out.println("Dumping cache:");
for (int i = 0; i < records_.length; ++i)
{
System.out.println(records_[i]);
}
}
/**
*Find the record specified by record number in the cache.
*@param recordNumber the record number for which to search.
*@return the index of the record in the cache or -1 if the record does not exist
*in the cache.
**/
int findRecord(int recordNumber)
{
for (int i = 0; i < size_; ++i)
{ // Look for the record
if (records_[i].getRecordNumber() == recordNumber)
{ // Match; return now
return i;
}
}
// No match
return -1;
}
//@RBA
int findRecordLong(long recordNumber)
{
for (int i = 0; i < size_; ++i)
{ // Look for the record
if (records_[i].getRecordNumberLong() == recordNumber)
{ // Match; return now
return i;
}
}
// No match
return -1;
}
/**
*Find the record specified by key from the current position in the cache.
*@param key the key of the record for which to search.
*@return the index of the record in the cache or -1 if the record does not exist
*in the cache.
*@exception UnsupportedEncodingException If an error occurs when converting
*the server data to a Java Object.
**/
int findRecord(Object[] key, boolean searchForward)
throws UnsupportedEncodingException
{
boolean match; // Indicates if we have a match
// Search for a match. If a match occurs we will return before this loop finishes
if (key.length > records_[0].getKeyFields().length)
{ // Key passed in is invalid; return -1
return -1;
}
if (searchForward)
{
for (int i = currentPosition_ + 1; i < size_; ++i)
{
match = compareKeys(key, records_[i].getKeyFields());
if (match)
{
return i;
}
}
}
else
{
for (int i = currentPosition_ - 1; i > -1; --i)
{
match = compareKeys(key, records_[i].getKeyFields());
if (match)
{
return i;
}
}
}
// No record found matching key
return -1;
}
/**
*Returns the record currently pointed to in the cache.
*@return the record currently pointed to in the cache or null if not currently
*positioned on a record.
**/
Record getCurrent()
{
return (currentPosition_ > -1 && currentPosition_ < size_)? records_[currentPosition_] : null;
}
/**
*Returns the first record in the cache.
*@return the first record in the cache or null if cache is empty.
**/
Record getFirst()
{
if (isEmpty_)
{
return null;
}
currentPosition_ = 0;
return records_[currentPosition_];
}
/**
*Returns the last record in the cache.
*@return the last record in the cache or null if cache is empty.
**/
Record getLast()
{
if (isEmpty_)
{
return null;
}
currentPosition_ = size_ - 1;
return records_[currentPosition_];
}
/**
*Returns the next record in the cache.
*@return the next record in the cache, or null if cache is empty or if
*we are at the end of the cache.
**/
Record getNext()
{
if (isEmpty_)
{
return null;
}
return (currentPosition_ < size_ - 1)? records_[++currentPosition_] : null;
}
/**
*Returns the previous record in the cache.
*@return the previous record in the cache, or null if cache is empty or if
*we are at the beginning of the cache.
**/
Record getPrevious()
{
if (isEmpty_)
{
return null;
}
return (currentPosition_ != 0)? records_[--currentPosition_] : null;
}
/**
*Returns the first record in the cache that matches the specified record number.
*@param recordNumber the record number of the record to return.
*@return the first record in the cache that matches the specified record number,
*or null if cache is empty or if the record is not found.
**/
Record getRecord(int recordNumber)
{
if (isEmpty_)
{
return null;
}
int index = findRecord(recordNumber);
if (index != -1)
{
currentPosition_ = index;
return records_[index];
}
else
{
return null;
}
}
/**
*Returns the next record in the cache that matches the specified key.
*@param key the key of the record to return.
*@return the next record in the cache that matches the specified key,
*or null if cache is empty or if the record is not found.
*@exception UnsupportedEncodingException If an error occurs when converting
*the server data to a Java Object.
**/
Record getNextEqualRecord(Object[] key)
throws UnsupportedEncodingException
{
if (isEmpty_)
{
return null;
}
int index = findRecord(key, true);
if (index != -1)
{
currentPosition_ = index;
return records_[index];
}
else
{
return null;
}
}
/**
*Returns the previous record in the cache that matches the specified key.
*@param key the key of the record to return.
*@return the previous record in the cache that matches the specified key,
*or null if cache is empty or if the record is not found.
*@exception UnsupportedEncodingException If an error occurs when converting
*the server data to a Java Object.
**/
Record getPreviousEqualRecord(Object[] key)
throws UnsupportedEncodingException
{
if (isEmpty_)
{
return null;
}
int index = findRecord(key, false);
if (index != -1)
{
currentPosition_ = index;
return records_[index];
}
else
{
return null;
}
}
/**
*Indicates if we are after the last record.
**/
boolean isAfterLast()
{
return (isEmpty_)? false : (currentPosition_ == size_);
}
/**
*Indicates if we are before the first record.
**/
boolean isBeforeFirst()
{
return (isEmpty_)? false : (currentPosition_ == -1);
}
/**
*Indicates if we are at the beginning of the cache.
*@return true if we are at the beginning of the cache; false otherwise.
**/
boolean isBeginningOfCache()
{
return (currentPosition_ <= 0);
}
/**
*Indicates if the cache is empty.
*@return true if the cache is empty; false otherwise.
**/
boolean isEmpty()
{
return isEmpty_;
}
/**
*Indicates if we are at the end of the cache.
*@return true if we are at the end of the cache; false otherwise.
**/
boolean isEndOfCache()
{
return (currentPosition_ >= size_ - 1);
}
/**
*Refreshes the cache with the supplied records.
*@param records the records with which to populate the cache. This value may be
*null.
*@param direction the direction in which to populate the cache. Valid values are
*FORWARD and BACKWARD. If direction is FORWARD, the records are stored
*in the cache in the order in which they occur in records. If
*direction is BACKWARD, the records are stored in the reverse order in which
*they occur in records.
*@param firstRecord indicates if the first record of the file is in records.
*@param lastRecord indicates if the last record of the file is in records.
**/
void refresh(Record[] records, int direction, boolean firstRecord, boolean lastRecord)
{
// Set the current direction
currentDirection_ = direction;
if (records == null || records.length == 0)
{
// Set the state variables
isEmpty_ = true;
containsFirstRecord_ = false;
containsLastRecord_ = false;
currentPosition_ = -1;
size_ = 0;
}
else
{
if (direction == FORWARD)
{ // Records are ordered correctly
records_ = records;
}
else
{ // Need to reverse the order of the records to place them into the array correctly
records_ = new Record[records.length];
for (int i = records.length - 1, j = 0; i >= 0; --i, ++j)
{
records_[j] = records[i];
}
}
containsFirstRecord_ = firstRecord;
containsLastRecord_ = lastRecord;
// The current position of the cache depends on the direction - if we are going forward it
// is prior to the first record in the cache; if we are going backward it is after the last
// record in the cache. When direction is backward it is expected that the user will be
// doing readPrevious()'s to search from the end of the cache (file).
currentPosition_ = (direction == FORWARD)? 0 : records.length - 1;
size_ = records_.length;
isEmpty_ = false;
}
if (Trace.isTraceOn())
{
Trace.log(Trace.INFORMATION, "Record cache refreshed:");
for (int i = 0; i < records_.length; ++i)
{
Trace.log(Trace.INFORMATION, records_[i].toString());
}
}
}
/**
*Sets the state of the cache to empty.
**/
void setIsEmpty()
{
// Set the state variables
isEmpty_ = true;
containsFirstRecord_ = false;
containsLastRecord_ = false;
currentPosition_ = -1;
size_ = 0;
}
/**
*Sets the current cache position to the record matching the specified record number.
*@param recordNumber the record number of the record to position to.
*@return true if the record was found and positioned to; false otherwise.
**/
boolean setPosition(int recordNumber)
{
if (isEmpty_)
{
return false;
}
int i = findRecord(recordNumber);
if (i > -1)
{
currentPosition_ = i;
}
return (i > -1);
}
//@RBA
boolean setPositionLong(long recordNumber)
{
if (isEmpty_)
{
return false;
}
int i = findRecordLong(recordNumber);
if (i > -1)
{
currentPosition_ = i;
}
return (i > -1);
}
/**
*Sets the current cache position to the record matching the specified key.
*@param key the key of the record to position to.
*@return true if the record was found and positioned to; false otherwise.
*@exception UnsupportedEncodingException If an error occurs when converting
*the server data to a Java Object.
**/
boolean setPosition(Object[] key)
throws UnsupportedEncodingException
{
if (isEmpty_)
{
return false;
}
int i = findRecord(key, true);
if (i > -1)
{
currentPosition_ = i;
}
return (i > -1);
}
/**
*Sets the current cache position to after the last record in the cache
**/
void setPositionAfterLast()
{
if (!isEmpty_)
{
currentPosition_ = size_;
}
}
/**
*Sets the current cache position to before the first record in the cache
**/
void setPositionBeforeFirst()
{
if (!isEmpty_)
{
currentPosition_ = -1;
}
}
/**
*Sets the current cache position to the first record in the cache
**/
void setPositionFirst()
{
if (!isEmpty_)
{
currentPosition_ = 0;
}
}
/**
*Sets the current cache position to the last record in the cache
**/
void setPositionLast()
{
if (!isEmpty_)
{
currentPosition_ = size_ - 1;
}
}
/**
*Sets the current cache position to the next record in the cache
**/
void setPositionNext()
{
if (!isEmpty_ && currentPosition_ < size_)
{
currentPosition_++;
}
}
/**
*Sets the current cache position to the previous record in the cache
**/
void setPositionPrevious()
{
if (!isEmpty_ && currentPosition_ > -1)
{
currentPosition_--;
}
}
}