3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 package org
.apache
.hadoop
.hbase
.rest
;
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
;
30 import org
.apache
.yetus
.audience
.InterfaceAudience
;
31 import org
.apache
.hadoop
.hbase
.HConstants
;
32 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
35 * Parses a path based row/column/timestamp specification into its component
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
;
45 private byte[] row
= HConstants
.EMPTY_START_ROW
;
46 private byte[] endRow
= null;
47 private TreeSet
<byte[]> columns
= new TreeSet
<>(Bytes
.BYTES_COMPARATOR
);
48 private List
<String
> labels
= new ArrayList
<>();
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
;
54 public RowSpec(String path
) throws IllegalArgumentException
{
56 while (path
.charAt(i
) == '/') {
59 i
= parseRowKeys(path
, i
);
60 i
= parseColumns(path
, i
);
61 i
= parseTimestamp(path
, i
);
62 i
= parseQueryParams(path
, i
);
65 private int parseRowKeys(final String path
, int i
)
66 throws IllegalArgumentException
{
67 String startRow
= null, endRow
= null;
69 StringBuilder sb
= new StringBuilder();
71 while (i
< path
.length() && (c
= path
.charAt(i
)) != '/') {
76 String row
= startRow
= sb
.toString();
77 int idx
= startRow
.indexOf(',');
79 startRow
= URLDecoder
.decode(row
.substring(0, idx
),
80 HConstants
.UTF8_ENCODING
);
81 endRow
= URLDecoder
.decode(row
.substring(idx
+ 1),
82 HConstants
.UTF8_ENCODING
);
84 startRow
= URLDecoder
.decode(row
, HConstants
.UTF8_ENCODING
);
86 } catch (IndexOutOfBoundsException e
) {
87 throw new IllegalArgumentException(e
);
88 } catch (UnsupportedEncodingException e
) {
89 throw new RuntimeException(e
);
91 // HBase does not support wildcards on row keys so we will emulate a
92 // suffix glob by synthesizing appropriate start and end row keys for
94 if (startRow
.charAt(startRow
.length() - 1) == '*') {
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;
104 this.row
= Bytes
.toBytes(startRow
.toString());
105 if (endRow
!= null) {
106 this.endRow
= Bytes
.toBytes(endRow
.toString());
112 private int parseColumns(final String path
, int i
) throws IllegalArgumentException
{
113 if (i
>= path
.length()) {
118 StringBuilder column
= new StringBuilder();
119 while (i
< path
.length() && (c
= path
.charAt(i
)) != '/') {
121 if (column
.length() < 1) {
122 throw new IllegalArgumentException("invalid path");
124 String s
= URLDecoder
.decode(column
.toString(), HConstants
.UTF8_ENCODING
);
125 this.columns
.add(Bytes
.toBytes(s
));
134 // trailing list entry
135 if (column
.length() > 0) {
136 String s
= URLDecoder
.decode(column
.toString(), HConstants
.UTF8_ENCODING
);
137 this.columns
.add(Bytes
.toBytes(s
));
139 } catch (IndexOutOfBoundsException e
) {
140 throw new IllegalArgumentException(e
);
141 } catch (UnsupportedEncodingException e
) {
143 throw new RuntimeException(e
);
148 private int parseTimestamp(final String path
, int i
)
149 throws IllegalArgumentException
{
150 if (i
>= path
.length()) {
153 long time0
= 0, time1
= 0;
156 StringBuilder stamp
= new StringBuilder();
157 while (i
< path
.length()) {
159 if (c
== '/' || c
== ',') {
166 time0
= Long
.parseLong(URLDecoder
.decode(stamp
.toString(),
167 HConstants
.UTF8_ENCODING
));
168 } catch (NumberFormatException e
) {
169 throw new IllegalArgumentException(e
);
172 stamp
= new StringBuilder();
174 while (i
< path
.length() && ((c
= path
.charAt(i
)) != '/')) {
179 time1
= Long
.parseLong(URLDecoder
.decode(stamp
.toString(),
180 HConstants
.UTF8_ENCODING
));
181 } catch (NumberFormatException e
) {
182 throw new IllegalArgumentException(e
);
188 } catch (IndexOutOfBoundsException e
) {
189 throw new IllegalArgumentException(e
);
190 } catch (UnsupportedEncodingException e
) {
192 throw new RuntimeException(e
);
203 private int parseQueryParams(final String path
, int i
) {
204 if (i
>= path
.length()) {
207 StringBuilder query
= new StringBuilder();
209 query
.append(URLDecoder
.decode(path
.substring(i
),
210 HConstants
.UTF8_ENCODING
));
211 } catch (UnsupportedEncodingException e
) {
213 throw new RuntimeException(e
);
217 while (j
< query
.length()) {
218 char c
= query
.charAt(j
);
219 if (c
!= '?' && c
!= '&') {
222 if (++j
> query
.length()) {
223 throw new IllegalArgumentException("malformed query parameter");
225 char what
= query
.charAt(j
);
226 if (++j
> query
.length()) {
231 throw new IllegalArgumentException("malformed query parameter");
233 if (++j
> query
.length()) {
238 StringBuilder sb
= new StringBuilder();
239 while (j
<= query
.length()) {
241 if (c
< '0' || c
> '9') {
247 maxVersions
= Integer
.parseInt(sb
.toString());
250 StringBuilder sb
= new StringBuilder();
251 while (j
<= query
.length()) {
253 if (c
< '0' || c
> '9') {
259 maxValues
= Integer
.parseInt(sb
.toString());
262 throw new IllegalArgumentException("unknown parameter '" + c
+ "'");
268 public RowSpec(byte[] startRow
, byte[] endRow
, byte[][] columns
,
269 long startTime
, long endTime
, int maxVersions
) {
271 this.endRow
= endRow
;
272 if (columns
!= null) {
273 Collections
.addAll(this.columns
, columns
);
275 this.startTime
= startTime
;
276 this.endTime
= endTime
;
277 this.maxVersions
= maxVersions
;
280 public RowSpec(byte[] startRow
, byte[] endRow
, Collection
<byte[]> columns
,
281 long startTime
, long endTime
, int maxVersions
, Collection
<String
> labels
) {
282 this(startRow
, endRow
, columns
, startTime
, endTime
, maxVersions
);
284 this.labels
.addAll(labels
);
287 public RowSpec(byte[] startRow
, byte[] endRow
, Collection
<byte[]> columns
,
288 long startTime
, long endTime
, int maxVersions
) {
290 this.endRow
= endRow
;
291 if (columns
!= null) {
292 this.columns
.addAll(columns
);
294 this.startTime
= startTime
;
295 this.endTime
= endTime
;
296 this.maxVersions
= maxVersions
;
299 public boolean isSingleRow() {
300 return endRow
== null;
303 public int getMaxVersions() {
307 public void setMaxVersions(final int maxVersions
) {
308 this.maxVersions
= maxVersions
;
311 public int getMaxValues() {
315 public void setMaxValues(final int maxValues
) {
316 this.maxValues
= maxValues
;
319 public boolean hasColumns() {
320 return !columns
.isEmpty();
323 public boolean hasLabels() {
324 return !labels
.isEmpty();
327 public byte[] getRow() {
331 public byte[] getStartRow() {
335 public boolean hasEndRow() {
336 return endRow
!= null;
339 public byte[] getEndRow() {
343 public void addColumn(final byte[] column
) {
347 public byte[][] getColumns() {
348 return columns
.toArray(new byte[columns
.size()][]);
351 public List
<String
> getLabels() {
355 public boolean hasTimestamp() {
356 return (startTime
== 0) && (endTime
!= Long
.MAX_VALUE
);
359 public long getTimestamp() {
363 public long getStartTime() {
367 public void setStartTime(final long startTime
) {
368 this.startTime
= startTime
;
371 public long getEndTime() {
375 public void setEndTime(long endTime
) {
376 this.endTime
= endTime
;
380 public String
toString() {
381 StringBuilder result
= new StringBuilder();
382 result
.append("{startRow => '");
384 result
.append(Bytes
.toString(row
));
386 result
.append("', endRow => '");
387 if (endRow
!= null) {
388 result
.append(Bytes
.toString(endRow
));
390 result
.append("', columns => [");
391 for (byte[] col
: columns
) {
393 result
.append(Bytes
.toString(col
));
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
));
405 return result
.toString();