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