Revert "HBASE-26523 Upgrade hbase-thirdparty dependency to 4.0.0 (#3910)"
[hbase.git] / hbase-rest / src / main / java / org / apache / hadoop / hbase / rest / RowSpec.java
blobc510c9ed797d172fe9d95022aa8b1f22b06fa096
1 /*
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;
34 /**
35 * Parses a path based row/column/timestamp specification into its component
36 * elements.
37 * <p>
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 {
55 int i = 0;
56 while (path.charAt(i) == '/') {
57 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;
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++;
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);
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
93 // table scanning
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());
109 return i;
112 private int parseColumns(final String path, int i) throws IllegalArgumentException {
113 if (i >= path.length()) {
114 return i;
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");
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;
130 column.append(c);
131 i++;
133 i++;
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) {
142 // shouldn't happen
143 throw new RuntimeException(e);
145 return i;
148 private int parseTimestamp(final String path, int i)
149 throws IllegalArgumentException {
150 if (i >= path.length()) {
151 return i;
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;
162 stamp.append(c);
163 i++;
165 try {
166 time0 = Long.parseLong(URLDecoder.decode(stamp.toString(),
167 HConstants.UTF8_ENCODING));
168 } catch (NumberFormatException e) {
169 throw new IllegalArgumentException(e);
171 if (c == ',') {
172 stamp = new StringBuilder();
173 i++;
174 while (i < path.length() && ((c = path.charAt(i)) != '/')) {
175 stamp.append(c);
176 i++;
178 try {
179 time1 = Long.parseLong(URLDecoder.decode(stamp.toString(),
180 HConstants.UTF8_ENCODING));
181 } catch (NumberFormatException e) {
182 throw new IllegalArgumentException(e);
185 if (c == '/') {
186 i++;
188 } catch (IndexOutOfBoundsException e) {
189 throw new IllegalArgumentException(e);
190 } catch (UnsupportedEncodingException e) {
191 // shouldn't happen
192 throw new RuntimeException(e);
194 if (time1 != 0) {
195 startTime = time0;
196 endTime = time1;
197 } else {
198 endTime = time0;
200 return i;
203 private int parseQueryParams(final String path, int i) {
204 if (i >= path.length()) {
205 return i;
207 StringBuilder query = new StringBuilder();
208 try {
209 query.append(URLDecoder.decode(path.substring(i),
210 HConstants.UTF8_ENCODING));
211 } catch (UnsupportedEncodingException e) {
212 // should not happen
213 throw new RuntimeException(e);
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;
222 if (++j > query.length()) {
223 throw new IllegalArgumentException("malformed query parameter");
225 char what = query.charAt(j);
226 if (++j > query.length()) {
227 break;
229 c = query.charAt(j);
230 if (c != '=') {
231 throw new IllegalArgumentException("malformed query parameter");
233 if (++j > query.length()) {
234 break;
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;
245 sb.append(c);
247 maxVersions = Integer.parseInt(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;
257 sb.append(c);
259 maxValues = Integer.parseInt(sb.toString());
260 } break;
261 default:
262 throw new IllegalArgumentException("unknown parameter '" + c + "'");
265 return i;
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 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);
283 if(labels != null) {
284 this.labels.addAll(labels);
287 public RowSpec(byte[] startRow, byte[] endRow, Collection<byte[]> columns,
288 long startTime, long endTime, int maxVersions) {
289 this.row = startRow;
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() {
304 return maxVersions;
307 public void setMaxVersions(final int maxVersions) {
308 this.maxVersions = maxVersions;
311 public int getMaxValues() {
312 return maxValues;
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() {
328 return row;
331 public byte[] getStartRow() {
332 return row;
335 public boolean hasEndRow() {
336 return endRow != null;
339 public byte[] getEndRow() {
340 return endRow;
343 public void addColumn(final byte[] column) {
344 columns.add(column);
347 public byte[][] getColumns() {
348 return columns.toArray(new byte[columns.size()][]);
351 public List<String> getLabels() {
352 return labels;
355 public boolean hasTimestamp() {
356 return (startTime == 0) && (endTime != Long.MAX_VALUE);
359 public long getTimestamp() {
360 return endTime;
363 public long getStartTime() {
364 return startTime;
367 public void setStartTime(final long startTime) {
368 this.startTime = startTime;
371 public long getEndTime() {
372 return endTime;
375 public void setEndTime(long endTime) {
376 this.endTime = endTime;
379 @Override
380 public String toString() {
381 StringBuilder result = new StringBuilder();
382 result.append("{startRow => '");
383 if (row != null) {
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) {
392 result.append(" '");
393 result.append(Bytes.toString(col));
394 result.append("'");
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();