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