blob: 2b11c92edd7fc8dd8d1fd892c66820b24ad0a7d4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.accumulo.access;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.accumulo.access.ByteUtils.isBackslashSymbol;
import static org.apache.accumulo.access.ByteUtils.isQuoteOrSlash;
import static org.apache.accumulo.access.ByteUtils.isQuoteSymbol;
import java.util.stream.IntStream;
/**
* A simple wrapper around a byte array that keeps some state and provides high level operations to
* the {@link ParserEvaluator} class. The purpose of this class is to make {@link ParserEvaluator}
* as simple and easy to understand as possible while still being performant.
*/
final class Tokenizer {
private static final boolean[] validAuthChars = new boolean[256];
static {
IntStream.range(0, 256).forEach(i -> validAuthChars[i] = false);
IntStream numbers = IntStream.rangeClosed('0', '9');
IntStream letters =
IntStream.concat(IntStream.rangeClosed('A', 'Z'), IntStream.rangeClosed('a', 'z'));
IntStream.concat(numbers, letters).forEach(i -> validAuthChars[i] = true);
"_-:./".chars().forEach(c -> validAuthChars[c] = true);
}
static boolean isValidAuthChar(byte b) {
return validAuthChars[0xff & b];
}
private byte[] expression;
private int index;
private final AuthorizationToken authorizationToken = new AuthorizationToken();
static class AuthorizationToken {
byte[] data;
int start;
int len;
}
Tokenizer(byte[] expression) {
this.expression = expression;
authorizationToken.data = expression;
}
public void reset(byte[] expression) {
this.expression = expression;
authorizationToken.data = expression;
this.index = 0;
}
boolean hasNext() {
return index < expression.length;
}
public void advance() {
index++;
}
public void next(byte expected) {
if (!hasNext()) {
error("Expected '" + (char) expected + "' instead saw end of input");
}
if (expression[index] != expected) {
error("Expected '" + (char) expected + "' instead saw '" + (char) (expression[index]) + "'");
}
index++;
}
public void error(String msg) {
error(msg, index);
}
public void error(String msg, int idx) {
throw new InvalidAccessExpressionException(msg, new String(expression, UTF_8), idx);
}
byte peek() {
return expression[index];
}
public byte[] expression() {
return expression;
}
public int curentOffset() {
return index;
}
AuthorizationToken nextAuthorization(boolean includeQuotes) {
if (isQuoteSymbol(expression[index])) {
int start = ++index;
while (index < expression.length && !isQuoteSymbol(expression[index])) {
if (isBackslashSymbol(expression[index])) {
index++;
if (index == expression.length || !isQuoteOrSlash(expression[index])) {
error("Invalid escaping within quotes", index - 1);
}
}
index++;
}
if (index == expression.length) {
error("Unclosed quote", start - 1);
}
if (start == index) {
error("Empty authorization token in quotes", start - 1);
}
authorizationToken.start = start;
authorizationToken.len = index - start;
if (includeQuotes) {
authorizationToken.start--;
authorizationToken.len += 2;
}
index++;
return authorizationToken;
} else if (isValidAuthChar(expression[index])) {
int start = index;
while (index < expression.length && isValidAuthChar(expression[index])) {
index++;
}
authorizationToken.start = start;
authorizationToken.len = index - start;
return authorizationToken;
} else {
error(
"Expected a '(' character or an authorization token instead saw '" + (char) peek() + "'");
return null;
}
}
}