/*
* Copyright © 2014 Cask Data, Inc.
*
* Licensed 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
*
* http://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 co.cask.cdap.logging.filter;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.io.StreamTokenizer;
import java.io.StringReader;
/**
* Parses string expression into a @{link Filter}.
*/
public final class FilterParser {
private enum Operator {
AND, OR
}
public static Filter parse(String expression) {
StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(expression));
// Treat : as part of word
tokenizer.wordChars((int) ':', (int) ':');
// Treat End of Line as whitespace
tokenizer.eolIsSignificant(false);
// Reset special handling of numerals
tokenizer.ordinaryChars((int) '0', (int) '9');
tokenizer.ordinaryChar((int) '.');
tokenizer.ordinaryChar((int) '-');
tokenizer.wordChars((int) '0', (int) '9');
tokenizer.wordChars((int) '.', (int) '.');
// Check if empty expression
try {
tokenizer.nextToken();
if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
return Filter.EMPTY_FILTER;
} else if (tokenizer.ttype == (int) '\'' || tokenizer.ttype == (int) '"') {
// Empty quoted string - '' or ""
if (tokenizer.sval.isEmpty()) {
return Filter.EMPTY_FILTER;
}
}
} catch (IOException e) {
throw Throwables.propagate(e);
}
// Not an empty expression
tokenizer.pushBack();
return parseExpression(tokenizer);
}
private static Filter parseExpression(StreamTokenizer tokenizer) {
Filter operand1 = parseOperand(tokenizer);
Operator operator = parseOperator(tokenizer);
if (operator == null) {
return operand1;
}
Filter operand2 = parseExpression(tokenizer);
return createFilter(operand1, operand2, operator);
}
private static Filter parseOperand(StreamTokenizer tokenizer) {
String key = parseString(tokenizer);
parseEquals(tokenizer);
String value = parseString(tokenizer);
// Generate expression
if (key.startsWith(".")) {
// System tag
return new MdcExpression(key, value);
} else if (key.startsWith("MDC:")) {
// User MDC tag
return new MdcExpression(key, value);
} else if (key.equals("loglevel")) {
// Log level
return new LogLevelExpression(value);
} else {
throw new IllegalArgumentException(String.format("Unknown expression of type %s", key));
}
}
private static String parseString(StreamTokenizer tokenizer) {
try {
tokenizer.nextToken();
if (tokenizer.ttype != StreamTokenizer.TT_EOF) {
switch (tokenizer.ttype) {
// handle operands
case (int) '\'':
case (int) '"':
case StreamTokenizer.TT_WORD:
return tokenizer.sval;
default:
throw new IllegalStateException(String.format("Expected operand but got %s", (char) tokenizer.ttype));
}
}
} catch (IOException e) {
throw Throwables.propagate(e);
}
throw new IllegalStateException("Expected operand but got end of expression");
}
private static void parseEquals(StreamTokenizer tokenizer) {
try {
tokenizer.nextToken();
if (tokenizer.ttype != StreamTokenizer.TT_EOF) {
switch (tokenizer.ttype) {
// handle equals
case (int) '=':
return;
default:
throw new IllegalStateException(String.format("Expected operator = but got %s", (char) tokenizer.ttype));
}
}
} catch (IOException e) {
throw Throwables.propagate(e);
}
throw new IllegalStateException("Expected operator = but got end of expression");
}
private static Operator parseOperator(StreamTokenizer tokenizer) {
try {
tokenizer.nextToken();
if (tokenizer.ttype != StreamTokenizer.TT_EOF) {
switch (tokenizer.ttype) {
// handle operator
case StreamTokenizer.TT_WORD:
return Operator.valueOf(tokenizer.sval);
default:
throw new IllegalStateException(String.format("Expected operator = but got %s", (char) tokenizer.ttype));
}
}
} catch (IOException e) {
throw Throwables.propagate(e);
}
// No operator present
return null;
}
private static Filter createFilter(Filter operand1, Filter operand2, Operator operator) {
if (operator == null) {
return Filter.EMPTY_FILTER;
}
switch (operator) {
case AND:
return new AndFilter(ImmutableList.of(operand1, operand2));
case OR:
return new OrFilter(ImmutableList.of(operand1, operand2));
default:
throw new UnsupportedOperationException(String.format("Operator %s not supported", operator));
}
}
}