You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ds...@apache.org on 2015/01/22 18:24:12 UTC
[1/2] ambari git commit: AMBARI-8605 Query predicate .matches doesn't
work for stacks endpoint with passed logical OR(URL-encoding) (dsen)
Repository: ambari
Updated Branches:
refs/heads/trunk 9f296a25c -> 0edce3c4f
AMBARI-8605 Query predicate .matches doesn't work for stacks endpoint with passed logical OR(URL-encoding) (dsen)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/0edce3c4
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/0edce3c4
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/0edce3c4
Branch: refs/heads/trunk
Commit: 0edce3c4f275b2ebca042e1fd8fb877acb7c5336
Parents: f77be4c
Author: Dmytro Sen <ds...@apache.org>
Authored: Thu Jan 22 18:24:19 2015 +0200
Committer: Dmytro Sen <ds...@apache.org>
Committed: Thu Jan 22 19:23:35 2015 +0200
----------------------------------------------------------------------
.../api/services/ResultPostProcessorImpl.java | 13 +++++-
.../server/api/services/StacksService.java | 47 ++++++++++++++------
.../server/api/services/StacksServiceTest.java | 19 +++++++-
3 files changed, 63 insertions(+), 16 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/0edce3c4/ambari-server/src/main/java/org/apache/ambari/server/api/services/ResultPostProcessorImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ResultPostProcessorImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ResultPostProcessorImpl.java
index 8d17846..61afee2 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ResultPostProcessorImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ResultPostProcessorImpl.java
@@ -61,7 +61,18 @@ public class ResultPostProcessorImpl implements ResultPostProcessor {
@Override
public void process(Result result) {
- processNode(result.getResultTree(), m_request.getURI());
+ // Decode query string only
+ // Path should not be decoded here (username can contain '?')
+ String href = m_request.getURI();
+ int pos = href.indexOf('?');
+ if (pos != -1) {
+ try {
+ href = href.substring(0, pos + 1) + URLDecoder.decode(href.substring(pos + 1), "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("Unable to decode URI: " + e, e);
+ }
+ }
+ processNode(result.getResultTree(), href);
}
/**
http://git-wip-us.apache.org/repos/asf/ambari/blob/0edce3c4/ambari-server/src/main/java/org/apache/ambari/server/api/services/StacksService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/StacksService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/StacksService.java
index 366accf..74c7b57 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/StacksService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/StacksService.java
@@ -22,6 +22,7 @@ import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -42,6 +43,9 @@ import javax.ws.rs.core.UriInfo;
import org.apache.ambari.server.api.predicate.QueryLexer;
import org.apache.ambari.server.api.resources.ResourceInstance;
import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.apache.http.message.BasicNameValuePair;
/**
* Service for stacks management.
@@ -426,23 +430,38 @@ public class StacksService extends BaseService {
return m_delegate.getPathSegments(b);
}
+ /**
+ * Converts the new corrected property names to the old names for the backend.
+ * Because both the /stacks and /stacks2 api use the same underlying classes, we
+ * need to convert the new corrected property names to the old names for the backend.
+ * This should be removed when /stacks2 is removed and we can change the property names
+ * in the resource definitions to the new form.
+ */
+ private String normalizeComponentNames(String value) {
+ if (value == null) {
+ return null;
+ }
+ value = value.replaceAll("services/", "stackServices/");
+ value = value.replaceAll("components/", "serviceComponents/");
+ value = value.replaceAll("operating_systems/", "operatingSystems/");
+ return value;
+ }
+
@Override
public URI getRequestUri() {
- String uri;
- try {
- uri = URLDecoder.decode(m_delegate.getRequestUri().toASCIIString(), "UTF-8");
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("Unable to decode URI: " + e, e);
- }
- uri = uri.replaceAll("services/", "stackServices/");
- uri = uri.replaceAll("components/", "serviceComponents/");
- uri = uri.replaceAll("operating_systems/", "operatingSystems/");
-
- try {
- return new URI(uri);
- } catch (URISyntaxException e) {
- throw new RuntimeException("Unable to create modified stacks URI: " + e, e);
+ String uriPath = m_delegate.getRequestUri().getPath();
+ UriBuilder uriBuilder = UriBuilder.fromUri(m_delegate.getRequestUri());
+ List<NameValuePair> parametersList = URLEncodedUtils.parse(m_delegate.getRequestUri(), "UTF-8");
+ List<NameValuePair> newQuery = new ArrayList<NameValuePair>();
+ for (NameValuePair nameValuePair : parametersList) {
+ newQuery.add(new BasicNameValuePair(normalizeComponentNames(nameValuePair.getName()),
+ normalizeComponentNames(nameValuePair.getValue())));
}
+
+ uriBuilder.replacePath(normalizeComponentNames(uriPath));
+ uriBuilder.replaceQuery(URLEncodedUtils.format(newQuery, "UTF-8"));
+
+ return uriBuilder.build();
}
@Override
http://git-wip-us.apache.org/repos/asf/ambari/blob/0edce3c4/ambari-server/src/test/java/org/apache/ambari/server/api/services/StacksServiceTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/StacksServiceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/StacksServiceTest.java
index e5be477..c36c6d9 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/services/StacksServiceTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/StacksServiceTest.java
@@ -21,17 +21,22 @@ package org.apache.ambari.server.api.services;
import org.apache.ambari.server.api.resources.ResourceInstance;
import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
import org.apache.ambari.server.api.services.serializers.ResultSerializer;
+import org.easymock.EasyMock;
+import org.junit.Test;
+
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
-
import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
+
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.notNull;
import static org.easymock.EasyMock.same;
@@ -215,4 +220,16 @@ public class StacksServiceTest extends BaseServiceTest {
expect(requestFactory.createRequest(same(httpHeaders), same(requestBody), (UriInfo) notNull(),
same(testMethod.getRequestType()), same(resourceInstance))).andReturn(request);
}
+
+ @Test
+ public void testStackUriInfo() throws URISyntaxException {
+
+ UriInfo delegate = new LocalUriInfo("http://host/services/?fields=*");
+ StacksService.StackUriInfo sui = new StacksService.StackUriInfo(delegate);
+ assertEquals(new URI("http://host/stackServices/?fields=*"), sui.getRequestUri());
+
+ delegate = new LocalUriInfo("http://host/?condition1=true&condition2=true&services/service.matches(A%7CB)");
+ sui = new StacksService.StackUriInfo(delegate);
+ assertEquals(new URI("http://host/?condition1=true&condition2=true&stackServices%2Fservice.matches%28A%7CB%29"), sui.getRequestUri());
+ }
}
[2/2] ambari git commit: AMBARI-8605 Query predicate .matches doesn't
work for stacks endpoint with passed logical OR (dsen)
Posted by ds...@apache.org.
AMBARI-8605 Query predicate .matches doesn't work for stacks endpoint with passed logical OR (dsen)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/f77be4c4
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/f77be4c4
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/f77be4c4
Branch: refs/heads/trunk
Commit: f77be4c4df25aef336f00a7aa9bc0591b347dfb6
Parents: 9f296a2
Author: Dmytro Sen <ds...@apache.org>
Authored: Thu Jan 22 15:13:08 2015 +0200
Committer: Dmytro Sen <ds...@apache.org>
Committed: Thu Jan 22 19:23:35 2015 +0200
----------------------------------------------------------------------
.../ambari/server/api/predicate/QueryLexer.java | 195 +++++++++++++++++--
.../server/api/predicate/QueryLexerTest.java | 51 ++++-
2 files changed, 231 insertions(+), 15 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/f77be4c4/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java b/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java
index b645040..e7051a1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/predicate/QueryLexer.java
@@ -62,6 +62,7 @@ public class QueryLexer {
*/
private static final Set<String> SET_IGNORE = new HashSet<String>();
+
/**
* Constructor.
* Register token handlers.
@@ -88,14 +89,18 @@ public class QueryLexer {
listHandlers = new ArrayList<TokenHandler>();
listHandlers.add(new CloseBracketTokenHandler());
- listHandlers.add(new ValueOperandTokenHandler());
+ listHandlers.add(new ComplexValueOperandTokenHandler());
TOKEN_HANDLERS.put(Token.TYPE.RELATIONAL_OPERATOR_FUNC, listHandlers);
listHandlers = new ArrayList<TokenHandler>();
listHandlers.add(new CloseBracketTokenHandler());
listHandlers.add(new LogicalOperatorTokenHandler());
- TOKEN_HANDLERS.put(Token.TYPE.VALUE_OPERAND, listHandlers);
TOKEN_HANDLERS.put(Token.TYPE.BRACKET_CLOSE, listHandlers);
+
+ listHandlers = new ArrayList<TokenHandler>(listHandlers);
+ // complex value operands can span multiple tokens
+ listHandlers.add(0, new ComplexValueOperandTokenHandler());
+ TOKEN_HANDLERS.put(Token.TYPE.VALUE_OPERAND, listHandlers);
}
@@ -139,6 +144,9 @@ public class QueryLexer {
tok + "\', previous token type=" + ctx.getLastTokenType());
}
}
+
+ ctx.validateEndState();
+
return ctx.getTokenList().toArray(new Token[ctx.getTokenList().size()]);
}
@@ -234,6 +242,23 @@ public class QueryLexer {
private Set<String> m_propertiesToIgnore = new HashSet<String>();
/**
+ * Bracket score. This score is the difference between the number of
+ * opening brackets and the number of closing brackets processed by
+ * a handler. Only handlers which process values containing brackets
+ * will be interested in this information.
+ */
+ private int bracketScore = 0;
+
+ /**
+ * Intermediate tokens are tokens which are used by a handler which may
+ * process several adjacent tokens. A handler might push intermediate
+ * tokens and then in subsequent invocations combine/alter/remove/etc
+ * these tokens prior to adding them to the context tokens.
+ */
+ private Deque<Token> m_intermediateTokens = new ArrayDeque<Token>();
+
+
+ /**
* Constructor.
*/
private ScanContext() {
@@ -329,6 +354,86 @@ public class QueryLexer {
m_propertiesToIgnore.addAll(ignoredProperties);
}
}
+
+ /**
+ * Add an intermediate token.
+ *
+ * @param token the token to add
+ */
+ public void pushIntermediateToken(Token token) {
+ if (m_ignoreSegmentEndToken == null) {
+ m_intermediateTokens.add(token);
+ } else if (token.getType() == m_ignoreSegmentEndToken) {
+ m_ignoreSegmentEndToken = null;
+ }
+ }
+
+ /**
+ * Return the intermediate tokens if any.
+ *
+ * @return the intermediate tokens. Will never return null.
+ */
+ public Deque<Token> getIntermediateTokens() {
+ return m_intermediateTokens;
+ }
+
+ /**
+ * Move all intermediate tokens to the context tokens.
+ */
+ public void addIntermediateTokens() {
+ m_listTokens.addAll(m_intermediateTokens);
+ m_intermediateTokens.clear();
+ }
+
+ /**
+ * Obtain the bracket score. This count is the number of outstanding opening brackets.
+ * A value of 0 indicates all opening and closing brackets are matched
+ * @return the current bracket score
+ */
+ public int getBracketScore() {
+ return bracketScore;
+ }
+
+ /**
+ * Increment the bracket score by n. This indicates that n unmatched opening brackets
+ * have been encountered.
+ *
+ * @param n amount to increment
+ * @return the new bracket score after incrementing
+ */
+ public int incrementBracketScore(int n) {
+ return bracketScore += n;
+ }
+
+ /**
+ * Decrement the bracket score. This is done when matching a closing bracket with a previously encountered
+ * opening bracket. If the requested decrement would result in a negative number an exception is thrown
+ * as this isn't a valid state.
+ *
+ * @param decValue amount to decrement
+ * @return the new bracket score after decrementing
+ * @throws InvalidQueryException if the decrement operation will result in a negative value
+ */
+ public int decrementBracketScore(int decValue) throws InvalidQueryException {
+ bracketScore -= decValue;
+ if (bracketScore < 0) {
+ throw new InvalidQueryException("Unexpected closing bracket. Last token type: " + getLastTokenType() +
+ ", Current property operand: " + getPropertyOperand() + ", tokens: " + getTokenList());
+ }
+ return bracketScore;
+ }
+
+ //todo: most handlers should implement this
+ /**
+ * Validate the end state of the scan context.
+ * Iterates over each handler associated with the final token type and asks it to validate the context.
+ * @throws InvalidQueryException if the context is determined to in an invalid end state
+ */
+ public void validateEndState() throws InvalidQueryException {
+ for (TokenHandler handler : TOKEN_HANDLERS.get(getLastTokenType())) {
+ handler.validateEndState(this);
+ }
+ }
}
/**
@@ -346,7 +451,7 @@ public class QueryLexer {
* @throws InvalidQueryException if an invalid token is encountered
*/
public boolean handleToken(String token, ScanContext ctx) throws InvalidQueryException {
- if (handles(token, ctx.getLastTokenType())) {
+ if (handles(token, ctx)) {
_handleToken(token, ctx);
ctx.setLastTokenType(getType());
return true;
@@ -355,6 +460,12 @@ public class QueryLexer {
}
}
+ public void validateEndState(ScanContext ctx) throws InvalidQueryException {
+ if (! ctx.getIntermediateTokens().isEmpty()) {
+ throw new InvalidQueryException("Unexpected end of expression.");
+ }
+ }
+
/**
* Process a token.
*
@@ -374,12 +485,12 @@ public class QueryLexer {
/**
* Determine if a handler handles a specific token type.
*
- * @param token the token type
- * @param previousTokenType the previous token type
*
+ * @param token the token type
+ * @param ctx scan context
* @return true if the handler handles the specified type; false otherwise
*/
- public abstract boolean handles(String token, Token.TYPE previousTokenType);
+ public abstract boolean handles(String token, ScanContext ctx);
}
/**
@@ -411,7 +522,7 @@ public class QueryLexer {
}
@Override
- public boolean handles(String token, Token.TYPE previousTokenType) {
+ public boolean handles(String token, ScanContext ctx) {
return token.matches("[^!&\\|<=|>=|!=|=|<|>\\(\\)]+");
}
}
@@ -431,7 +542,7 @@ public class QueryLexer {
}
@Override
- public boolean handles(String token, Token.TYPE previousTokenType) {
+ public boolean handles(String token, ScanContext ctx) {
return token.matches("[^!&\\|<=|>=|!=|=|<|>]+");
}
}
@@ -451,7 +562,7 @@ public class QueryLexer {
}
@Override
- public boolean handles(String token, Token.TYPE previousTokenType) {
+ public boolean handles(String token, ScanContext ctx) {
return token.matches("\\(");
}
}
@@ -471,7 +582,7 @@ public class QueryLexer {
}
@Override
- public boolean handles(String token, Token.TYPE previousTokenType) {
+ public boolean handles(String token, ScanContext ctx) {
return token.matches("\\)");
}
}
@@ -492,7 +603,7 @@ public class QueryLexer {
}
@Override
- public boolean handles(String token, Token.TYPE previousTokenType) {
+ public boolean handles(String token, ScanContext ctx) {
return token.matches("<=|>=|!=|=|<|>");
}
}
@@ -514,11 +625,67 @@ public class QueryLexer {
//todo: add a unary relational operator func
@Override
- public boolean handles(String token, Token.TYPE previousTokenType) {
+ public boolean handles(String token, ScanContext ctx) {
return token.matches("\\.[a-zA-Z]+\\(");
}
}
+ /**
+ * Complex Value Operand token handler.
+ * Supports values that span multiple tokens.
+ */
+ private class ComplexValueOperandTokenHandler extends TokenHandler {
+ @Override
+ public void _handleToken(String token, ScanContext ctx) throws InvalidQueryException {
+ if (token.equals(")")) {
+ ctx.decrementBracketScore(1);
+ } else if (token.endsWith("(")) {
+ // .endsWith() is used because of tokens ".matches(",".in(" and".isEmpty("
+ ctx.incrementBracketScore(1);
+ }
+
+ String tokenValue = token;
+ if (ctx.getBracketScore() > 0) {
+ Deque<Token> intermediateTokens = ctx.getIntermediateTokens();
+ if (! intermediateTokens.isEmpty()) {
+ Token lastToken = intermediateTokens.peek();
+ if (lastToken.getType() == Token.TYPE.VALUE_OPERAND) {
+ intermediateTokens.pop();
+ tokenValue = lastToken.getValue() + token;
+ }
+ }
+ ctx.pushIntermediateToken(new Token(Token.TYPE.VALUE_OPERAND, tokenValue));
+ }
+
+ if (ctx.getBracketScore() == 0) {
+ ctx.addIntermediateTokens();
+ ctx.addToken(new Token(Token.TYPE.BRACKET_CLOSE, ")"));
+ }
+ }
+
+ @Override
+ public Token.TYPE getType() {
+ return Token.TYPE.VALUE_OPERAND;
+ }
+
+ @Override
+ public boolean handles(String token, ScanContext ctx) {
+ Token.TYPE lastTokenType = ctx.getLastTokenType();
+ if (lastTokenType == Token.TYPE.RELATIONAL_OPERATOR_FUNC) {
+ ctx.incrementBracketScore(1);
+ return true;
+ } else {
+ return ctx.getBracketScore() > 0;
+ }
+ }
+
+ @Override
+ public void validateEndState(ScanContext ctx) throws InvalidQueryException {
+ if (ctx.getBracketScore() > 0) {
+ throw new InvalidQueryException("Missing closing bracket for function: " + ctx.getTokenList());
+ }
+ }
+ }
/**
* Logical Operator token handler.
@@ -535,7 +702,7 @@ public class QueryLexer {
}
@Override
- public boolean handles(String token, Token.TYPE previousTokenType) {
+ public boolean handles(String token, ScanContext ctx) {
return token.matches("[!&\\|]");
}
}
@@ -555,7 +722,7 @@ public class QueryLexer {
}
@Override
- public boolean handles(String token, Token.TYPE previousTokenType) {
+ public boolean handles(String token, ScanContext ctx) {
return "!".equals(token);
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f77be4c4/ambari-server/src/test/java/org/apache/ambari/server/api/predicate/QueryLexerTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/predicate/QueryLexerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/predicate/QueryLexerTest.java
index 2c04d6c..8caa821 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/predicate/QueryLexerTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/predicate/QueryLexerTest.java
@@ -22,7 +22,6 @@ package org.apache.ambari.server.api.predicate;
import org.junit.Test;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -360,4 +359,54 @@ public class QueryLexerTest {
//expected
}
}
+
+ @Test
+ public void testTokens_matchesRegexp_simple() throws InvalidQueryException {
+ List<Token> listTokens = new ArrayList<Token>();
+ listTokens.add(new Token(Token.TYPE.RELATIONAL_OPERATOR_FUNC, ".matches("));
+ listTokens.add(new Token(Token.TYPE.PROPERTY_OPERAND, "StackConfigurations/property_type"));
+ listTokens.add(new Token(Token.TYPE.VALUE_OPERAND, "(.*USER.*)|(.*GROUP.*)"));
+ listTokens.add(new Token(Token.TYPE.BRACKET_CLOSE, ")"));
+
+ QueryLexer lexer = new QueryLexer();
+ Token[] tokens = lexer.tokens("StackConfigurations/property_type.matches((.*USER.*)|(.*GROUP.*))");
+
+ assertArrayEquals(listTokens.toArray(new Token[listTokens.size()]), tokens);
+ }
+
+ @Test
+ public void testTokens_matchesRegexp() throws InvalidQueryException {
+ List<Token> listTokens = new ArrayList<Token>();
+ listTokens.add(new Token(Token.TYPE.BRACKET_OPEN, "("));
+ listTokens.add(new Token(Token.TYPE.RELATIONAL_OPERATOR_FUNC, ".matches("));
+ listTokens.add(new Token(Token.TYPE.PROPERTY_OPERAND, "StackConfigurations/property_type"));
+ listTokens.add(new Token(Token.TYPE.VALUE_OPERAND, "(([^=])|=|!=),.in(&).*USER.*.isEmpty(a).matches(b)"));
+ listTokens.add(new Token(Token.TYPE.BRACKET_CLOSE, ")"));
+ listTokens.add(new Token(Token.TYPE.LOGICAL_OPERATOR, "|"));
+ listTokens.add(new Token(Token.TYPE.RELATIONAL_OPERATOR_FUNC, ".matches("));
+ listTokens.add(new Token(Token.TYPE.PROPERTY_OPERAND, "StackConfigurations/property_type"));
+ listTokens.add(new Token(Token.TYPE.VALUE_OPERAND, "fields format to from .*GROUP.*"));
+ listTokens.add(new Token(Token.TYPE.BRACKET_CLOSE, ")"));
+ listTokens.add(new Token(Token.TYPE.BRACKET_CLOSE, ")"));
+
+ QueryLexer lexer = new QueryLexer();
+ Token[] tokens = lexer.tokens("(StackConfigurations/property_type.matches((([^=])|=|!=),.in(&).*USER.*" +
+ ".isEmpty(a).matches(b))|StackConfigurations/property_type.matches(fields format to from .*GROUP.*))");
+
+ assertArrayEquals("All characters between \".matches(\" and corresponding closing \")\" bracket should " +
+ "come to VALUE_OPERAND.", listTokens.toArray(new Token[listTokens.size()]), tokens);
+ }
+
+ @Test(expected = InvalidQueryException.class)
+ public void testTokens_matchesRegexpInvalidQuery() throws InvalidQueryException {
+ QueryLexer lexer = new QueryLexer();
+ lexer.tokens("StackConfigurations/property_type.matches((.*USER.*)|(.*GROUP.*)");
+ }
+
+ @Test(expected = InvalidQueryException.class)
+ public void testTokens_matchesRegexpInvalidQuery2() throws InvalidQueryException {
+ QueryLexer lexer = new QueryLexer();
+ lexer.tokens("StackConfigurations/property_type.matches((.*USER.*)|(.*GROUP.*)|StackConfigurations/property_type.matches(.*GROUP.*)");
+ }
+
}