1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.rest;
22
23 import java.util.Collection;
24 import java.util.TreeSet;
25
26 import org.apache.hadoop.hbase.HColumnDescriptor;
27 import org.apache.hadoop.hbase.HConstants;
28 import org.apache.hadoop.hbase.util.Bytes;
29
30
31
32
33
34
35
36 public class RowSpec {
37 public static final long DEFAULT_START_TIMESTAMP = 0;
38 public static final long DEFAULT_END_TIMESTAMP = Long.MAX_VALUE;
39
40 private byte[] row = HConstants.EMPTY_START_ROW;
41 private byte[] endRow = null;
42 private TreeSet<byte[]> columns =
43 new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
44 private long startTime = DEFAULT_START_TIMESTAMP;
45 private long endTime = DEFAULT_END_TIMESTAMP;
46 private int maxVersions = HColumnDescriptor.DEFAULT_VERSIONS;
47 private int maxValues = Integer.MAX_VALUE;
48
49 public RowSpec(String path) throws IllegalArgumentException {
50 int i = 0;
51 while (path.charAt(i) == '/') {
52 i++;
53 }
54 i = parseRowKeys(path, i);
55 i = parseColumns(path, i);
56 i = parseTimestamp(path, i);
57 i = parseQueryParams(path, i);
58 }
59
60 private int parseRowKeys(final String path, int i)
61 throws IllegalArgumentException {
62 StringBuilder startRow = new StringBuilder();
63 StringBuilder endRow = null;
64 try {
65 char c;
66 boolean doEndRow = false;
67 while (i < path.length() && (c = path.charAt(i)) != '/') {
68 if (c == ',') {
69 doEndRow = true;
70 i++;
71 break;
72 }
73 startRow.append(c);
74 i++;
75 }
76 i++;
77 this.row = Bytes.toBytes(startRow.toString());
78 if (doEndRow) {
79 endRow = new StringBuilder();
80 while ((c = path.charAt(i)) != '/') {
81 endRow.append(c);
82 i++;
83 }
84 i++;
85 }
86 } catch (IndexOutOfBoundsException e) {
87 throw new IllegalArgumentException(e);
88 }
89
90
91
92 if (startRow.charAt(startRow.length() - 1) == '*') {
93 if (endRow != null)
94 throw new IllegalArgumentException("invalid path: start row "+
95 "specified with wildcard");
96 this.row = Bytes.toBytes(startRow.substring(0,
97 startRow.lastIndexOf("*")));
98 this.endRow = new byte[this.row.length + 1];
99 System.arraycopy(this.row, 0, this.endRow, 0, this.row.length);
100 this.endRow[this.row.length] = (byte)255;
101 } else {
102 this.row = Bytes.toBytes(startRow.toString());
103 if (endRow != null) {
104 this.endRow = Bytes.toBytes(endRow.toString());
105 }
106 }
107 return i;
108 }
109
110 private int parseColumns(final String path, int i)
111 throws IllegalArgumentException {
112 if (i >= path.length()) {
113 return i;
114 }
115 try {
116 char c;
117 StringBuilder column = new StringBuilder();
118 boolean hasColon = false;
119 while (i < path.length() && (c = path.charAt(i)) != '/') {
120 if (c == ',') {
121 if (column.length() < 1) {
122 throw new IllegalArgumentException("invalid path");
123 }
124 if (!hasColon) {
125 column.append(':');
126 }
127 this.columns.add(Bytes.toBytes(column.toString()));
128 column = new StringBuilder();
129 hasColon = false;
130 i++;
131 continue;
132 }
133 if (c == ':') {
134 hasColon = true;
135 }
136 column.append(c);
137 i++;
138 }
139 i++;
140
141 if (column.length() > 1) {
142 if (!hasColon) {
143 column.append(':');
144 }
145 this.columns.add(Bytes.toBytes(column.toString()));
146 }
147 } catch (IndexOutOfBoundsException e) {
148 throw new IllegalArgumentException(e);
149 }
150 return i;
151 }
152
153 private int parseTimestamp(final String path, int i)
154 throws IllegalArgumentException {
155 if (i >= path.length()) {
156 return i;
157 }
158 long time0 = 0, time1 = 0;
159 try {
160 char c = 0;
161 StringBuilder stamp = new StringBuilder();
162 while (i < path.length()) {
163 c = path.charAt(i);
164 if (c == '/' || c == ',') {
165 break;
166 }
167 stamp.append(c);
168 i++;
169 }
170 try {
171 time0 = Long.valueOf(stamp.toString());
172 } catch (NumberFormatException e) {
173 throw new IllegalArgumentException(e);
174 }
175 if (c == ',') {
176 stamp = new StringBuilder();
177 i++;
178 while (i < path.length() && ((c = path.charAt(i)) != '/')) {
179 stamp.append(c);
180 i++;
181 }
182 try {
183 time1 = Long.valueOf(stamp.toString());
184 } catch (NumberFormatException e) {
185 throw new IllegalArgumentException(e);
186 }
187 }
188 if (c == '/') {
189 i++;
190 }
191 } catch (IndexOutOfBoundsException e) {
192 throw new IllegalArgumentException(e);
193 }
194 if (time1 != 0) {
195 startTime = time0;
196 endTime = time1;
197 } else {
198 endTime = time0;
199 }
200 return i;
201 }
202
203 private int parseQueryParams(final String path, int i) {
204 while (i < path.length()) {
205 char c = path.charAt(i);
206 if (c != '?' && c != '&') {
207 break;
208 }
209 if (++i > path.length()) {
210 break;
211 }
212 char what = path.charAt(i);
213 if (++i > path.length()) {
214 break;
215 }
216 c = path.charAt(i);
217 if (c != '=') {
218 throw new IllegalArgumentException("malformed query parameter");
219 }
220 if (++i > path.length()) {
221 break;
222 }
223 switch (what) {
224 case 'm': {
225 StringBuilder sb = new StringBuilder();
226 while (i <= path.length()) {
227 c = path.charAt(i);
228 if (c < '0' || c > '9') {
229 i--;
230 break;
231 }
232 sb.append(c);
233 }
234 maxVersions = Integer.valueOf(sb.toString());
235 } break;
236 case 'n': {
237 StringBuilder sb = new StringBuilder();
238 while (i <= path.length()) {
239 c = path.charAt(i);
240 if (c < '0' || c > '9') {
241 i--;
242 break;
243 }
244 sb.append(c);
245 }
246 maxValues = Integer.valueOf(sb.toString());
247 } break;
248 default:
249 throw new IllegalArgumentException("unknown parameter '" + c + "'");
250 }
251 }
252 return i;
253 }
254
255 public RowSpec(byte[] startRow, byte[] endRow, byte[][] columns,
256 long startTime, long endTime, int maxVersions) {
257 this.row = startRow;
258 this.endRow = endRow;
259 if (columns != null) {
260 for (byte[] col: columns) {
261 this.columns.add(col);
262 }
263 }
264 this.startTime = startTime;
265 this.endTime = endTime;
266 this.maxVersions = maxVersions;
267 }
268
269 public RowSpec(byte[] startRow, byte[] endRow, Collection<byte[]> columns,
270 long startTime, long endTime, int maxVersions) {
271 this.row = startRow;
272 this.endRow = endRow;
273 if (columns != null) {
274 this.columns.addAll(columns);
275 }
276 this.startTime = startTime;
277 this.endTime = endTime;
278 this.maxVersions = maxVersions;
279 }
280
281 public boolean isSingleRow() {
282 return endRow == null;
283 }
284
285 public int getMaxVersions() {
286 return maxVersions;
287 }
288
289 public void setMaxVersions(final int maxVersions) {
290 this.maxVersions = maxVersions;
291 }
292
293 public int getMaxValues() {
294 return maxValues;
295 }
296
297 public void setMaxValues(final int maxValues) {
298 this.maxValues = maxValues;
299 }
300
301 public boolean hasColumns() {
302 return !columns.isEmpty();
303 }
304
305 public byte[] getRow() {
306 return row;
307 }
308
309 public byte[] getStartRow() {
310 return row;
311 }
312
313 public boolean hasEndRow() {
314 return endRow != null;
315 }
316
317 public byte[] getEndRow() {
318 return endRow;
319 }
320
321 public void addColumn(final byte[] column) {
322 columns.add(column);
323 }
324
325 public byte[][] getColumns() {
326 return columns.toArray(new byte[columns.size()][]);
327 }
328
329 public boolean hasTimestamp() {
330 return (startTime == 0) && (endTime != Long.MAX_VALUE);
331 }
332
333 public long getTimestamp() {
334 return endTime;
335 }
336
337 public long getStartTime() {
338 return startTime;
339 }
340
341 public void setStartTime(final long startTime) {
342 this.startTime = startTime;
343 }
344
345 public long getEndTime() {
346 return endTime;
347 }
348
349 public void setEndTime(long endTime) {
350 this.endTime = endTime;
351 }
352
353 public String toString() {
354 StringBuilder result = new StringBuilder();
355 result.append("{startRow => '");
356 if (row != null) {
357 result.append(Bytes.toString(row));
358 }
359 result.append("', endRow => '");
360 if (endRow != null) {
361 result.append(Bytes.toString(endRow));
362 }
363 result.append("', columns => [");
364 for (byte[] col: columns) {
365 result.append(" '");
366 result.append(Bytes.toString(col));
367 result.append("'");
368 }
369 result.append(" ], startTime => ");
370 result.append(Long.toString(startTime));
371 result.append(", endTime => ");
372 result.append(Long.toString(endTime));
373 result.append(", maxVersions => ");
374 result.append(Integer.toString(maxVersions));
375 result.append(", maxValues => ");
376 result.append(Integer.toString(maxValues));
377 result.append("}");
378 return result.toString();
379 }
380 }