--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.store.raw.data.StoredRecordHeader\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+package org.apache.derby.impl.store.raw.data;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.store.raw.PageKey;\r
+import org.apache.derby.iapi.store.raw.RecordHandle;\r
+\r
+import java.io.IOException;\r
+import java.io.EOFException;\r
+\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+\r
+import org.apache.derby.iapi.services.io.CompressedNumber;\r
+\r
+/**\r
+ A class StoredPage uses to cache record headers by passing instances\r
+ to BasePage, and to write stored versions of record headers.\r
+\r
+ Format\r
+\r
+ <PRE>\r
+\r
+ 1 byte - status\r
+ compressed int - record identifier\r
+\r
+ compressed long - overflow page } only if hasOverflow() is true\r
+ compressed int - overflow id } " " "\r
+\r
+ compressed int - first field } only if hasFirstField set - otherwise 0\r
+\r
+ compressed int - number of fields in this portion - only if hasOverflow()\r
+ is false OR hasFirstField is true - otherwise 0\r
+ </PRE>\r
+\r
+*/\r
+\r
+public final class StoredRecordHeader\r
+{\r
+\r
+ /**************************************************************************\r
+ * Constants of the class\r
+ **************************************************************************\r
+ */\r
+\r
+ /**\r
+ * Status bits for the record header:\r
+ *\r
+ * RECORD_INITIAL - used when record header is first initialized\r
+ * RECORD_DELETED - used to indicate the record has been deleted\r
+ * RECORD_OVERFLOW - used to indicate the record has been \r
+ * overflowed, it will point to the overflow \r
+ * page and ID\r
+ * RECORD_HAS_FIRST_FIELD - used to indicate that firstField is stored. \r
+ * When RECORD_OVERFLOW and \r
+ * RECORD_HAS_FIRST_FIELD both are set, part of \r
+ * record is on the page, the record header \r
+ * also stores the overflow point to the next \r
+ * part of the record.\r
+ * RECORD_VALID_MASK - A mask of valid bits that can be set \r
+ * currently, such that the following assert can\r
+ * be made: \r
+ * ASSERT((status & ~RECORD_VALID_MASK) == 0))\r
+ **/\r
+ public static final int RECORD_INITIAL = 0x00;\r
+ public static final int RECORD_DELETED = 0x01;\r
+ public static final int RECORD_OVERFLOW = 0x02;\r
+ public static final int RECORD_HAS_FIRST_FIELD = 0x04; \r
+ public static final int RECORD_VALID_MASK = 0x0f;\r
+\r
+\r
+ /**************************************************************************\r
+ * Fields of the class\r
+ **************************************************************************\r
+ */\r
+\r
+ /**\r
+ * Actual identifier of the record\r
+ *\r
+ * <BR> MT - Mutable\r
+ **/\r
+ protected int id;\r
+\r
+ /**\r
+ * Status of the record.\r
+ *\r
+ * See above for description of fields:\r
+ * RECORD_INITIAL\r
+ * RECORD_DELETED\r
+ * RECORD_OVERFLOW\r
+ * RECORD_HAS_FIRST_FIELD\r
+ * RECORD_VALID_MASK\r
+ *\r
+ * <BR> MT - Mutable - single thread required.\r
+ **/\r
+ protected int status;\r
+\r
+ /**\r
+ * number of fields in the row.\r
+ **/\r
+ protected int numberFields;\r
+\r
+ /**\r
+ * A record handle that can represent the record, may be null.\r
+ **/\r
+ protected RecordHandle handle;\r
+\r
+\r
+ /**\r
+ * If (hasOverflow()) then this is the id of the row on page overflowPage\r
+ * where the next portion of the row can be found. In this case there\r
+ * are no "real" fields on this page. This situation comes about if a\r
+ * row has been updated such that the real first field no longer fits on\r
+ * the head page.\r
+ **/\r
+ protected int overflowId;\r
+\r
+\r
+ /**\r
+ * If (hasOverflow()) then this is the page where where the next portion of\r
+ * the row can be found. In this case there are no "real" fields on this \r
+ * page.\r
+ **/\r
+ protected long overflowPage;\r
+\r
+ /**\r
+ * if (hasFirstField()) then this field is the number of the column in\r
+ * the orginal row which is now stored as the first field in this row. This\r
+ * row is 2nd through N'th portion of a long row. \r
+ *\r
+ * For example if a row has its first 3 fields on page 0 and its next 3\r
+ * fields on page 1, then the record header of the row portion on page 1\r
+ * will have hasFirstField() set to true, and the value would be 4, \r
+ * indicating that the 4th field of the row is stored as the 1st field of\r
+ * the partial row portion stored on page 1.\r
+ **/\r
+ protected int firstField;\r
+\r
+\r
+ /**************************************************************************\r
+ * Constructors for This class:\r
+ **************************************************************************\r
+ */\r
+ public StoredRecordHeader() \r
+ {\r
+ }\r
+\r
+ public StoredRecordHeader(int id, int numberFields) \r
+ {\r
+ setId(id);\r
+ setNumberFields(numberFields);\r
+ }\r
+\r
+ public StoredRecordHeader(\r
+ byte data[],\r
+ int offset)\r
+ {\r
+ read(data, offset);\r
+ }\r
+\r
+ public StoredRecordHeader(StoredRecordHeader loadTargetFrom) \r
+ {\r
+ this.status = loadTargetFrom.status;\r
+ this.id = loadTargetFrom.id;\r
+ this.numberFields = loadTargetFrom.numberFields;\r
+ handle = null;\r
+\r
+ overflowId = loadTargetFrom.overflowId;\r
+ overflowPage = loadTargetFrom.overflowPage;\r
+ firstField = loadTargetFrom.firstField;\r
+ }\r
+\r
+ /**************************************************************************\r
+ * Public Accessor "Get" Methods of This class:\r
+ **************************************************************************\r
+ */\r
+\r
+ /**\r
+ * Get a record handle for the record.\r
+ * <p>\r
+ *\r
+ * <BR> MT - single thread required\r
+ **/\r
+ protected RecordHandle getHandle(\r
+ PageKey pageId, \r
+ int current_slot) \r
+ {\r
+ if (handle == null)\r
+ handle = new RecordId(pageId, id, current_slot);\r
+\r
+ return handle;\r
+ }\r
+\r
+ /**\r
+ * Get the record identifier\r
+ *\r
+ * <BR> MT - thread safe\r
+ **/\r
+ public final int getId() \r
+ {\r
+ return id;\r
+ }\r
+\r
+ public int getNumberFields() \r
+ {\r
+ return numberFields;\r
+ }\r
+\r
+ public long getOverflowPage() \r
+ {\r
+ return overflowPage;\r
+ }\r
+\r
+ public int getOverflowId() \r
+ {\r
+ return overflowId;\r
+ }\r
+\r
+ public int getFirstField() \r
+ {\r
+ return firstField;\r
+ }\r
+\r
+ public final boolean hasOverflow() \r
+ {\r
+ return ((status & RECORD_OVERFLOW) == RECORD_OVERFLOW);\r
+ }\r
+\r
+ protected final boolean hasFirstField() \r
+ {\r
+ return ((status & RECORD_HAS_FIRST_FIELD) == RECORD_HAS_FIRST_FIELD);\r
+ }\r
+\r
+ /**\r
+ * Get the deleted state of the record.\r
+ * <p>\r
+ *\r
+ * <BR> MT - single thread required\r
+ **/\r
+ public final boolean isDeleted() \r
+ {\r
+ return ((status & RECORD_DELETED) == RECORD_DELETED);\r
+ }\r
+\r
+\r
+ /**\r
+ * return the size of the record header.\r
+ * <p>\r
+ * Calculates the size of the record header, mostly used to allow a\r
+ * reader to skip over the record header and position on the 1st field\r
+ * of the record.\r
+ * <p>\r
+ * This low level routine is performance critical to processing lots of\r
+ * rows, so calls to CompressNumber have been hand inlined.\r
+ *\r
+ * @return The length of the record header.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ public int size() \r
+ {\r
+ // account for length of fieldDataLength field stored as a compressed\r
+ // int plus one byte for status. \r
+ //\r
+ // int len = CompressedNumber.sizeInt(id) + 1;\r
+ int len = \r
+ (id <= CompressedNumber.MAX_COMPRESSED_INT_ONE_BYTE) ?\r
+ 2 : \r
+ (id <= CompressedNumber.MAX_COMPRESSED_INT_TWO_BYTES) ?\r
+ 3 : 5;\r
+\r
+ if ((status & (RECORD_OVERFLOW | RECORD_HAS_FIRST_FIELD)) == 0)\r
+ {\r
+ // usual case, not a record overflow and does not have first field\r
+ len += \r
+ (numberFields <= CompressedNumber.MAX_COMPRESSED_INT_ONE_BYTE) ?\r
+ 1 : \r
+ (numberFields <= CompressedNumber.MAX_COMPRESSED_INT_TWO_BYTES) ?\r
+ 2 : 4;\r
+ }\r
+ else if ((status & RECORD_OVERFLOW) == 0)\r
+ {\r
+ // not overflow, and has first field set.\r
+\r
+ // account for size of the numberFields field + size fo the \r
+ // firstField field.\r
+ //\r
+ // len += (CompressedNumber.sizeInt(numberFields) +\r
+ // CompressedNumber.sizeInt(firstField);\r
+ //\r
+ len += \r
+ ((numberFields <= CompressedNumber.MAX_COMPRESSED_INT_ONE_BYTE) ?\r
+ 1 : \r
+ (numberFields <= CompressedNumber.MAX_COMPRESSED_INT_TWO_BYTES) ?\r
+ 2 : 4) +\r
+\r
+ ((firstField <= CompressedNumber.MAX_COMPRESSED_INT_ONE_BYTE) ?\r
+ 1 : \r
+ (firstField <= CompressedNumber.MAX_COMPRESSED_INT_TWO_BYTES) ?\r
+ 2 : 4);\r
+ }\r
+ else\r
+ {\r
+ // is an overflow field\r
+\r
+ len += CompressedNumber.sizeLong(overflowPage);\r
+ len += CompressedNumber.sizeInt(overflowId);\r
+\r
+ if (hasFirstField())\r
+ {\r
+ len += CompressedNumber.sizeInt(firstField);\r
+ len += CompressedNumber.sizeInt(numberFields);\r
+ }\r
+ }\r
+\r
+ return len;\r
+ }\r
+\r
+ /**************************************************************************\r
+ * Public Accessor "Set" Methods of This class:\r
+ **************************************************************************\r
+ */\r
+\r
+ /**\r
+ * Set the deleted state of the record.\r
+ * <p>\r
+ * return 1, if delete status from not deleted to deleted\r
+ * return -1, if delete status from deleted to not deleted\r
+ * return 0, if status unchanged.\r
+ *\r
+ * <BR> MT - single thread required\r
+ **/\r
+ public int setDeleted(boolean deleteTrue) \r
+ {\r
+\r
+ int retCode = 0;\r
+\r
+ if (deleteTrue) \r
+ {\r
+ if (!isDeleted()) \r
+ {\r
+ // setting the bit from not deleted to deleted\r
+ retCode = 1;\r
+ status |= RECORD_DELETED;\r
+ }\r
+ } \r
+ else \r
+ {\r
+ if (isDeleted()) \r
+ {\r
+ // setting the bit from deleted to not deleted\r
+ retCode = -1;\r
+ status &= ~RECORD_DELETED;\r
+ }\r
+ }\r
+\r
+ return(retCode);\r
+ }\r
+\r
+ public void setFirstField(int firstField) \r
+ {\r
+ this.firstField = firstField;\r
+ status |= RECORD_HAS_FIRST_FIELD;\r
+ }\r
+\r
+ public final void setId(int id) \r
+ {\r
+ this.id = id;\r
+ }\r
+\r
+ public void setOverflowDetails(RecordHandle overflowHandle) \r
+ {\r
+ this.overflowPage = overflowHandle.getPageNumber();\r
+ this.overflowId = overflowHandle.getId();\r
+ }\r
+\r
+ public void setOverflowFields(StoredRecordHeader loadFromTarget)\r
+ {\r
+ this.status = (loadFromTarget.status | RECORD_OVERFLOW);\r
+ this.id = loadFromTarget.id;\r
+ this.numberFields = loadFromTarget.numberFields;\r
+ this.firstField = loadFromTarget.firstField;\r
+ handle = null;\r
+ }\r
+\r
+\r
+ public final void setNumberFields(int numberFields) \r
+ {\r
+ this.numberFields = numberFields;\r
+ }\r
+\r
+ /**************************************************************************\r
+ * Public Methods implmenting read/write of Storable Interface:\r
+ **************************************************************************\r
+ */\r
+ public int write(OutputStream out) \r
+ throws IOException \r
+ {\r
+ // check consistency of the status field - this has caught\r
+ // byte writing corruptions in StoredPage in the past.\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if ((status & ~RECORD_VALID_MASK) != 0)\r
+ SanityManager.THROWASSERT(\r
+ "Invalid status in StoredRecordHeaader = " + status);\r
+ }\r
+\r
+ // write status\r
+ int len = 1;\r
+ out.write(status);\r
+\r
+ // write id\r
+ len += CompressedNumber.writeInt(out, id);\r
+ \r
+\r
+ // write overflow information for overflow record headers\r
+ if (hasOverflow()) \r
+ {\r
+ // if overflow bit is set, then write the overflow pointer info.\r
+ len += CompressedNumber.writeLong(out, overflowPage);\r
+ len += CompressedNumber.writeInt(out, overflowId);\r
+ }\r
+\r
+ // write first field info for long row parts\r
+ if (hasFirstField()) \r
+ {\r
+ len += CompressedNumber.writeInt(out, firstField);\r
+ }\r
+\r
+ // write number of fields, except in the case of a record header\r
+ // which is solely a pointer to another row portion.\r
+ //\r
+ // see read\r
+ if (!hasOverflow() || hasFirstField())\r
+ len += CompressedNumber.writeInt(out, numberFields);\r
+\r
+ return len;\r
+ }\r
+\r
+ public void read(java.io.ObjectInput in) \r
+ throws IOException \r
+ {\r
+\r
+ // read status\r
+ status = in.read();\r
+ if (status < 0)\r
+ throw new EOFException();\r
+\r
+ // check consistency of the status field - this has caught\r
+ // byte writing corruptions in StoredPage in the past.\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if ((status & ~RECORD_VALID_MASK) != 0)\r
+ SanityManager.THROWASSERT(\r
+ "Invalid status in StoredRecordHeader = " + status);\r
+ }\r
+\r
+ // read the record id\r
+ id = CompressedNumber.readInt(in);\r
+\r
+ // initialize the overflow pointer based on status.\r
+ if (hasOverflow()) \r
+ {\r
+ overflowPage = CompressedNumber.readLong(in);\r
+ overflowId = CompressedNumber.readInt(in);\r
+\r
+ } \r
+ else \r
+ {\r
+ overflowPage = 0;\r
+ overflowId = 0;\r
+ }\r
+\r
+ // initialize the 1st field overflow pointer based on status.\r
+ if (hasFirstField()) \r
+ {\r
+ firstField = CompressedNumber.readInt(in);\r
+ } \r
+ else \r
+ {\r
+ firstField = 0;\r
+ }\r
+ \r
+ // In releases prior to 1.3 an overflow record was handled\r
+ // by an overflow header pointing to a complete record on\r
+ // another page. This header had the has overflow bit set but not\r
+ // the has first field bit. This header also did not have the\r
+ // number of fields written out, but it can be seen as\r
+ // a header with 0 fields and a first field of 0.\r
+ if (!hasOverflow() || hasFirstField())\r
+ numberFields = CompressedNumber.readInt(in);\r
+ else\r
+ numberFields = 0;\r
+\r
+ handle = null;\r
+ }\r
+\r
+ private int readId(\r
+ byte[] data,\r
+ int offset)\r
+ {\r
+ int value = data[offset++];\r
+\r
+ if ((value & ~0x3f) == 0)\r
+ {\r
+ // value stored in this byte.\r
+ id = value;\r
+\r
+ return(1);\r
+ }\r
+ else if ((value & 0x80) == 0)\r
+ {\r
+ // value is stored in 2 bytes. only use low 6 bits from 1st byte.\r
+\r
+ id = (((value & 0x3f) << 8) | (data[offset] & 0xff));\r
+\r
+ return(2);\r
+ }\r
+ else\r
+ {\r
+ // value is stored in 4 bytes. only use low 7 bits from 1st byte.\r
+ id = \r
+ ((value & 0x7f) << 24) |\r
+ ((data[offset++] & 0xff) << 16) |\r
+ ((data[offset++] & 0xff) << 8) |\r
+ ((data[offset] & 0xff) );\r
+\r
+ return(4);\r
+ }\r
+ }\r
+ private int readOverFlowPage(\r
+ byte[] data,\r
+ int offset)\r
+ {\r
+ int int_value = data[offset++];\r
+\r
+ if ((int_value & ~0x3f) == 0)\r
+ {\r
+ // test for small case first - assuming this is usual case.\r
+ // this is stored in 2 bytes.\r
+\r
+ overflowPage = ((int_value << 8) | (data[offset] & 0xff));\r
+\r
+ return(2);\r
+ }\r
+ else if ((int_value & 0x80) == 0)\r
+ {\r
+ // value is stored in 4 bytes. only use low 6 bits from 1st byte.\r
+\r
+ overflowPage = \r
+ ((int_value & 0x3f) << 24) |\r
+ ((data[offset++] & 0xff) << 16) |\r
+ ((data[offset++] & 0xff) << 8) |\r
+ ((data[offset] & 0xff) );\r
+\r
+ return(4);\r
+\r
+ } \r
+ else\r
+ {\r
+ // value is stored in 8 bytes. only use low 6 bits from 1st byte.\r
+ overflowPage = \r
+ (((long) (int_value & 0x7f)) << 56) |\r
+ (((long) (data[offset++] & 0xff)) << 48) |\r
+ (((long) (data[offset++] & 0xff)) << 40) |\r
+ (((long) (data[offset++] & 0xff)) << 32) |\r
+ (((long) (data[offset++] & 0xff)) << 24) |\r
+ (((long) (data[offset++] & 0xff)) << 16) |\r
+ (((long) (data[offset++] & 0xff)) << 8) |\r
+ (((long) (data[offset] & 0xff)) );\r
+\r
+ return(8);\r
+ }\r
+ }\r
+ private int readOverFlowId(\r
+ byte[] data,\r
+ int offset)\r
+ {\r
+ int value = data[offset++];\r
+\r
+ if ((value & ~0x3f) == 0)\r
+ {\r
+ // length stored in this byte.\r
+ overflowId = value;\r
+\r
+ return(1);\r
+ }\r
+ else if ((value & 0x80) == 0)\r
+ {\r
+ // length is stored in 2 bytes. only use low 6 bits from 1st byte.\r
+\r
+ overflowId = (((value & 0x3f) << 8) | (data[offset] & 0xff));\r
+\r
+ return(2);\r
+ }\r
+ else\r
+ {\r
+ // length is stored in 4 bytes. only use low 7 bits from 1st byte.\r
+ overflowId = \r
+ ((value & 0x7f) << 24) |\r
+ ((data[offset++] & 0xff) << 16) |\r
+ ((data[offset++] & 0xff) << 8) |\r
+ ((data[offset] & 0xff) );\r
+\r
+ return(4);\r
+ }\r
+ }\r
+ private int readFirstField(\r
+ byte[] data,\r
+ int offset)\r
+ {\r
+ int value = data[offset++];\r
+\r
+ if ((value & ~0x3f) == 0)\r
+ {\r
+ // length stored in this byte.\r
+ firstField = value;\r
+\r
+ return(1);\r
+ }\r
+ else if ((value & 0x80) == 0)\r
+ {\r
+ // length is stored in 2 bytes. only use low 6 bits from 1st byte.\r
+\r
+ firstField = (((value & 0x3f) << 8) | (data[offset] & 0xff));\r
+\r
+ return(2);\r
+ }\r
+ else\r
+ {\r
+ // length is stored in 4 bytes. only use low 7 bits from 1st byte.\r
+ firstField = \r
+ ((value & 0x7f) << 24) |\r
+ ((data[offset++] & 0xff) << 16) |\r
+ ((data[offset++] & 0xff) << 8) |\r
+ ((data[offset] & 0xff) );\r
+\r
+ return(4);\r
+ }\r
+ }\r
+ private void readNumberFields(\r
+ byte[] data,\r
+ int offset)\r
+ {\r
+ int value = data[offset++];\r
+\r
+ if ((value & ~0x3f) == 0)\r
+ {\r
+ // length stored in this byte.\r
+ numberFields = value;\r
+ }\r
+ else if ((value & 0x80) == 0)\r
+ {\r
+ // length is stored in 2 bytes. only use low 6 bits from 1st byte.\r
+\r
+ numberFields = (((value & 0x3f) << 8) | (data[offset] & 0xff));\r
+ }\r
+ else\r
+ {\r
+ // length is stored in 4 bytes. only use low 7 bits from 1st byte.\r
+ numberFields = \r
+ ((value & 0x7f) << 24) |\r
+ ((data[offset++] & 0xff) << 16) |\r
+ ((data[offset++] & 0xff) << 8) |\r
+ ((data[offset] & 0xff) );\r
+ }\r
+ }\r
+\r
+\r
+ private void read(\r
+ byte[] data,\r
+ int offset)\r
+ {\r
+ status = data[offset++];\r
+\r
+ int value = data[offset++];\r
+\r
+ if ((value & ~0x3f) == 0)\r
+ {\r
+ // value stored in this byte.\r
+ id = value;\r
+ }\r
+ else if ((value & 0x80) == 0)\r
+ {\r
+ // value is stored in 2 bytes. only use low 6 bits from 1st byte.\r
+\r
+ id = (((value & 0x3f) << 8) | (data[offset++] & 0xff));\r
+ }\r
+ else\r
+ {\r
+ // value is stored in 4 bytes. only use low 7 bits from 1st byte.\r
+ id = \r
+ ((value & 0x7f) << 24) |\r
+ ((data[offset++] & 0xff) << 16) |\r
+ ((data[offset++] & 0xff) << 8) |\r
+ ((data[offset++] & 0xff) );\r
+ }\r
+\r
+ if ((status & (RECORD_OVERFLOW | RECORD_HAS_FIRST_FIELD)) == 0)\r
+ {\r
+ // usual case, not a record overflow and does not have first field\r
+ overflowPage = 0;\r
+ overflowId = 0;\r
+ firstField = 0;\r
+\r
+ readNumberFields(data, offset);\r
+ }\r
+ else if ((status & RECORD_OVERFLOW) == 0)\r
+ {\r
+ // not overflow, and has first field set.\r
+ overflowPage = 0;\r
+ overflowId = 0;\r
+\r
+ offset += readFirstField(data, offset);\r
+\r
+ readNumberFields(data, offset);\r
+ }\r
+ else\r
+ {\r
+ // is an overflow field\r
+\r
+ offset += readOverFlowPage(data, offset);\r
+ offset += readOverFlowId(data, offset);\r
+\r
+ if (hasFirstField())\r
+ {\r
+ offset += readFirstField(data, offset);\r
+ readNumberFields(data, offset);\r
+ }\r
+ else\r
+ {\r
+ firstField = 0;\r
+ numberFields = 0;\r
+ }\r
+ }\r
+\r
+ handle = null;\r
+\r
+ return;\r
+ }\r
+\r
+\r
+ public String toString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ String str = "recordHeader: Id=" + getId();\r
+ \r
+ str += "\n isDeleted = " + isDeleted();\r
+ str += "\n hasOverflow = " + hasOverflow();\r
+ str += "\n hasFirstField = " + hasFirstField();\r
+ str += "\n numberFields = " + getNumberFields();\r
+ str += "\n firstField = " + getFirstField();\r
+ str += "\n overflowPage = " + getOverflowPage();\r
+ str += "\n overflowId = " + getOverflowId();\r
+\r
+ return str;\r
+ }\r
+ else\r
+ {\r
+ return null;\r
+ }\r
+ }\r
+}\r