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