-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathBtree.java
616 lines (527 loc) · 19.8 KB
/
Btree.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
package hadoopApp;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Vector;
class Btree {
private static Node tree;
private static int degree;
private static boolean debug;
private Btree(int x) {
// a B+ Tree must have an initial degree
degree = x;
// The initial type of Node for a B+Tree is a leaf
tree = new LeafNode(degree);
debug = false;
}
private static void executeCommand(Command c, BufferedWriter output) throws InvalidCommandException, IOException {
// execute command, does as it says, calls the appropriate procedure to accomplish the command
// There are also some debug options to help the user see what's going on
switch( (int) c.getCommand() ) {
case 'd':
if( c.getXValue() == 1 && !debug) {
debug = true;
System.out.println("ENTERING DEBUG MODE");
}
else if ( c.getXValue() == 0 && debug) {
debug = false;
System.out.println("EXITTING DEBUG MODE");
}
else if (c.getXValue() != 0 || c.getXValue() != 1){
throw new InvalidCommandException("Invalid Operand with command d. Must be 0 or 1.");
}
case 'p':
if(debug) {
System.out.println("PRINTING TREE");
}
printTree(output);
break;
case 's':
if(debug) {
System.out.println("SEARCHING TREE FOR x = " + c.getXValue());
}
searchTree(c.getXValue(), output);
break;
case 'i':
if(debug) {
System.out.println("INSERTING x = " + c.getXValue() + " INTO THE TREE");
}
insertIntoTree(new DataNode(c.getXValue()));
break;
}
if(debug && (int)c.getCommand() != 'p') {
printTree(new BufferedWriter(new PrintWriter(System.out)));
System.out.println("--->OPERATION COMPLETE");
}
}
private static void insertIntoTree(DataNode dnode) {
tree = tree.insert(dnode);
}
private static void searchTree(int x, BufferedWriter output) throws IOException {
// search the tree starting from the top
if( tree.search(new DataNode(x)) ) {
output.write("FOUND" + System.getProperty("line.separator"));
}
else {
output.write("NOT FOUND" + System.getProperty("line.separator"));
}
}
@SuppressWarnings("unchecked")
private static void printTree(BufferedWriter output) throws IOException {
// create a vector to store all the nodes from each level as we
Vector<Node> nodeList = new Vector();
// put the root of the tree onto the stack to start the process
nodeList.add(tree);
boolean done = false;
while(! done) {
// this vector will hold all the children of the nodes in the current level
Vector<Node> nextLevelList = new Vector();
String toprint = "";
// for each node in the list convert it to a string and add any children to the nextlevel stack
for(int i=0; i < nodeList.size(); i++) {
// get the node at position i
Node node = (Node)nodeList.elementAt(i);
// convert the node into a string
toprint += node.toString() + " ";
// if this is a leaf node we need only print the contents
if(node.isLeafNode()) {
done = true;
}
// if this is a tree node print the contents and populate
// the temp vector with nodes that node i points to
else
{
for(int j=0; j < node.size()+1 ; j++) {
nextLevelList.add( ((TreeNode)node).getPointerAt(j) );
}
}
}
// print the level
output.write(toprint + System.getProperty("line.separator"));
// go to the next level and print it
nodeList = nextLevelList;
}
}
private static void readDegree(BufferedReader in) {
// get the tree's degree from input
try {
int x = Integer.parseInt(in.readLine().trim());
// set the degree of the tree
new Btree(x);
} catch (Exception e1) {
System.err.println("degree could not be read... defaulting to order 3");
new Btree(3);
}
}
public static void main(String[] args) throws IOException {
// if there are too many arguments error
if(args.length > 1) {
System.err.println("Syntax error in call sequence, use:\n\tjava BplusTree");
}
else {
File file = new File("Dairy.txt");
file.createNewFile();
FileWriter fw = new FileWriter(file);
// declare a reader on Standard in, incase the file reader fails
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
// create a new file to store output
BufferedWriter output = new BufferedWriter( new FileWriter(new File("bplustree.out")) );
try {
in = new BufferedReader(new InputStreamReader(new FileInputStream("bplustree.inp")));
} catch (FileNotFoundException e) {
System.err.println("Error: specified file not found (defaulting to standard input)");
}
// get the degree of the B+Tree
readDegree(in);
// declare a new command object
Command c = new Command();
// continue executing commands until quit is reached
while(c.getCommand() != 'q') {
try {
// read a command from input
c.readCommand(in);
// execute the command
executeCommand(c, output);
}
catch (IOException e) {
e.printStackTrace();
}
catch (InvalidCommandException e) {
System.err.println(e.getMessage());
System.out.println("Valid Query-Modes:\n\ti x - insert x into tree\n\ts x - find x in tree\n\tp - print tree\n\tq - quit");
}
catch (NumberFormatException e) {
System.err.println("This type of command requires a integer operand");
System.out.println("Valid Query-Modes:\n\ti x - insert x into tree\n\ts x - find x in tree\n\tp - print tree\n\tq - quit");
}
catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
}
// close input and output
output.close();
in.close();
// output.write("Exitting");
System.exit(0);
}
}
}
class Command {
int xvalue;
char command;
Command() {
command = 0;
xvalue = 0;
}
public String toString() {
return "command = " + command + " x = " + xvalue;
}
public void readCommand(BufferedReader in) throws InvalidCommandException, IOException, NumberFormatException {
boolean readcommand = false;
// until a command has been read (valid or invalid) get something from input
while(!readcommand && in.ready()) {
command = (char)in.read();
// if the line is a comment output the rest of the line
if(command == '#') {
// print the comment to the screen
System.out.println( in.readLine() );
}
// if a valid command was read get any necessary arguments
else if(this.validCommand()) {
if(this.commandWithArgument()) {
xvalue = Integer.parseInt(in.readLine().trim());
}
else {
xvalue = 0;
in.readLine();
}
readcommand = true;
}
else {
// clean up the rest of the garbage on the input line
in.readLine();
throw new InvalidCommandException(command);
}
}
}
public char getCommand() {
return command;
}
public int getXValue() {
return xvalue;
}
private boolean validCommand() {
return commandWithArgument() || commandWithoutArgument();
}
// list of commands that have an argument
private boolean commandWithArgument() {
return this.command == 'i' || this.command == 's' || this.command == 'd';
}
// list of commands that don't have arguments
private boolean commandWithoutArgument() {
return this.command == 'p' || this.command == 'q';
}
}
// Class to handle occurances of invalid commands
class InvalidCommandException extends Exception {
private static final long serialVersionUID = -2169157330841961180L;
InvalidCommandException(char command) {
super("Error: invalid query-mode \"" + command + "\" entered");
}
InvalidCommandException(String s) {
super(s);
}
}
// The node class is an abstract class
// its subclasses are LeafNode and TreeNode
abstract class Node {
// all nodes need data, parent, and a capacity
protected Vector<DataNode> data;
protected Node parent;
protected int maxsize;
public boolean isLeafNode() {
// determine if a node is a leafnode...
return this.getClass().getName().trim().equals("LeafNode");
}
// both types of node need to insert and search
abstract Node insert(DataNode dnode);
abstract boolean search(DataNode x);
protected boolean isFull() {
return data.size() == maxsize-1;
}
public DataNode getDataAt(int index) {
return (DataNode) data.elementAt(index);
}
protected void propagate(DataNode dnode, Node right) {
// propogate takes a piece of data and two pointers left(this) and right and pushes the data up the tree
// if there was no parent
if(parent == null) {
// create a new parent
TreeNode newparent = new TreeNode(maxsize);
// add the necessary data and pointers
newparent.data.add(dnode);
newparent.pointer.add(this);
newparent.pointer.add(right);
// update the parent information for right and left
this.setParent(newparent);
right.setParent(newparent);
}
else {
// if the parent is not full
if( ! parent.isFull() ) {
// add the necessary data and pointers to existing parent
boolean dnodeinserted = false;
for(int i = 0; !dnodeinserted && i < parent.data.size(); i++) {
if( ((DataNode)parent.data.elementAt(i)).inOrder(dnode) ) {
parent.data.add(i,dnode);
((TreeNode)parent).pointer.add(i+1, right);
dnodeinserted = true;
}
}
if(!dnodeinserted) {
parent.data.add(dnode);
((TreeNode)parent).pointer.add(right);
}
// set the necessary parent on the right node, left.parent is already set
right.setParent(this.parent);
}
// the parent is full
else {
// split will take car of setting the parent of both nodes, because
// there are 3 different ways the parents need to be set
((TreeNode)parent).split(dnode, this, right);
}
}
}
public int size() {
return data.size();
}
@SuppressWarnings("unchecked") Node(int degree) {
// initially the parent node is null
parent = null;
data = new Vector();
maxsize = degree;
}
// Convert a node to a string
public String toString() {
String s = "";
for(int i=0; i < data.size(); i++) {
s += ((DataNode)data.elementAt(i)).toString() + " ";
}
return s + "#";
}
// this operation traverses the tree using the parent nodes until the parent is null and returns the node
protected Node findRoot() {
Node node = this;
while(node.parent != null) {
node = node.parent;
}
return node;
}
protected void setParent(Node newparent) {
this.parent = newparent;
}
}
class LeafNode extends Node {
private LeafNode nextNode;
LeafNode(int degree) {
super(degree);
// initially the nextnode is null
nextNode = null;
}
private void setNextNode(LeafNode next) {
nextNode = next;
}
protected LeafNode getNextNode() {
return nextNode;
}
public boolean search(DataNode x) {
// search through the data sequentially until x is found, or there are no more entries
for(int i=0; i < data.size(); i++) {
if( ((DataNode)data.elementAt(i)).getData() == x.getData() ) {
return true;
}
}
return false;
}
protected void split(DataNode dnode) {
// insert dnode into the vector (it will now be overpacked)
boolean dnodeinserted = false;
for(int i=0; !dnodeinserted && i < data.size(); i++) {
if( ((DataNode)data.elementAt(i)).inOrder(dnode) ) {
data.add(i,dnode);
dnodeinserted = true;
}
}
if(!dnodeinserted) {
data.add(data.size(), dnode);
}
// calculate the split point; ceiling(maxsize/2)
int splitlocation;
if(maxsize%2 == 0) {
splitlocation = maxsize/2;
}
else {
splitlocation = (maxsize+1)/2;
}
// create new LeafNode
LeafNode right = new LeafNode(maxsize);
for(int i = data.size()-splitlocation; i > 0; i--) {
right.data.add(data.remove(splitlocation));
}
// link the two together
right.setNextNode(this.getNextNode());
this.setNextNode(right);
// get the middle item's data
DataNode mid = (DataNode) data.elementAt(data.size()-1);
// propagate the data and pointers into the parent node
this.propagate(mid, right);
}
public Node insert(DataNode dnode) {
// if the leaf isn't full insert it at the proper place
if(data.size() < maxsize-1) {
boolean dnodeinserted = false;
int i = 0;
while(!dnodeinserted && i < data.size()) {
if( ((DataNode)data.elementAt(i)).inOrder(dnode) ) {
data.add(i,dnode);
dnodeinserted = true;
}
i++;
}
if(!dnodeinserted) {
data.add(data.size(), dnode);
}
}
// if the leaf is full split
else {
this.split(dnode);
}
// return the root of the tree
return this.findRoot();
}
}
class TreeNode extends Node {
protected Vector<Node> pointer;
// constructor for TreeNode
// x-1 is the maximum # of DataNodes a single node can store
@SuppressWarnings("unchecked") TreeNode(int x) {
super(x);
pointer = new Vector();
}
// this will find the correct pointer to the next lowest level of the tree where x should be found
public Node getPointerTo(DataNode x) {
// find the index i where x would be located
int i = 0;
boolean xptrfound = false;
while(!xptrfound && i < data.size()) {
if( ((DataNode)data.elementAt(i)).inOrder(x ) ) {
xptrfound = true;
}
else {
i++;
}
}
// return the Node in pointer(i)
return (Node) pointer.elementAt(i);
}
// returns the pointer at a specific index in the pointer stack
public Node getPointerAt(int index) {
return (Node) pointer.elementAt(index);
}
boolean search(DataNode dnode) {
// get a pointer to where dnode.data should be found
Node next = this.getPointerTo(dnode);
// recursive call to find dnode.data if it is present
return next.search(dnode);
}
protected void split(DataNode dnode, Node left, Node right) {
// calculate the split point ( floor(maxsize/2)
int splitlocation, insertlocation = 0;
if(maxsize%2 == 0) {
splitlocation = maxsize/2;
}
else {
splitlocation = (maxsize+1)/2 -1;
}
// insert dnode into the vector (it will now be overpacked)
boolean dnodeinserted = false;
for(int i=0; !dnodeinserted && i < data.size(); i++) {
if( ((DataNode)data.elementAt(i)).inOrder(dnode) ) {
data.add(i,dnode);
((TreeNode)this).pointer.remove(i);
((TreeNode)this).pointer.add(i, left);
((TreeNode)this).pointer.add(i+1, right);
dnodeinserted = true;
// set the location of the insert this will be used to set the parent
insertlocation = i;
}
}
if(!dnodeinserted) {
// set the location of the insert this will be used to set the parent
insertlocation = data.size();
data.add(dnode);
((TreeNode)this).pointer.remove(((TreeNode)this).pointer.size()-1);
((TreeNode)this).pointer.add(left);
((TreeNode)this).pointer.add(right);
}
// get the middle dataNode
DataNode mid = (DataNode) data.remove(splitlocation);
// create a new tree node to accomodate the split
TreeNode newright = new TreeNode(maxsize);
// populate the data and pointers of the new right node
for(int i=data.size()-splitlocation; i > 0; i--) {
newright.data.add(this.data.remove(splitlocation));
newright.pointer.add(this.pointer.remove(splitlocation+1));
}
newright.pointer.add(this.pointer.remove(splitlocation+1));
// set the parents of right and left
// if the item was inserted before the split point both nodes are children of left
if(insertlocation < splitlocation) {
left.setParent(this);
right.setParent(this);
}
// if the item was inserted at the splitpoint the nodes have different parents this and right
else if(insertlocation == splitlocation) {
left.setParent(this);
right.setParent(newright);
}
// if the item was was inserted past the splitpoint the nodes are children of right
else {
left.setParent(newright);
right.setParent(newright);
}
// propogate the node up
this.propagate(mid, newright);
}
Node insert(DataNode dnode) {
Node next = this.getPointerTo(dnode);
return next.insert(dnode);
}
}
class DataNode {
// I chose Integer because it allows a null value, unlike int
private Integer data;
DataNode() {
data = null;
}
public String toString() {
return data.toString();
}
public DataNode(int x) {
data = x;
}
public int getData() {
return data.intValue();
}
public boolean inOrder(DataNode dnode) {
return (dnode.getData() <= this.data.intValue());
}
}