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.HConstants;
29 import org.apache.hadoop.hbase.util.Bytes;
30
31
32
33
34
35
36
37 @InterfaceAudience.Private
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 = 1;
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) throws IllegalArgumentException {
110 if (i >= path.length()) {
111 return i;
112 }
113 try {
114 char c;
115 StringBuilder column = new StringBuilder();
116 while (i < path.length() && (c = path.charAt(i)) != '/') {
117 if (c == ',') {
118 if (column.length() < 1) {
119 throw new IllegalArgumentException("invalid path");
120 }
121 String s = URLDecoder.decode(column.toString(), HConstants.UTF8_ENCODING);
122 this.columns.add(Bytes.toBytes(s));
123 column.setLength(0);
124 i++;
125 continue;
126 }
127 column.append(c);
128 i++;
129 }
130 i++;
131
132 if (column.length() > 0) {
133 String s = URLDecoder.decode(column.toString(), HConstants.UTF8_ENCODING);
134 this.columns.add(Bytes.toBytes(s));
135 }
136 } catch (IndexOutOfBoundsException e) {
137 throw new IllegalArgumentException(e);
138 } catch (UnsupportedEncodingException e) {
139
140 throw new RuntimeException(e);
141 }
142 return i;
143 }
144
145 private int parseTimestamp(final String path, int i)
146 throws IllegalArgumentException {
147 if (i >= path.length()) {
148 return i;
149 }
150 long time0 = 0, time1 = 0;
151 try {
152 char c = 0;
153 StringBuilder stamp = new StringBuilder();
154 while (i < path.length()) {
155 c = path.charAt(i);
156 if (c == '/' || c == ',') {
157 break;
158 }
159 stamp.append(c);
160 i++;
161 }
162 try {
163 time0 = Long.valueOf(URLDecoder.decode(stamp.toString(),
164 HConstants.UTF8_ENCODING));
165 } catch (NumberFormatException e) {
166 throw new IllegalArgumentException(e);
167 }
168 if (c == ',') {
169 stamp = new StringBuilder();
170 i++;
171 while (i < path.length() && ((c = path.charAt(i)) != '/')) {
172 stamp.append(c);
173 i++;
174 }
175 try {
176 time1 = Long.valueOf(URLDecoder.decode(stamp.toString(),
177 HConstants.UTF8_ENCODING));
178 } catch (NumberFormatException e) {
179 throw new IllegalArgumentException(e);
180 }
181 }
182 if (c == '/') {
183 i++;
184 }
185 } catch (IndexOutOfBoundsException e) {
186 throw new IllegalArgumentException(e);
187 } catch (UnsupportedEncodingException e) {
188
189 throw new RuntimeException(e);
190 }
191 if (time1 != 0) {
192 startTime = time0;
193 endTime = time1;
194 } else {
195 endTime = time0;
196 }
197 return i;
198 }
199
200 private int parseQueryParams(final String path, int i) {
201 if (i >= path.length()) {
202 return i;
203 }
204 StringBuilder query = new StringBuilder();
205 try {
206 query.append(URLDecoder.decode(path.substring(i),
207 HConstants.UTF8_ENCODING));
208 } catch (UnsupportedEncodingException e) {
209
210 throw new RuntimeException(e);
211 }
212 i += query.length();
213 int j = 0;
214 while (j < query.length()) {
215 char c = query.charAt(j);
216 if (c != '?' && c != '&') {
217 break;
218 }
219 if (++j > query.length()) {
220 throw new IllegalArgumentException("malformed query parameter");
221 }
222 char what = query.charAt(j);
223 if (++j > query.length()) {
224 break;
225 }
226 c = query.charAt(j);
227 if (c != '=') {
228 throw new IllegalArgumentException("malformed query parameter");
229 }
230 if (++j > query.length()) {
231 break;
232 }
233 switch (what) {
234 case 'm': {
235 StringBuilder sb = new StringBuilder();
236 while (j <= query.length()) {
237 c = query.charAt(j);
238 if (c < '0' || c > '9') {
239 j--;
240 break;
241 }
242 sb.append(c);
243 }
244 maxVersions = Integer.valueOf(sb.toString());
245 } break;
246 case 'n': {
247 StringBuilder sb = new StringBuilder();
248 while (j <= query.length()) {
249 c = query.charAt(j);
250 if (c < '0' || c > '9') {
251 j--;
252 break;
253 }
254 sb.append(c);
255 }
256 maxValues = Integer.valueOf(sb.toString());
257 } break;
258 default:
259 throw new IllegalArgumentException("unknown parameter '" + c + "'");
260 }
261 }
262 return i;
263 }
264
265 public RowSpec(byte[] startRow, byte[] endRow, byte[][] columns,
266 long startTime, long endTime, int maxVersions) {
267 this.row = startRow;
268 this.endRow = endRow;
269 if (columns != null) {
270 for (byte[] col: columns) {
271 this.columns.add(col);
272 }
273 }
274 this.startTime = startTime;
275 this.endTime = endTime;
276 this.maxVersions = maxVersions;
277 }
278
279 public RowSpec(byte[] startRow, byte[] endRow, Collection<byte[]> columns,
280 long startTime, long endTime, int maxVersions) {
281 this.row = startRow;
282 this.endRow = endRow;
283 if (columns != null) {
284 this.columns.addAll(columns);
285 }
286 this.startTime = startTime;
287 this.endTime = endTime;
288 this.maxVersions = maxVersions;
289 }
290
291 public boolean isSingleRow() {
292 return endRow == null;
293 }
294
295 public int getMaxVersions() {
296 return maxVersions;
297 }
298
299 public void setMaxVersions(final int maxVersions) {
300 this.maxVersions = maxVersions;
301 }
302
303 public int getMaxValues() {
304 return maxValues;
305 }
306
307 public void setMaxValues(final int maxValues) {
308 this.maxValues = maxValues;
309 }
310
311 public boolean hasColumns() {
312 return !columns.isEmpty();
313 }
314
315 public byte[] getRow() {
316 return row;
317 }
318
319 public byte[] getStartRow() {
320 return row;
321 }
322
323 public boolean hasEndRow() {
324 return endRow != null;
325 }
326
327 public byte[] getEndRow() {
328 return endRow;
329 }
330
331 public void addColumn(final byte[] column) {
332 columns.add(column);
333 }
334
335 public byte[][] getColumns() {
336 return columns.toArray(new byte[columns.size()][]);
337 }
338
339 public boolean hasTimestamp() {
340 return (startTime == 0) && (endTime != Long.MAX_VALUE);
341 }
342
343 public long getTimestamp() {
344 return endTime;
345 }
346
347 public long getStartTime() {
348 return startTime;
349 }
350
351 public void setStartTime(final long startTime) {
352 this.startTime = startTime;
353 }
354
355 public long getEndTime() {
356 return endTime;
357 }
358
359 public void setEndTime(long endTime) {
360 this.endTime = endTime;
361 }
362
363 public String toString() {
364 StringBuilder result = new StringBuilder();
365 result.append("{startRow => '");
366 if (row != null) {
367 result.append(Bytes.toString(row));
368 }
369 result.append("', endRow => '");
370 if (endRow != null) {
371 result.append(Bytes.toString(endRow));
372 }
373 result.append("', columns => [");
374 for (byte[] col: columns) {
375 result.append(" '");
376 result.append(Bytes.toString(col));
377 result.append("'");
378 }
379 result.append(" ], startTime => ");
380 result.append(Long.toString(startTime));
381 result.append(", endTime => ");
382 result.append(Long.toString(endTime));
383 result.append(", maxVersions => ");
384 result.append(Integer.toString(maxVersions));
385 result.append(", maxValues => ");
386 result.append(Integer.toString(maxValues));
387 result.append("}");
388 return result.toString();
389 }
390 }