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