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

org.xBaseJ.indexes.NDX Maven / Gradle / Ivy

/**
 * eobjects.org MetaModel
 * Copyright (C) 2010 eobjects.org
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * 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 GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */

package org.xBaseJ.indexes;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.util.StringTokenizer;

import org.xBaseJ.DBF;
import org.xBaseJ.DbaseUtils;
import org.xBaseJ.xBaseJException;
import org.xBaseJ.fields.Field;

/**
 * This class is courtesy of the xBaseJ project: http://xbasej.sourceforge.net/
 * 
 * Copyright 1997-2007 - American Coders, LTD - Raleigh NC USA
 * 
 * 
 * American Coders, Ltd
 * P. O. Box 97462
 * Raleigh, NC  27615  USA
 * 1-919-846-2014
 * http://www.americancoders.com
 * 
* * @author Joe McVerry, American Coders Ltd. */ public class NDX extends Index { public NDX() { } public NDX(String filename, DBF indatabase, char readonly) throws IOException, xBaseJException { int reading; int i; String wb; Node lNode = null; int Index_record; Field Field = null; dosname = filename; database = indatabase; file = new File(filename); if (!file.exists() || !file.isFile()) { throw new xBaseJException("Unknown Index file"); } // /* endif */ if (readonly == 'r') nfile = new RandomAccessFile(filename, "r"); else nfile = new RandomAccessFile(filename, "rw"); anchor_read(); Index_record = top_Node; reading = Index_record; for (i = 0; i < 488; i++) { if (key_definition[i] <= ' ') break; } try { stringKey = new String(key_definition, 0, i, DBF.encodedType); } catch (UnsupportedEncodingException UEE) { stringKey = new String(key_definition, 0, i); } StringTokenizer strtok = new StringTokenizer(stringKey, "+ "); while (strtok.hasMoreElements()) { wb = (String) strtok.nextElement(); wb = wb.trim(); Field = database.getField(wb); keyControl.addElement(Field); } /* endwhile */ while (reading > 0) { if (topNode == null) { lNode = new Node(nfile, key_per_Node, key_length, keyType, Index_record, false); } else { Node llNode = new Node(nfile, key_per_Node, key_length, keyType, Index_record, false); lNode.set_prev(llNode); lNode = (Node) llNode; } // /* endif */ workNode = lNode; lNode.set_pos(0); lNode.read(); if (reading > 0) {// /* if reading is zero we're still reading // Nodes, when < 0 then read // * a leaf */ Index_record = lNode.get_lower_level(); if (Index_record == 0) { Index_record = lNode.get_key_record_number(); reading = 0; // /* read a leaf so last time in loop */ lNode.set_pos(0);// /* so sequentially reads get first // record */ }// /* Index = 0 then it's a leaf pointer */ } // /* reading > 0 */ if (topNode == null) topNode = (Node) lNode.clone(); }// /* endwhile */ if (topNode == null) { topNode = new Node(nfile, key_per_Node, key_length, keyType, Index_record, false); workNode = topNode; } } /** * creates a NDX object and NDX file * * @param name * Index file name , can be full or partial pathname * @param NDXString * database Fields that define the Index, join Fields with "+" do * not use spaces * @param indatabase * DBF object to be associated with * @param destroy * permission to destroy file if it exists * @param unique * unique flag indicator * @throws IOException * exception thrown by calling methods * @throws xBaseJException * most likely cause - file not found */ public NDX(String name, String NDXString, DBF indatabase, boolean destroy, boolean unique) throws xBaseJException, IOException { int len; String tempch1; char key_type = ' '; StringTokenizer strtok = new StringTokenizer(NDXString.toUpperCase(), "+"); file = new File(name); dosname = name; database = indatabase; if (destroy == false) if (file.exists()) throw new xBaseJException("NDX file already exists"); if (destroy == true) if (file.exists()) if (file.delete() == false) throw new xBaseJException("Can't delete old NDX file"); key_length = 0; stringKey = new String(NDXString); set_key_definition(NDXString); unique_key = (byte) (unique ? 64 : 0); while (strtok.hasMoreElements()) { char type; tempch1 = (String) strtok.nextElement(); Field Field = database.getField(tempch1); type = Field.getType(); if (type == 'M') throw new xBaseJException("Can't make memo field part of a key"); if (type == 'L') throw new xBaseJException( "Can't make logical ield part of a key"); if (type == 'F') throw new xBaseJException( "Can't make float field part of a key"); // the else portion of this can only get called when we // have more than one field making up the key. // It is ugly, but that's the way it is. // if (key_type == ' ') key_type = type; else if (key_type == 'D' && type == 'N') key_type = 'N'; else if (key_type == 'N' && type == 'D') key_type = 'N'; // date key type really doesn't change else if (key_type != type) key_type = 'C'; key_length += Field.getLength(); keyControl.addElement(Field); } if (key_type == 'D' || key_type == 'N') { keyType = 'N'; key_length = 8; // 20091007_rth } else { keyType = 'C'; } len = (((key_length - 1) / 4) + 1) * 4; if (len < 1) { throw new xBaseJException("key length too short"); } /* endif */ if (len > 100) { throw new xBaseJException("key length too int"); } /* endif */ len += 8; next_available = 1; key_entry_size = (short) len; key_per_Node = (short) (509 / len); nfile = new RandomAccessFile(name, "rw"); anchor_write(); if (database.getRecordCount() > 0) bIndex(); else { topNode = new Node(nfile, key_per_Node, key_length, keyType, next_available, false); workNode = topNode; topNode.set_pos(0); top_Node = next_available; next_available++; anchor_write(); topNode.set_lower_level(0); topNode.set_key_record_number(0); topNode.set_keys_in_this_Node(0); topNode.write(); } } public void close() throws IOException { nfile.close(); } public int find_entry(NodeKey findKey) throws xBaseJException, IOException { return find_entry(findKey, findAnyKey); } public int find_entry(NodeKey findKey, int rec) throws xBaseJException, IOException { if (topNode == null) { /* no keys yet */ throw new xBaseJException("No keys built"); } /* endif */ topNode.set_pos(0); record = find_entry(findKey, topNode, rec); return record; } public int find_entry(NodeKey findKey, Node aNode, int findrec) throws xBaseJException, IOException { foundExact = false; int rec, leaf, until; int stat = 1; Node Node_2; workNode = aNode; if (aNode == null) { /* no keys yet */ throw new xBaseJException("No keys built"); } /* endif */ leaf = aNode.get_lower_level(); if (leaf != 0) /* * leaf pointers usually have one more pointer than * shown */ until = aNode.get_keys_in_this_Node() + 1; else until = aNode.get_keys_in_this_Node(); for (aNode.set_pos(0); aNode.get_pos() < until && stat > 0; aNode .pos_up()) { leaf = aNode.get_lower_level(); rec = aNode.get_key_record_number(); if (aNode.get_pos() < (aNode.get_keys_in_this_Node())) { /* * leafs * make us * do this */ stat = findKey.compareKey(aNode.get_key_value()); if (stat > 0) continue; // looping } if (leaf > 0) { /* still dealing with Nodes */ if (aNode.get_next() == null) { Node_2 = new Node(nfile, key_per_Node, key_length, keyType, leaf, true); aNode.set_next(Node_2); Node_2.set_prev(aNode); } else Node_2 = aNode.get_next(); Node_2.set_record_number(leaf); Node_2.read(); Node_2.set_pos(0); workNode = Node_2; rec = find_entry(findKey, Node_2, findrec); return (rec); /* if rec < 0 then didn't find the record yet */ } /* leaf > 0 */ if (stat < 0) /* can't find the key but ... */ { if (findrec > 0) return (keyNotFound); /* * when findrec -1 then looking for * specific key and record */ if (findrec == findFirstMatchingKey) return (keyNotFound); /* * when findrec findAnyKey then for the * key */ return (rec); /* looking for key greater than or equal to */ } foundExact = true; /* stat is zero - key matches the current key */ if ((findrec > 0) && (rec == findrec)) /* * found matching key and * matching record number */ return rec; if (findrec == findFirstMatchingKey) return rec; /* found one key that matches */ if (findrec == findAnyKey) return rec; /* found one key that matches */ /* * findrec not zero so we are looking for the key that is greater * than */ /* or we looking for a key with a particular record number */ } /* end for */ return (foundMatchingKeyButNotRecord); /* * at end of current line but * keep looking for recursion */ } public void bIndex() throws xBaseJException, IOException { int i; int reccount = database.getRecordCount(); NodeKey lastkey; BinaryTree topTree = null; if (database.getRecordCount() > 0) { database.gotoRecord(1); top_Node = 0; next_available = 1; for (i = 1; i <= reccount; i++) { lastkey = build_key(); if (topTree == null) topTree = new BinaryTree(lastkey, i, topTree); else new BinaryTree(lastkey, i, topTree); if (i < reccount) database.read(); } topNode = null; reIndexWork(topTree.getLeast(), 0); anchor_write(); } return; } public void reIndex() throws xBaseJException, IOException { int i; int reccount = database.getRecordCount(); NodeKey lastkey; BinaryTree topTree = null; if (database.getRecordCount() > 0) { // database.gotoRecord(1); 20091010_rth top_Node = 0; next_available = 1; for (i = 1; i <= reccount; i++) { database.gotoRecord(i); // 20091010_rth lastkey = build_key(); if (topTree == null) topTree = new BinaryTree(lastkey, i, topTree); else new BinaryTree(lastkey, i, topTree); // if (i < reccount) 20091010_rth // database.read(); } topNode = null; nfile.close(); file.delete(); nfile = new RandomAccessFile(file, "rw"); anchor_write(); if (database.getRecordCount() > 0) reIndexWork(topTree.getLeast(), 0); anchor_write(); } return; } private int reIndexWork(BinaryTree bt, int level) throws IOException, xBaseJException { BinaryTree tree2 = null; int pos = 0; top_Node = next_available; workNode = new Node(nfile, key_per_Node, key_length, keyType, top_Node, level > 0); next_available++; workNode.set_pos(0); NodeKey lastKey = null; btLoop: while (true) { if (pos == key_per_Node || bt == null) { if ((tree2 == null && pos == 1 && level > 0) || pos == 0) { top_Node--; next_available--; topNode = workNode; // just in case its not set for (int i = pos; i < key_per_Node; i++) { workNode.set_pos(i); workNode.set_key_value(lastKey); } workNode.write(); break btLoop; } if (bt != null || tree2 != null) { // if bt not null more keys // to add and to upper node // if tree2 not nuul then // upper node needs the last // node if (tree2 == null) { topNode = workNode; tree2 = new BinaryTree(lastKey, workNode .get_record_number(), tree2); } else new BinaryTree(lastKey, workNode.get_record_number(), tree2); } if (level == 0) workNode.set_keys_in_this_Node(pos); else workNode.set_keys_in_this_Node(pos - 1); { for (int i = pos; i < key_per_Node; i++) { workNode.set_pos(i); workNode.set_key_value(lastKey); } } workNode.write(); if (bt == null) { // we're all done get out of loop topNode = workNode; // just in case its not set break btLoop; } top_Node = next_available; workNode = new Node(nfile, key_per_Node, key_length, keyType, top_Node, level > 0); next_available++; pos = 0; workNode.set_pos(0); } pos++; lastKey = bt.getKey(); workNode.set_key_value(lastKey); if (level == 0) workNode.set_key_record_number(bt.getWhere()); else workNode.set_lower_level(bt.getWhere()); workNode.pos_up(); bt = bt.getNext(); } if (tree2 == null) return 0; return reIndexWork(tree2.getLeast(), ++level); } public int add_entry(NodeKey NDXkey, int recno) throws xBaseJException, IOException { if (topNode != null) { find_entry(NDXkey, findAnyKey); } set_active_key(NDXkey); return update_entry(workNode, NDXkey, 0, 0, recno); } private int update_entry(Node aNode, NodeKey NDXkey, int leftleaf, int rightleaf, int recno) throws IOException, xBaseJException { int savepos; Node bNode; if (topNode == null) { /* * we don't have any Index area yet so we must * be adding the first record */ topNode = new Node(nfile, key_per_Node, key_length, keyType, next_available, false); workNode = topNode; topNode.set_pos(0); top_Node = next_available; next_available++; anchor_write(); topNode.set_lower_level(0); topNode.set_key_record_number(recno); topNode.set_key_value(NDXkey); topNode.set_keys_in_this_Node(1); topNode.write(); return 0; } /* * this is flaky but if both leaf no or rec no are not zero then we are * splitting the top Node */ /* * for all other conditions if passed a leaf or a Node one of the two * (leaf no or rec no) */ /* will be zero */ if (leftleaf > 0 && recno > 0) { /* work to split the top Node */ /* stuff should still reside in the old Node */ bNode = new Node(nfile, key_per_Node, key_length, keyType, next_available, true); aNode.set_prev(bNode); bNode.set_next(aNode); bNode.set_pos(0); /* we want to get last one */ bNode.set_lower_level(leftleaf); bNode.set_key_record_number(0); bNode.set_key_value(NDXkey); bNode.pos_up(); bNode.set_lower_level(recno); bNode.set_key_record_number(0); /* * now looks like a top Node * leaf=value,rec#=0,key,leaf=0,rec#=0,empty */ topNode = (Node) bNode; bNode.set_keys_in_this_Node(1); top_Node = next_available; bNode.set_record_number(top_Node); next_available++; anchor_write(); bNode.write(); return 0; } savepos = aNode.get_pos(); if (savepos < (aNode.get_keys_in_this_Node())) { /* add to middle of list */ /* * first move the record number of that trailing record indicator * dbase III quirk */ int ptr, recn, i; NodeKey buf; i = aNode.get_keys_in_this_Node(); aNode.set_pos(i); ptr = aNode.get_lower_level(); aNode.pos_up(); aNode.set_lower_level(ptr); /* then move all the other subNodes */ // i--; aNode.set_pos(i); /* should be at last one in list */ for (; // i is pointing to last Nodelet i > -1 && i >= savepos; i--) { ptr = aNode.get_lower_level(); recn = aNode.get_key_record_number(); buf = aNode.get_key_value(); aNode.pos_up(); aNode.set_lower_level(ptr); aNode.set_key_record_number(recn); aNode.set_key_value(buf); aNode.set_pos(i - 1); } /* endfor */ aNode.set_pos(savepos); /* reposition after falling out */ aNode.set_lower_level(leftleaf); aNode.set_key_record_number(recno); aNode.set_key_value(NDXkey); if (rightleaf > 0) { aNode.pos_up(); aNode.set_lower_level(rightleaf); aNode.pos_down(); } } else { aNode.set_pos(aNode.get_keys_in_this_Node()); aNode.set_lower_level(leftleaf); aNode.set_key_record_number(recno); aNode.set_key_value(NDXkey); if (rightleaf > 0) { aNode.pos_up(); aNode.set_lower_level(rightleaf); aNode.pos_down(); } } aNode.set_keys_in_this_Node(aNode.get_keys_in_this_Node() + 1); aNode.write(); if (aNode.get_keys_in_this_Node() >= key_per_Node) splitNode(aNode, savepos); return 0; } private void splitNode(Node aNode, int savepos) throws xBaseJException, IOException { int i, j, k; int left, right; Node bNode; /* this is where we do the famous split */ /* first split one Node and preserve it on the file */ /* then the new Node with half the old data can use the ending logic */ /* which simply updates it in place */ /* if pos < half way point in record */ /* fix and write last (#allowed /2) +1 thru #allowed */ /* split from 1 to #allowed /2 */ /* add our new Index */ /* write our new Index */ /* add entry to Node above. last entry in our new Node */ bNode = new Node(nfile, key_per_Node, key_length, keyType, 0, aNode .isBranch()); bNode.set_pos(0); aNode.set_pos(0); for (k = 0; k < aNode.get_keys_in_this_Node(); k++) { bNode.set_lower_level(aNode.get_lower_level()); bNode.set_key_record_number(aNode.get_key_record_number()); bNode.set_key_value(aNode.get_key_value()); bNode.pos_up(); aNode.pos_up(); } /* endfor */ bNode.set_lower_level(aNode.get_lower_level()); bNode.set_key_record_number(0); bNode.set_key_value(""); i = aNode.get_keys_in_this_Node() / 2; j = aNode.get_keys_in_this_Node() - i; if (savepos > i) { bNode.set_keys_in_this_Node(i); if (aNode.get_next() != null) aNode.set_keys_in_this_Node(i - 1); else aNode.set_keys_in_this_Node(i); /* for top level split */ left = aNode.get_record_number(); aNode.write(); right = next_available; next_available++; anchor_write(); if (aNode.get_prev() != null) { bNode.set_pos(i - 1); update_entry((Node) aNode.get_prev(), bNode.get_key_value(), aNode.get_record_number(), right, 0); } aNode.set_pos(0); bNode.set_pos(i); for (k = 0; k <= j; k++) { aNode.set_lower_level(bNode.get_lower_level()); aNode.set_key_record_number(bNode.get_key_record_number()); aNode.set_key_value(bNode.get_key_value()); aNode.pos_up(); bNode.pos_up(); } /* the right side is already one short */ aNode.set_keys_in_this_Node(j); aNode.set_pos(savepos - i); aNode.set_record_number(right); aNode.write(); if (aNode.get_prev() == null) { // create a new top Node bNode.set_pos(i - 1); // use new Node because it has the old data in it, at pos(i) is // the last key update_entry(bNode, bNode.get_key_value(), left, 0, right); } } else { /* * create new second half and use the first half to put new key * in */ right = aNode.get_record_number(); aNode.set_pos(0); bNode.set_pos(j); for (k = 0; k <= i; k++) { aNode.set_lower_level(bNode.get_lower_level()); aNode.set_key_record_number(bNode.get_key_record_number()); aNode.set_key_value(bNode.get_key_value()); aNode.pos_up(); bNode.pos_up(); } aNode.set_keys_in_this_Node(i); aNode.write(); bNode.set_keys_in_this_Node(i); aNode.set_pos(0); bNode.set_pos(0); for (k = 0; k < key_per_Node; k++) { aNode.set_lower_level(bNode.get_lower_level()); aNode.set_key_record_number(bNode.get_key_record_number()); aNode.set_key_value(bNode.get_key_value()); aNode.pos_up(); bNode.pos_up(); } aNode.set_record_number(next_available); // did a split renumber // Node next_available++; anchor_write(); if (aNode.get_next() != null) aNode.set_keys_in_this_Node(j - 1); else aNode.set_keys_in_this_Node(j); aNode.set_pos(j - 1); aNode.write(); left = aNode.get_record_number(); if (aNode.get_prev() != null) { update_entry((Node) aNode.get_prev(), aNode.get_key_value(), left, right, 0); } else { bNode.set_pos(j - 1); update_entry(bNode, // pass it aNode for it 's creating a new // top Node bNode.get_key_value(), left, 0, right); } } bNode = null; } /* even if we did a split we don't exit because we still have to */ public void del_entry(Node inNode) throws IOException, xBaseJException { Node aNode; int pos, k; aNode = inNode; pos = aNode.get_pos(); k = pos; aNode.set_keys_in_this_Node(aNode.get_keys_in_this_Node() - 1); if (aNode.get_lower_level() != 0 // pointer node && pos <= aNode.get_keys_in_this_Node()) { for (k = pos - 1; k < aNode.get_keys_in_this_Node(); k++) { int level, rec; NodeKey key; aNode.pos_up(); level = aNode.get_lower_level(); rec = aNode.get_key_record_number(); key = aNode.get_key_value(); aNode.pos_down(); aNode.set_lower_level(level); aNode.set_key_record_number(rec); aNode.set_key_value(key); aNode.pos_up(); } /* endfor */ } /* endif */ else if (pos < aNode.get_keys_in_this_Node()) // record node { for (k = pos; k < aNode.get_keys_in_this_Node(); k++) { int level, rec; NodeKey key; aNode.pos_up(); level = aNode.get_lower_level(); rec = aNode.get_key_record_number(); key = aNode.get_key_value(); aNode.pos_down(); aNode.set_lower_level(level); aNode.set_key_record_number(rec); aNode.set_key_value(key); aNode.pos_up(); } /* endfor */ } /* endif */ if (aNode.get_prev() != null) // should we fix parent? { if (aNode.get_keys_in_this_Node() == 0) { if (aNode.get_lower_level() == 0) // record node so go fix its // parent del_entry(aNode.get_prev()); else ; // pointer node so don't fix unless negative } else { if (aNode.get_keys_in_this_Node() == -1) del_entry(aNode.get_prev()); } } aNode.set_pos(pos); aNode.write(); } public int get_next_key() throws xBaseJException, IOException { return get_next_key(workNode); } private int get_next_key(Node aNode) throws xBaseJException, IOException { int rec, until, leaf; if (aNode == null) return -1; aNode.pos_up(); leaf = aNode.get_lower_level(); if (leaf > 0) /* leaf pointers usually have one more pointer than shown */ until = aNode.get_keys_in_this_Node() + 1; else until = aNode.get_keys_in_this_Node(); if (aNode.get_pos() >= until) { Node rNode; rNode = aNode.get_prev(); rec = get_next_key(rNode); if (rec == -1) { aNode.set_pos(until); return -1; } /* endif */ workNode = aNode; aNode.set_record_number(rec); aNode.read(); aNode.set_pos(0); } /* endif */ leaf = aNode.get_lower_level(); workNode = aNode; if (leaf > 0) return (leaf); return (aNode.get_key_record_number()); } public int get_prev_key() throws xBaseJException, IOException { return get_prev_key(workNode); } private int get_prev_key(Node aNode) throws xBaseJException, IOException { int rec, until, leaf; if (aNode == null) return -1; if (aNode.get_pos() < 0) return -1; leaf = aNode.get_lower_level(); if (leaf > 0) /* leaf pointers usually have one more pointer than shown */ until = 1; else until = 0; if (aNode.get_pos() > -1) aNode.pos_down(); if (aNode.get_pos() < 0) { rec = get_prev_key(aNode.get_prev()); if (rec == -1) { return -1; } aNode.set_record_number(rec); aNode.read(); aNode.set_pos(aNode.get_keys_in_this_Node() + until); aNode.pos_down(); /* offset at zero not 1 */ } /* endif */ leaf = aNode.get_lower_level(); workNode = aNode; if (leaf > 0) { return (leaf); } return aNode.get_key_record_number(); } public void anchor_read() throws IOException { nfile.seek(0); top_Node = nfile.readInt(); next_available = nfile.readInt(); reserved_02 = nfile.readInt(); key_length = nfile.readShort(); key_per_Node = nfile.readShort(); keyType = (nfile.readShort() != 0) ? 'N' : 'C'; key_entry_size = nfile.readShort(); reserved_01 = nfile.readByte(); reserved_03 = nfile.readByte(); reserved_04 = nfile.readByte(); unique_key = nfile.readByte(); nfile.readFully(key_definition, 0, 488); redo_numbers(); } public void anchor_write() throws IOException { nfile.seek(0); redo_numbers(); nfile.writeInt(top_Node); nfile.writeInt(next_available); nfile.writeInt(reserved_02); nfile.writeShort(key_length); nfile.writeShort(key_per_Node); nfile.writeShort(keyType == 'N' ? 1 : 0); nfile.writeShort(key_entry_size); nfile.writeByte(reserved_01); nfile.writeByte(reserved_03); nfile.writeByte(reserved_04); nfile.writeByte(unique_key); nfile.write(key_definition, 0, 488); redo_numbers(); } public void redo_numbers() { top_Node = DbaseUtils.x86(top_Node); next_available = DbaseUtils.x86(next_available); key_length = DbaseUtils.x86(key_length); key_per_Node = DbaseUtils.x86(key_per_Node); // keyType = Util.x86(keyType); key_entry_size = DbaseUtils.x86(key_entry_size); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy