1 | /* ============================================================= |
2 | * SmallSQL : a free Java DBMS library for the Java(tm) platform |
3 | * ============================================================= |
4 | * |
5 | * (C) Copyright 2004-2006, by Volker Berlin. |
6 | * |
7 | * Project Info: http://www.smallsql.de/ |
8 | * |
9 | * This library is free software; you can redistribute it and/or modify it |
10 | * under the terms of the GNU Lesser General Public License as published by |
11 | * the Free Software Foundation; either version 2.1 of the License, or |
12 | * (at your option) any later version. |
13 | * |
14 | * This library is distributed in the hope that it will be useful, but |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
16 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public |
17 | * License for more details. |
18 | * |
19 | * You should have received a copy of the GNU Lesser General Public |
20 | * License along with this library; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, |
22 | * USA. |
23 | * |
24 | * [Java is a trademark or registered trademark of Sun Microsystems, Inc. |
25 | * in the United States and other countries.] |
26 | * |
27 | * --------------- |
28 | * IndexScrollStatus.java |
29 | * --------------- |
30 | * Author: Volker Berlin |
31 | * |
32 | * Created on 18.04.2005 |
33 | */ |
34 | package smallsql.database; |
35 | |
36 | /** |
37 | * This class save the status of the scrolling through a Index. This is like a Cursor. |
38 | */ |
39 | class IndexScrollStatus { |
40 | private final IndexNode rootPage; |
41 | private final Expressions expressions; // is used for the description of ASC and DESC |
42 | |
43 | private final java.util.Stack nodeStack = new java.util.Stack(); //TODO performance Stack durch nicht synchronisierte Klasse ersetzten |
44 | /** Used for getRowOffset() as context cash between 2 calls */ |
45 | private LongTreeList longList; |
46 | private LongTreeListEnum longListEnum = new LongTreeListEnum(); |
47 | |
48 | |
49 | IndexScrollStatus(IndexNode rootPage, Expressions expressions){ |
50 | this.rootPage = rootPage; |
51 | this.expressions= expressions; |
52 | reset(); |
53 | } |
54 | |
55 | |
56 | /** |
57 | * Reset this index to start a new scan of it with nextRowoffset() |
58 | */ |
59 | final void reset(){ |
60 | nodeStack.clear(); |
61 | boolean asc = (expressions.get(0).getAlias() != SQLTokenizer.DESC_STR); |
62 | nodeStack.push( new IndexNodeScrollStatus(rootPage, asc, true, 0) ); |
63 | } |
64 | |
65 | |
66 | /** |
67 | * Return the next rowOffset of this index. You need to call reset() before the first use. |
68 | * @param next if true the next rowOffset else the previous rowOffset |
69 | */ |
70 | final long getRowOffset( boolean scroll){ |
71 | if(longList != null){ |
72 | long rowOffset = scroll ? |
73 | longList.getNext(longListEnum) : |
74 | longList.getPrevious(longListEnum); |
75 | if(rowOffset < 0){ |
76 | // No more enties on this node |
77 | longList = null; |
78 | }else{ |
79 | return rowOffset; |
80 | } |
81 | } |
82 | while(true){ |
83 | IndexNodeScrollStatus status = (IndexNodeScrollStatus)nodeStack.peek(); |
84 | int level = status.level; |
85 | if(!status.asc ^ scroll){ |
86 | //ASC order |
87 | int idx = ++status.idx; |
88 | if(idx == -1){ |
89 | if(status.nodeValue != null){ |
90 | if(status.nodeValue instanceof IndexNode){ |
91 | level++; |
92 | nodeStack.push( |
93 | new IndexNodeScrollStatus( (IndexNode)status.nodeValue, |
94 | (expressions.get(level).getAlias() != SQLTokenizer.DESC_STR), |
95 | scroll, level)); |
96 | continue; |
97 | }else |
98 | return getReturnValue(status.nodeValue); |
99 | } |
100 | //There is no RowOffset in this node |
101 | idx = ++status.idx; |
102 | } |
103 | if(idx >= status.nodes.length){ |
104 | //No more nodes in this level |
105 | if(nodeStack.size() > 1){ |
106 | nodeStack.pop(); |
107 | continue; |
108 | }else{ |
109 | //No more RowOffsets in this Index |
110 | return -1; |
111 | } |
112 | } |
113 | IndexNode node = status.nodes[idx]; |
114 | nodeStack.push( new IndexNodeScrollStatus(node, status.asc, scroll, level) ); |
115 | }else{ |
116 | //DESC order |
117 | int idx = --status.idx; |
118 | if(idx == -1){ |
119 | if(status.nodeValue != null){ |
120 | if(status.nodeValue instanceof IndexNode){ |
121 | level++; |
122 | nodeStack.push( |
123 | new IndexNodeScrollStatus( (IndexNode)status.nodeValue, |
124 | (expressions.get(level).getAlias() != SQLTokenizer.DESC_STR), |
125 | scroll, level)); |
126 | continue; |
127 | }else |
128 | return getReturnValue(status.nodeValue); |
129 | } |
130 | //There is no RowOffset in this node |
131 | } |
132 | if(idx < 0){ |
133 | //No more nodes in this level |
134 | if(nodeStack.size() > 1){ |
135 | nodeStack.pop(); |
136 | continue; |
137 | }else{ |
138 | //No more RowOffsets in this Index |
139 | return -1; |
140 | } |
141 | } |
142 | IndexNode node = status.nodes[idx]; |
143 | nodeStack.push( new IndexNodeScrollStatus(node, status.asc, scroll, level) ); |
144 | } |
145 | } |
146 | } |
147 | |
148 | |
149 | /** |
150 | * Move the index after the last position. The next call nextRowOffset() returns a -1 |
151 | * |
152 | */ |
153 | final void afterLast(){ |
154 | longList = null; |
155 | nodeStack.setSize(1); |
156 | ((IndexNodeScrollStatus)nodeStack.peek()).afterLast(); |
157 | } |
158 | |
159 | |
160 | private final long getReturnValue( Object value){ |
161 | if(rootPage.getUnique()){ |
162 | return ((Long)value).longValue(); |
163 | }else{ |
164 | longList = (LongTreeList)value; |
165 | longListEnum.reset(); |
166 | return longList.getNext(longListEnum); // there be should one value as minimum |
167 | } |
168 | |
169 | } |
170 | |
171 | |
172 | |
173 | |
174 | } |