KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > activemq > kaha > impl > index > tree > TreePage


1 /**
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
4  * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
5  * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
6  * License. You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
11  * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
12  * specific language governing permissions and limitations under the License.
13  */

14
15 package org.apache.activemq.kaha.impl.index.tree;
16
17 import java.io.DataInput JavaDoc;
18 import java.io.DataOutput JavaDoc;
19 import java.io.IOException JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.HashSet JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.Set JavaDoc;
24 import org.apache.activemq.kaha.Marshaller;
25
26 /**
27  * Page in a BTree
28  *
29  * @version $Revision: 1.1.1.1 $
30  */

31 class TreePage{
32
33     static final int PAGE_HEADER_SIZE=18;
34
35     static enum Flavour{
36         LESS,MORE
37     }
38     private TreeIndex tree;
39     private int maximumEntries;
40     private long id;
41     private long parentId=TreeEntry.NOT_SET;
42     private boolean leaf=true;
43     private List JavaDoc<TreeEntry> treeEntries;
44     /*
45      * for persistence only
46      */

47     private long nextFreePageId=TreeEntry.NOT_SET;
48     private boolean active=true;
49
50     /**
51      * Constructor
52      *
53      * @param tree
54      * @param id
55      * @param parentId
56      * @param maximumEntries
57      */

58     TreePage(TreeIndex tree,long id,long parentId,int maximumEntries){
59         this(maximumEntries);
60         this.tree=tree;
61         this.id=id;
62         this.parentId=parentId;
63     }
64
65     /**
66      * Constructor
67      *
68      * @param maximumEntries
69      */

70     public TreePage(int maximumEntries){
71         this.maximumEntries=maximumEntries;
72         this.treeEntries=new ArrayList JavaDoc<TreeEntry>(maximumEntries);
73     }
74
75     public String JavaDoc toString(){
76         return "TreePage["+getId()+"]parent="+getParentId();
77     }
78
79     public boolean equals(Object JavaDoc o){
80         boolean result=false;
81         if(o instanceof TreePage){
82             TreePage other=(TreePage)o;
83             result=other.id==id;
84         }
85         return result;
86     }
87
88     public int hashCode(){
89         return (int)id;
90     }
91
92     boolean isActive(){
93         return this.active;
94     }
95
96     void setActive(boolean active){
97         this.active=active;
98     }
99
100     long getNextFreePageId(){
101         return this.nextFreePageId;
102     }
103
104     void setNextFreePageId(long nextPageId){
105         this.nextFreePageId=nextPageId;
106     }
107
108     long getId(){
109         return id;
110     }
111
112     void setId(long id){
113         this.id=id;
114     }
115
116     void write(Marshaller keyMarshaller,DataOutput JavaDoc dataOut) throws IOException JavaDoc{
117         writeHeader(dataOut);
118         dataOut.writeInt(treeEntries.size());
119         for(TreeEntry entry:treeEntries){
120             entry.write(keyMarshaller,dataOut);
121         }
122     }
123
124     void read(Marshaller keyMarshaller,DataInput JavaDoc dataIn) throws IOException JavaDoc{
125         readHeader(dataIn);
126         int size=dataIn.readInt();
127         treeEntries.clear();
128         for(int i=0;i<size;i++){
129             TreeEntry entry=new TreeEntry();
130             entry.read(keyMarshaller,dataIn);
131             treeEntries.add(entry);
132         }
133     }
134
135     void readHeader(DataInput JavaDoc dataIn) throws IOException JavaDoc{
136         active=dataIn.readBoolean();
137         leaf=dataIn.readBoolean();
138         setParentId(dataIn.readLong());
139         nextFreePageId=dataIn.readLong();
140     }
141
142     void writeHeader(DataOutput JavaDoc dataOut) throws IOException JavaDoc{
143         dataOut.writeBoolean(isActive());
144         dataOut.writeBoolean(isLeaf());
145         dataOut.writeLong(getParentId());
146         dataOut.writeLong(nextFreePageId);
147     }
148
149     boolean isEmpty(){
150         return treeEntries.isEmpty();
151     }
152
153     boolean isFull(){
154         return(treeEntries.size()>=maximumEntries);
155     }
156
157     boolean isRoot(){
158         return getParentId()<0;
159     }
160
161     boolean isLeaf(){
162         if(treeEntries.isEmpty()){
163             leaf=true;
164         }
165         return leaf;
166     }
167
168     boolean isUnderflowed(){
169         return treeEntries.size()<(maximumEntries/2);
170     }
171
172     boolean isOverflowed(){
173         return treeEntries.size()>maximumEntries;
174     }
175
176     void setLeaf(boolean newValue){
177         this.leaf=newValue;
178     }
179
180     TreePage getParent() throws IOException JavaDoc{
181         return tree.lookupPage(parentId);
182     }
183
184     long getParentId(){
185         return parentId;
186     }
187
188     void setParentId(long newId) throws IOException JavaDoc{
189         if(newId==this.id){
190             throw new IllegalStateException JavaDoc("Cannot set page as a child of itself "+this+" trying to set parentId = "
191                     +newId);
192         }
193         this.parentId=newId;
194         tree.writePage(this);
195     }
196
197     List JavaDoc<TreeEntry> getEntries(){
198         return treeEntries;
199     }
200
201     void setEntries(List JavaDoc<TreeEntry> newEntries){
202         this.treeEntries=newEntries;
203     }
204
205     int getMaximumEntries(){
206         return this.maximumEntries;
207     }
208
209     void setMaximumEntries(int maximumEntries){
210         this.maximumEntries=maximumEntries;
211     }
212
213     int size(){
214         return treeEntries.size();
215     }
216
217     TreeIndex getTree(){
218         return this.tree;
219     }
220
221     void setTree(TreeIndex tree){
222         this.tree=tree;
223     }
224
225     void reset() throws IOException JavaDoc{
226         treeEntries.clear();
227         setParentId(TreeEntry.NOT_SET);
228         setNextFreePageId(TreeEntry.NOT_SET);
229         setLeaf(true);
230     }
231
232     public TreeEntry find(TreeEntry key) throws IOException JavaDoc{
233         int low=0;
234         int high=size()-1;
235         long pageId=-1;
236         while(low<=high){
237             int mid=(low+high)>>1;
238             TreeEntry te=getTreeEntry(mid);
239             int cmp=te.compareTo(key);
240             if(cmp==0){
241                 return te;
242             }else if(cmp<0){
243                 low=mid+1;
244                 pageId=te.getNextPageId();
245             }else{
246                 high=mid-1;
247                 pageId=te.getPrevPageId();
248             }
249         }
250         TreePage page=tree.lookupPage(pageId);
251         if(page!=null){
252             return page.find(key);
253         }
254         return null;
255     }
256
257     TreeEntry put(TreeEntry newEntry) throws IOException JavaDoc{
258         TreeEntry result=null;
259         if(isRoot()){
260             if(isEmpty()){
261                 insertTreeEntry(0,newEntry);
262             }else{
263                 result=doInsert(null,newEntry);
264             }
265         }else{
266             throw new IllegalStateException JavaDoc("insert() should not be called on non root page - "+this);
267         }
268         return result;
269     }
270
271     TreeEntry remove(TreeEntry entry) throws IOException JavaDoc{
272         TreeEntry result = null;
273         if(isRoot()){
274             if(!isEmpty()){
275                 result = doRemove(entry);
276             }
277         }else{
278             throw new IllegalStateException JavaDoc("remove() should not be called on non root page");
279         }
280         return result;
281     }
282
283     private TreeEntry doInsert(Flavour flavour,TreeEntry newEntry) throws IOException JavaDoc{
284         TreeEntry result=null;
285         TreePageEntry closest=findClosestEntry(newEntry);
286         if(closest!=null){
287             TreeEntry closestEntry=closest.getTreeEntry();
288             TreePage closestPage=closest.getTreePage();
289             int cmp=closestEntry.compareTo(newEntry);
290             if(cmp==0){
291                 // we actually just need to pass back the value
292
long oldValue=closestEntry.getIndexOffset();
293                 closestEntry.setIndexOffset(newEntry.getIndexOffset());
294                 newEntry.setIndexOffset(oldValue);
295                 result=newEntry;
296                 save();
297             }else if(closestPage!=null){
298                 result=closestPage.doInsert(closest.getFlavour(),newEntry);
299             }else{
300                 if(!isFull()){
301                     insertTreeEntry(closest.getIndex(),newEntry);
302                     save();
303                 }else{
304                     doOverflow(flavour,newEntry);
305                 }
306             }
307         }else{
308             if(!isFull()){
309                 doInsertEntry(newEntry);
310                 save();
311             }else{
312                 // need to insert the new entry and propogate up the hightest value
313
doOverflow(flavour,newEntry);
314             }
315         }
316         return result;
317     }
318
319     private TreePage doOverflow(Flavour flavour,TreeEntry newEntry) throws IOException JavaDoc{
320         TreePage result=this;
321         TreeEntry theEntry=newEntry;
322         if(!isFull()){
323             doInsertEntry(newEntry);
324             save();
325         }else{
326             if(!isRoot()&&flavour!=null){
327                 // we aren't the root, but to ensure the correct distribution we need to
328
// insert the new entry and take a node of the end of the page
329
// and pass that up the tree to find a home
330
doInsertEntry(newEntry);
331                 if(flavour==Flavour.LESS){
332                     theEntry=removeTreeEntry(0);
333                     theEntry.reset();
334                     theEntry.setNextPageId(getId());
335                 }else{
336                     theEntry=removeTreeEntry(size()-1);
337                     theEntry.reset();
338                     theEntry.setPrevPageId(getId());
339                 }
340                 save();
341                 
342                 result=getParent().doOverflow(flavour,theEntry);
343                 if (!theEntry.equals(newEntry)) {
344                     //the newEntry stayed here
345
result = this;
346                 }
347             }else{
348                 // so we are the root and need to split
349
doInsertEntry(newEntry);
350                 int midIndex=(size()/2);
351                 TreeEntry midEntry=removeTreeEntry(midIndex);
352                 List JavaDoc<TreeEntry> subList=getSubList(midIndex,size());
353                 removeAllTreeEntries(subList);
354                 TreePage newRoot=tree.createRoot();
355                 newRoot.setLeaf(false);
356                 this.setParentId(newRoot.getId());
357                 save(); // we are no longer root - need to save - we maybe looked up v. soon!
358
TreePage rightPage=tree.createPage(newRoot.getId());
359                 rightPage.setEntries(subList);
360                 rightPage.checkLeaf();
361                 resetParentId(rightPage.getId(),rightPage.getEntries());
362                 midEntry.setNextPageId(rightPage.getId());
363                 midEntry.setPrevPageId(this.getId());
364                 newRoot.insertTreeEntry(0,midEntry);
365                 resetParentId(newRoot.getId(),newRoot.getEntries());
366                 save();
367                 rightPage.save();
368                 newRoot.save();
369             }
370         }
371         return result;
372     }
373
374     private TreeEntry doRemove(TreeEntry entry) throws IOException JavaDoc{
375         TreeEntry result = null;
376         TreePageEntry closest=findClosestEntry(entry);
377         if(closest!=null){
378             TreeEntry closestEntry=closest.getTreeEntry();
379             if(closestEntry!=null){
380                 TreePage closestPage=closest.getTreePage();
381                 int cmp=closestEntry.compareTo(entry);
382                 if(cmp==0){
383                     result=closest.getTreeEntry();
384                     int index=closest.getIndex();
385                     removeTreeEntry(index);
386                     save();
387                     // ensure we don't loose children
388
doUnderflow(result,index);
389                 }else if(closestPage!=null){
390                     closestPage.doRemove(entry);
391                 }
392             }
393         }
394         return result;
395     }
396
397     /**
398      * @return true if the page is removed
399      * @throws IOException
400      */

401     private boolean doUnderflow() throws IOException JavaDoc{
402         boolean result=false;
403         boolean working=true;
404         while(working&&isUnderflowed()&&!isEmpty()&&!isLeaf()){
405             int lastIndex=size()-1;
406             TreeEntry entry=getTreeEntry(lastIndex);
407             working=doUnderflow(entry,lastIndex);
408         }
409         if(isUnderflowed()&&isLeaf()){
410             result=doUnderflowLeaf();
411         }
412         return result;
413     }
414
415     private boolean doUnderflow(TreeEntry entry,int index) throws IOException JavaDoc{
416         boolean result=false;
417         // pull an entry up from a leaf to fill the empty space
418
if(entry.getNextPageId()!=TreeEntry.NOT_SET){
419             TreePage page=tree.lookupPage(entry.getNextPageId());
420             if(page!=null&&!page.isEmpty()){
421                 TreeEntry replacement=page.removeTreeEntry(0);
422                 TreeEntry copy=replacement.copy();
423                 checkParentIdForRemovedPageEntry(copy,page.getId(),getId());
424                 if(!page.isEmpty()){
425                     copy.setNextPageId(page.getId());
426                     page.setParentId(this.id);
427                 }else{
428                     page.setLeaf(true);
429                 }
430                 int replacementIndex=doInsertEntry(copy);
431                 if(page.doUnderflow()){
432                     // page removed so update our replacement
433
resetPageReference(replacementIndex,copy.getNextPageId());
434                     copy.setNextPageId(TreeEntry.NOT_SET);
435                 }else{
436                     page.save();
437                 }
438                 save();
439                 result=true;
440             }
441         }
442         // ensure we don't loose previous bit of the tree
443
if(entry.getPrevPageId()!=TreeEntry.NOT_SET){
444             TreeEntry prevEntry=(index>0)?getTreeEntry(index-1):null;
445             if(prevEntry==null||prevEntry.getNextPageId()!=entry.getPrevPageId()){
446                 TreePage page=tree.lookupPage(entry.getPrevPageId());
447                 if(page!=null&&!page.isEmpty()){
448                     TreeEntry replacement=page.removeTreeEntry(page.getEntries().size()-1);
449                     TreeEntry copy=replacement.copy();
450                     // check children pages of the replacement point to the correct place
451
checkParentIdForRemovedPageEntry(copy,page.getId(),getId());
452                     if(!page.isEmpty()){
453                         copy.setPrevPageId(page.getId());
454                     }else{
455                         page.setLeaf(true);
456                     }
457                     insertTreeEntry(index,copy);
458                     TreePage landed=null;// if we overflow - the page the replacement ends up on
459
TreeEntry removed=null;
460                     if(isOverflowed()){
461                         TreePage parent=getParent();
462                         if(parent!=null){
463                             removed=getTreeEntry(0);
464                             Flavour flavour=getFlavour(parent,removed);
465                             if(flavour==Flavour.LESS){
466                                 removed=removeTreeEntry(0);
467                                 landed=parent.doOverflow(flavour,removed);
468                             }else{
469                                 removed=removeTreeEntry(size()-1);
470                                 landed=parent.doOverflow(Flavour.MORE,removed);
471                             }
472                         }
473                     }
474                     if(page.doUnderflow()){
475                         if(landed==null||landed.equals(this)){
476                            landed=this;
477                         }
478                         
479                         resetPageReference(copy.getNextPageId());
480                         landed.resetPageReference(copy.getNextPageId());
481                         copy.setPrevPageId(TreeEntry.NOT_SET);
482                         landed.save();
483                     }else{
484                         page.save();
485                     }
486                     save();
487                     result=true;
488                 }
489                 // now we need to check we haven't overflowed this page
490
}
491         }
492         if(!result){
493             save();
494         }
495         // now see if we need to save this page
496
result|=doUnderflowLeaf();
497         save();
498         return result;
499     }
500
501     private boolean doUnderflowLeaf() throws IOException JavaDoc{
502         boolean result=false;
503         // if we have unerflowed - and we are a leaf - push entries further up the tree
504
// and delete ourselves
505
if(isUnderflowed()&&isLeaf()){
506             List JavaDoc<TreeEntry> list=new ArrayList JavaDoc<TreeEntry>(treeEntries);
507             treeEntries.clear();
508             for(TreeEntry entry:list){
509                 // need to check for each iteration - we might get promoted to root
510
TreePage parent=getParent();
511                 if(parent!=null){
512                     Flavour flavour=getFlavour(parent,entry);
513                     TreePage landedOn=parent.doOverflow(flavour,entry);
514                     checkParentIdForRemovedPageEntry(entry,getId(),landedOn.getId());
515                 }
516             }
517             TreePage parent=getParent();
518             if(parent!=null){
519                 parent.checkLeaf();
520                 parent.removePageId(getId());
521                 parent.doUnderflow();
522                 parent.save();
523                 tree.releasePage(this);
524                 result=true;
525             }
526         }
527         return result;
528     }
529
530     private Flavour getFlavour(TreePage page,TreeEntry entry){
531         Flavour result=null;
532         if(page!=null&&!page.getEntries().isEmpty()){
533             TreeEntry last=page.getEntries().get(page.getEntries().size()-1);
534             if(last.compareTo(entry)>0){
535                 result=Flavour.MORE;
536             }else{
537                 result=Flavour.LESS;
538             }
539         }
540         return result;
541     }
542
543     private void checkLeaf(){
544         boolean result=false;
545         for(TreeEntry entry:treeEntries){
546             if(entry.hasChildPagesReferences()){
547                 result=true;
548                 break;
549             }
550         }
551         setLeaf(!result);
552     }
553
554     private void checkParentIdForRemovedPageEntry(TreeEntry entry,long oldPageId,long newPageId) throws IOException JavaDoc{
555         TreePage page=tree.lookupPage(entry.getPrevPageId());
556         if(page!=null&&page.getParentId()==oldPageId){
557             page.setParentId(newPageId);
558             page.save();
559         }
560         page=tree.lookupPage(entry.getNextPageId());
561         if(page!=null&&page.getParentId()==oldPageId){
562             page.setParentId(newPageId);
563             page.save();
564         }
565     }
566
567     private void removePageId(long pageId){
568         for(TreeEntry entry:treeEntries){
569             if(entry.getNextPageId()==pageId){
570                 entry.setNextPageId(TreeEntry.NOT_SET);
571             }
572             if(entry.getPrevPageId()==pageId){
573                 entry.setPrevPageId(TreeEntry.NOT_SET);
574             }
575         }
576     }
577
578     private TreePageEntry findClosestEntry(TreeEntry key) throws IOException JavaDoc{
579         TreePageEntry result=null;
580         TreeEntry treeEntry=null;
581         Flavour flavour=null;
582         long pageId=-1;
583         int low=0;
584         int high=size()-1;
585         int mid=low;
586         while(low<=high){
587             mid=(low+high)>>1;
588             treeEntry=getTreeEntry(mid);
589             int cmp=treeEntry.compareTo(key);
590             if(cmp<0){
591                 low=mid+1;
592                 pageId=treeEntry.getNextPageId();
593                 flavour=Flavour.LESS;
594             }else if(cmp>0){
595                 high=mid-1;
596                 pageId=treeEntry.getPrevPageId();
597                 flavour=Flavour.MORE;
598             }else{
599                 // got exact match
600
low=mid;
601                 break;
602             }
603         }
604         if(treeEntry!=null){
605             TreePage treePage=tree.lookupPage(pageId);
606             result=new TreePageEntry(treeEntry,treePage,flavour,low);
607         }
608         return result;
609     }
610
611     private int doInsertEntry(TreeEntry newEntry) throws IOException JavaDoc{
612         int low=0;
613         int high=size()-1;
614         while(low<=high){
615             int mid=(low+high)>>1;
616             TreeEntry midVal=getTreeEntry(mid);
617             int cmp=midVal.compareTo(newEntry);
618             if(cmp<0)
619                 low=mid+1;
620             else if(cmp>0)
621                 high=mid-1;
622         }
623         insertTreeEntry(low,newEntry);
624         return low;
625     }
626
627     private void insertTreeEntry(int index,TreeEntry entry) throws IOException JavaDoc{
628         int p=index-1;
629         int n=index;
630         TreeEntry prevEntry=(p>=0&&p<treeEntries.size())?treeEntries.get(p):null;
631         TreeEntry nextEntry=(n>=0&&n<treeEntries.size())?treeEntries.get(n):null;
632         if(prevEntry!=null){
633             if(prevEntry.getNextPageId()==entry.getNextPageId()){
634                 prevEntry.setNextPageId(TreeEntry.NOT_SET);
635             }
636             if(entry.getPrevPageId()==TreeEntry.NOT_SET){
637                 entry.setPrevPageId(prevEntry.getNextPageId());
638             }
639         }
640         if(nextEntry!=null){
641             if(nextEntry.getPrevPageId()==entry.getPrevPageId()){
642                 nextEntry.setPrevPageId(TreeEntry.NOT_SET);
643             }
644             if(entry.getNextPageId()==TreeEntry.NOT_SET){
645                 entry.setNextPageId(nextEntry.getPrevPageId());
646             }
647         }
648         addTreeEntry(index,entry);
649     }
650
651     private void resetPageReference(int index,long pageId){
652         int p=index-1;
653         int n=index;
654         TreeEntry prevEntry=(p>=0&&p<treeEntries.size())?treeEntries.get(p):null;
655         TreeEntry nextEntry=(n>=0&&n<treeEntries.size())?treeEntries.get(n):null;
656         if(prevEntry!=null){
657             if(prevEntry.getNextPageId()==pageId){
658                 prevEntry.setNextPageId(TreeEntry.NOT_SET);
659             }
660         }
661         if(nextEntry!=null){
662             if(nextEntry.getPrevPageId()==pageId){
663                 nextEntry.setPrevPageId(TreeEntry.NOT_SET);
664             }
665         }
666     }
667
668     private boolean resetPageReference(long pageId){
669         boolean updated=false;
670         for(TreeEntry entry:treeEntries){
671             if(entry.getPrevPageId()==pageId){
672                 entry.setPrevPageId(TreeEntry.NOT_SET);
673                 updated=true;
674             }
675             if(entry.getNextPageId()==pageId){
676                 entry.setNextPageId(TreeEntry.NOT_SET);
677                 updated=true;
678             }
679         }
680         return updated;
681     }
682
683     private void resetParentId(long newParentId,List JavaDoc<TreeEntry> entries) throws IOException JavaDoc{
684         Set JavaDoc<Long JavaDoc> set=new HashSet JavaDoc<Long JavaDoc>();
685         for(TreeEntry entry:entries){
686             if(entry!=null){
687                 set.add(entry.getPrevPageId());
688                 set.add(entry.getNextPageId());
689             }
690         }
691         for(Long JavaDoc pageId:set){
692             TreePage page=tree.lookupPage(pageId);
693             if(page!=null){
694                 page.setParentId(newParentId);
695             }
696         }
697     }
698
699     private void addTreeEntry(int index,TreeEntry entry) throws IOException JavaDoc{
700         treeEntries.add(index,entry);
701     }
702
703     private TreeEntry removeTreeEntry(int index) throws IOException JavaDoc{
704         TreeEntry result=treeEntries.remove(index);
705         return result;
706     }
707
708     private void removeAllTreeEntries(List JavaDoc<TreeEntry> c){
709         treeEntries.removeAll(c);
710     }
711
712     private List JavaDoc<TreeEntry> getSubList(int from,int to){
713         return new ArrayList JavaDoc<TreeEntry>(treeEntries.subList(from,to));
714     }
715
716     private TreeEntry getTreeEntry(int index){
717         TreeEntry result=treeEntries.get(index);
718         return result;
719     }
720
721     void saveHeader() throws IOException JavaDoc{
722         tree.writePage(this);
723     }
724
725     void save() throws IOException JavaDoc{
726         tree.writeFullPage(this);
727     }
728
729     protected void dump() throws IOException JavaDoc{
730         System.out.println(this);
731         Set JavaDoc<Long JavaDoc> set=new HashSet JavaDoc<Long JavaDoc>();
732         for(TreeEntry entry:treeEntries){
733             if(entry!=null){
734                 System.out.println(entry);
735                 set.add(entry.getPrevPageId());
736                 set.add(entry.getNextPageId());
737             }
738         }
739         for(Long JavaDoc pageId:set){
740             TreePage page=tree.lookupPage(pageId);
741             if(page!=null){
742                 page.dump();
743             }
744         }
745     }
746 }
747
Popular Tags