[OLINGO-834] $select parser in Java + clean-up
Signed-off-by: Christian Amend <christian.amend@sap.com>
This commit is contained in:
parent
ef19c9be1f
commit
d7e23bf89a
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
* 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 org.apache.olingo.server.core.uri.parser;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
import org.antlr.v4.runtime.DiagnosticErrorListener;
|
||||
import org.antlr.v4.runtime.Parser;
|
||||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.Recognizer;
|
||||
import org.antlr.v4.runtime.atn.ATNConfigSet;
|
||||
import org.antlr.v4.runtime.dfa.DFA;
|
||||
|
||||
class CheckFullContextListener extends DiagnosticErrorListener {
|
||||
|
||||
@Override
|
||||
public void syntaxError(final Recognizer<?, ?> recognizer, final Object offendingSymbol, final int line,
|
||||
final int charPositionInLine,
|
||||
final String msg, final RecognitionException e) {
|
||||
// System.err.println("syntaxError detected");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportAmbiguity(final Parser recognizer, final DFA dfa, final int startIndex, final int stopIndex,
|
||||
final boolean exact,
|
||||
final BitSet ambigAlts, final ATNConfigSet configs) {
|
||||
// System.err.println("reportAmbiguity detected");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportAttemptingFullContext(final Parser recognizer, final DFA dfa, final int startIndex,
|
||||
final int stopIndex,
|
||||
final BitSet conflictingAlts, final ATNConfigSet configs) {
|
||||
// System.err.println("reportAttemptingFullContext detected");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportContextSensitivity(final Parser recognizer, final DFA dfa, final int startIndex,
|
||||
final int stopIndex, final int prediction,
|
||||
final ATNConfigSet configs) {
|
||||
// System.err.println("reportContextSensitivity detected");
|
||||
}
|
||||
|
||||
}
|
|
@ -26,11 +26,13 @@ import org.antlr.v4.runtime.CommonTokenStream;
|
|||
import org.antlr.v4.runtime.Lexer;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.atn.PredictionMode;
|
||||
import org.antlr.v4.runtime.misc.ParseCancellationException;
|
||||
import org.apache.olingo.commons.api.edm.Edm;
|
||||
import org.apache.olingo.commons.api.edm.EdmEntityContainer;
|
||||
import org.apache.olingo.commons.api.edm.EdmEntitySet;
|
||||
import org.apache.olingo.commons.api.edm.EdmStructuredType;
|
||||
import org.apache.olingo.commons.api.edm.EdmType;
|
||||
import org.apache.olingo.commons.api.ex.ODataRuntimeException;
|
||||
import org.apache.olingo.server.api.OData;
|
||||
import org.apache.olingo.server.api.uri.UriInfo;
|
||||
|
@ -45,28 +47,28 @@ import org.apache.olingo.server.api.uri.UriResourceValue;
|
|||
import org.apache.olingo.server.api.uri.queryoption.AliasQueryOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.CustomQueryOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.FilterOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.QueryOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
|
||||
import org.apache.olingo.server.core.uri.UriInfoImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceStartingTypeFilterImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceTypedImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceWithKeysImpl;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriLexer;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.ExpandItemsEOFContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.FilterExpressionEOFContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.OrderByEOFContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.SelectEOFContext;
|
||||
import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
|
||||
import org.apache.olingo.server.core.uri.parser.search.SearchParser;
|
||||
import org.apache.olingo.server.core.uri.queryoption.AliasQueryOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.CustomQueryOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.FilterOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.FormatOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.IdOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.OrderByOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.SelectOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.SkipOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.SkipTokenOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.TopOptionImpl;
|
||||
|
@ -79,12 +81,11 @@ public class Parser {
|
|||
private static final String AT = "@";
|
||||
private static final String NULL = "null";
|
||||
|
||||
int logLevel = 0;
|
||||
private final Edm edm;
|
||||
private final OData odata;
|
||||
|
||||
private enum ParserEntryRules {
|
||||
ExpandItems, FilterExpression, Orderby, Select
|
||||
ExpandItems, FilterExpression, Orderby
|
||||
}
|
||||
|
||||
public Parser(final Edm edm, final OData odata) {
|
||||
|
@ -98,242 +99,268 @@ public class Parser {
|
|||
UriContext context = new UriContext();
|
||||
UriParseTreeVisitor uriParseTreeVisitor = new UriParseTreeVisitor(edm, context);
|
||||
|
||||
try {
|
||||
final RawUri uri = UriDecoder.decodeUri(path, query, fragment, 0); // -> 0 segments are before the service url
|
||||
final List<String> pathSegmentsDecoded = UriDecoder.splitAndDecodePath(path);
|
||||
final int numberOfSegments = pathSegmentsDecoded.size();
|
||||
|
||||
// first, read the decoded path segments
|
||||
final String firstSegment = uri.pathSegmentListDecoded.isEmpty() ? "" : uri.pathSegmentListDecoded.get(0);
|
||||
// first, read the decoded path segments
|
||||
final String firstSegment = numberOfSegments == 0 ? "" : pathSegmentsDecoded.get(0);
|
||||
|
||||
if (firstSegment.isEmpty()) {
|
||||
ensureLastSegment(firstSegment, 0, uri.pathSegmentListDecoded.size());
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.service);
|
||||
if (firstSegment.isEmpty()) {
|
||||
ensureLastSegment(firstSegment, 0, numberOfSegments);
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.service);
|
||||
|
||||
} else if (firstSegment.equals("$batch")) {
|
||||
ensureLastSegment(firstSegment, 1, uri.pathSegmentListDecoded.size());
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.batch);
|
||||
} else if (firstSegment.equals("$batch")) {
|
||||
ensureLastSegment(firstSegment, 1, numberOfSegments);
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.batch);
|
||||
|
||||
} else if (firstSegment.equals("$metadata")) {
|
||||
ensureLastSegment(firstSegment, 1, uri.pathSegmentListDecoded.size());
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.metadata);
|
||||
context.contextUriInfo.setFragment(uri.fragment);
|
||||
} else if (firstSegment.equals("$metadata")) {
|
||||
ensureLastSegment(firstSegment, 1, numberOfSegments);
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.metadata);
|
||||
context.contextUriInfo.setFragment(fragment);
|
||||
|
||||
} else if (firstSegment.equals("$all")) {
|
||||
ensureLastSegment(firstSegment, 1, uri.pathSegmentListDecoded.size());
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.all);
|
||||
} else if (firstSegment.equals("$all")) {
|
||||
ensureLastSegment(firstSegment, 1, numberOfSegments);
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.all);
|
||||
// This loads nearly the whole schema, but sooner or later '$all' needs all entity sets anyway.
|
||||
for (final EdmEntitySet entitySet : edm.getEntityContainer().getEntitySets()) {
|
||||
context.contextTypes.push(entitySet.getEntityType());
|
||||
}
|
||||
context.isCollection = true;
|
||||
|
||||
} else if (firstSegment.equals("$entity")) {
|
||||
if (numberOfSegments > 1) {
|
||||
final String typeCastSegment = pathSegmentsDecoded.get(1);
|
||||
ensureLastSegment(typeCastSegment, 2, numberOfSegments);
|
||||
context.contextUriInfo = new ResourcePathParser(edm, odata).parseDollarEntityTypeCast(typeCastSegment);
|
||||
context.contextTypes.push(context.contextUriInfo.getEntityTypeCast());
|
||||
} else {
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.entityId);
|
||||
// The type of the entity is not known until the $id query option has been parsed.
|
||||
}
|
||||
context.isCollection = false;
|
||||
|
||||
} else if (firstSegment.startsWith("$crossjoin")) {
|
||||
ensureLastSegment(firstSegment, 1, numberOfSegments);
|
||||
context.contextUriInfo = new ResourcePathParser(edm, odata).parseCrossjoinSegment(firstSegment);
|
||||
final EdmEntityContainer container = edm.getEntityContainer();
|
||||
for (final String name : context.contextUriInfo.getEntitySetNames()) {
|
||||
context.contextTypes.push(container.getEntitySet(name).getEntityType());
|
||||
}
|
||||
context.isCollection = true;
|
||||
|
||||
} else {
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.resource);
|
||||
final ResourcePathParser resourcePathParser = new ResourcePathParser(edm, odata);
|
||||
int count = 0;
|
||||
UriResource lastSegment = null;
|
||||
for (final String pathSegment : pathSegmentsDecoded) {
|
||||
count++;
|
||||
final UriResource segment = resourcePathParser.parsePathSegment(pathSegment, lastSegment);
|
||||
if (segment != null) {
|
||||
if (segment instanceof UriResourceCount
|
||||
|| segment instanceof UriResourceRef
|
||||
|| segment instanceof UriResourceValue) {
|
||||
ensureLastSegment(pathSegment, count, numberOfSegments);
|
||||
} else if (segment instanceof UriResourceAction
|
||||
|| segment instanceof UriResourceFunction
|
||||
&& !((UriResourceFunction) segment).getFunction().isComposable()) {
|
||||
if (count < numberOfSegments) {
|
||||
throw new UriValidationException(
|
||||
"The segment of an action or of a non-composable function must be the last resource-path segment.",
|
||||
UriValidationException.MessageKeys.UNALLOWED_RESOURCE_PATH,
|
||||
pathSegmentsDecoded.get(count));
|
||||
}
|
||||
lastSegment = segment;
|
||||
} else if (segment instanceof UriResourceStartingTypeFilterImpl) {
|
||||
throw new UriParserSemanticException("First resource-path segment must not be namespace-qualified.",
|
||||
UriParserSemanticException.MessageKeys.NAMESPACE_NOT_ALLOWED_AT_FIRST_ELEMENT);
|
||||
} else {
|
||||
lastSegment = segment;
|
||||
}
|
||||
context.contextUriInfo.addResourcePart(segment);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastSegment instanceof UriResourcePartTyped) {
|
||||
final UriResourcePartTyped typed = (UriResourcePartTyped) lastSegment;
|
||||
final EdmType type = getTypeInformation(typed);
|
||||
if (type != null) { // could be null for, e.g., actions without return type
|
||||
context.contextTypes.push(type);
|
||||
}
|
||||
context.isCollection = typed.isCollection();
|
||||
}
|
||||
}
|
||||
|
||||
// second, read the system query options and the custom query options
|
||||
final List<QueryOption> options = UriDecoder.splitAndDecodeOptions(query);
|
||||
for (final QueryOption option : options) {
|
||||
final String optionName = option.getName();
|
||||
final String optionValue = option.getText();
|
||||
if (optionName.startsWith("$")) {
|
||||
SystemQueryOption systemOption = null;
|
||||
if (optionName.equals(SystemQueryOptionKind.FILTER.toString())) {
|
||||
try {
|
||||
FilterExpressionEOFContext ctxFilterExpression =
|
||||
(FilterExpressionEOFContext) parseRule(optionValue, ParserEntryRules.FilterExpression);
|
||||
systemOption = (FilterOptionImpl) uriParseTreeVisitor.visitFilterExpressionEOF(ctxFilterExpression);
|
||||
} catch (final ParseCancellationException e) {
|
||||
throw e.getCause() instanceof UriParserException ?
|
||||
(UriParserException) e.getCause() :
|
||||
new UriParserSyntaxException("Syntax error", e, UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.FORMAT.toString())) {
|
||||
FormatOptionImpl formatOption = new FormatOptionImpl();
|
||||
formatOption.setText(optionValue);
|
||||
if (optionValue.equalsIgnoreCase(JSON)
|
||||
|| optionValue.equalsIgnoreCase(XML)
|
||||
|| optionValue.equalsIgnoreCase(ATOM)
|
||||
|| isFormatSyntaxValid(optionValue)) {
|
||||
formatOption.setFormat(optionValue);
|
||||
} else {
|
||||
throw new UriParserSyntaxException("Illegal value of $format option!",
|
||||
UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION_FORMAT, optionValue);
|
||||
}
|
||||
systemOption = formatOption;
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.EXPAND.toString())) {
|
||||
try {
|
||||
ExpandItemsEOFContext ctxExpandItems =
|
||||
(ExpandItemsEOFContext) parseRule(optionValue, ParserEntryRules.ExpandItems);
|
||||
systemOption = (ExpandOptionImpl) uriParseTreeVisitor.visitExpandItemsEOF(ctxExpandItems);
|
||||
} catch (final ParseCancellationException e) {
|
||||
throw e.getCause() instanceof UriParserException ?
|
||||
(UriParserException) e.getCause() :
|
||||
new UriParserSyntaxException("Syntax error", e, UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.ID.toString())) {
|
||||
IdOptionImpl idOption = new IdOptionImpl();
|
||||
idOption.setText(optionValue);
|
||||
idOption.setValue(optionValue);
|
||||
systemOption = idOption;
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.LEVELS.toString())) {
|
||||
throw new UriParserSyntaxException("System query option '$levels' is allowed only inside '$expand'!",
|
||||
UriParserSyntaxException.MessageKeys.SYSTEM_QUERY_OPTION_LEVELS_NOT_ALLOWED_HERE);
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.ORDERBY.toString())) {
|
||||
try {
|
||||
OrderByEOFContext ctxOrderByExpression =
|
||||
(OrderByEOFContext) parseRule(optionValue, ParserEntryRules.Orderby);
|
||||
systemOption = (OrderByOptionImpl) uriParseTreeVisitor.visitOrderByEOF(ctxOrderByExpression);
|
||||
} catch (final ParseCancellationException e) {
|
||||
throw e.getCause() instanceof UriParserException ?
|
||||
(UriParserException) e.getCause() :
|
||||
new UriParserSyntaxException("Syntax error", e, UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.SEARCH.toString())) {
|
||||
systemOption = new SearchParser().parse(optionValue);
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.SELECT.toString())) {
|
||||
UriTokenizer selectTokenizer = new UriTokenizer(optionValue);
|
||||
systemOption = new SelectParser(edm).parse(selectTokenizer,
|
||||
context.contextTypes.peek() instanceof EdmStructuredType ?
|
||||
(EdmStructuredType) context.contextTypes.peek() :
|
||||
null,
|
||||
context.isCollection);
|
||||
if (!selectTokenizer.next(TokenKind.EOF)) {
|
||||
throw new UriParserSyntaxException("Illegal value of $select option!",
|
||||
UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
|
||||
optionName, optionValue);
|
||||
}
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.SKIP.toString())) {
|
||||
SkipOptionImpl skipOption = new SkipOptionImpl();
|
||||
skipOption.setText(optionValue);
|
||||
try {
|
||||
skipOption.setValue(Integer.parseInt(optionValue));
|
||||
} catch (final NumberFormatException e) {
|
||||
throw new UriParserSyntaxException("Illegal value of $skip option!", e,
|
||||
UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
|
||||
optionName, optionValue);
|
||||
}
|
||||
systemOption = skipOption;
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.SKIPTOKEN.toString())) {
|
||||
SkipTokenOptionImpl skipTokenOption = new SkipTokenOptionImpl();
|
||||
skipTokenOption.setText(optionValue);
|
||||
skipTokenOption.setValue(optionValue);
|
||||
systemOption = skipTokenOption;
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.TOP.toString())) {
|
||||
TopOptionImpl topOption = new TopOptionImpl();
|
||||
topOption.setText(optionValue);
|
||||
try {
|
||||
topOption.setValue(Integer.parseInt(optionValue));
|
||||
} catch (final NumberFormatException e) {
|
||||
throw new UriParserSyntaxException("Illegal value of $top option!", e,
|
||||
UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
|
||||
optionName, optionValue);
|
||||
}
|
||||
systemOption = topOption;
|
||||
|
||||
} else if (optionName.equals(SystemQueryOptionKind.COUNT.toString())) {
|
||||
CountOptionImpl inlineCountOption = new CountOptionImpl();
|
||||
inlineCountOption.setText(optionValue);
|
||||
if (optionValue.equals("true") || optionValue.equals("false")) {
|
||||
inlineCountOption.setValue(Boolean.parseBoolean(optionValue));
|
||||
} else {
|
||||
throw new UriParserSyntaxException("Illegal value of $count option!",
|
||||
UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
|
||||
optionName, optionValue);
|
||||
}
|
||||
systemOption = inlineCountOption;
|
||||
|
||||
} else if (firstSegment.equals("$entity")) {
|
||||
if (uri.pathSegmentListDecoded.size() > 1) {
|
||||
final String typeCastSegment = uri.pathSegmentListDecoded.get(1);
|
||||
ensureLastSegment(typeCastSegment, 2, uri.pathSegmentListDecoded.size());
|
||||
context.contextUriInfo = new ResourcePathParser(edm, odata).parseDollarEntityTypeCast(typeCastSegment);
|
||||
context.contextTypes.push(
|
||||
uriParseTreeVisitor.new TypeInformation(context.contextUriInfo.getEntityTypeCast(), false));
|
||||
} else {
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.entityId);
|
||||
throw new UriParserSyntaxException("Unknown system query option!",
|
||||
UriParserSyntaxException.MessageKeys.UNKNOWN_SYSTEM_QUERY_OPTION, optionName);
|
||||
}
|
||||
try {
|
||||
context.contextUriInfo.setSystemQueryOption(systemOption);
|
||||
} catch (final ODataRuntimeException e) {
|
||||
throw new UriParserSyntaxException("Double system query option!", e,
|
||||
UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION, optionName);
|
||||
}
|
||||
|
||||
} else if (firstSegment.startsWith("$crossjoin")) {
|
||||
ensureLastSegment(firstSegment, 1, uri.pathSegmentListDecoded.size());
|
||||
context.contextUriInfo = new ResourcePathParser(edm, odata)
|
||||
.parseCrossjoinSegment(uri.pathSegmentListDecoded.get(0));
|
||||
final EdmEntityContainer container = edm.getEntityContainer();
|
||||
for (final String name : context.contextUriInfo.getEntitySetNames()) {
|
||||
context.contextTypes.push(
|
||||
uriParseTreeVisitor.new TypeInformation(container.getEntitySet(name).getEntityType(), true));
|
||||
} else if (optionName.startsWith(AT)) {
|
||||
if (context.contextUriInfo.getAlias(optionName) == null) {
|
||||
// TODO: Create a proper alias-value parser that can parse also common expressions.
|
||||
Expression expression = null;
|
||||
UriTokenizer aliasTokenizer = new UriTokenizer(optionValue);
|
||||
if (aliasTokenizer.next(TokenKind.jsonArrayOrObject)) {
|
||||
if (!aliasTokenizer.next(TokenKind.EOF)) {
|
||||
throw new UriParserSyntaxException("Illegal value for alias '" + optionName + "'.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
final FilterExpressionEOFContext filterExpCtx =
|
||||
(FilterExpressionEOFContext) parseRule(optionValue, ParserEntryRules.FilterExpression);
|
||||
expression = ((FilterOption) uriParseTreeVisitor.visitFilterExpressionEOF(filterExpCtx))
|
||||
.getExpression();
|
||||
} catch (final ParseCancellationException e) {
|
||||
throw e.getCause() instanceof UriParserException ?
|
||||
(UriParserException) e.getCause() :
|
||||
new UriParserSyntaxException("Syntax error", e, UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
}
|
||||
context.contextUriInfo.addAlias((AliasQueryOption) new AliasQueryOptionImpl()
|
||||
.setAliasValue(expression)
|
||||
.setName(optionName)
|
||||
.setText(NULL.equals(optionValue) ? null : optionValue));
|
||||
} else {
|
||||
throw new UriParserSyntaxException("Alias already specified! Name: " + optionName,
|
||||
UriParserSyntaxException.MessageKeys.DUPLICATED_ALIAS, optionName);
|
||||
}
|
||||
|
||||
} else {
|
||||
context.contextUriInfo = new UriInfoImpl().setKind(UriInfoKind.resource);
|
||||
final ResourcePathParser resourcePathParser = new ResourcePathParser(edm, odata);
|
||||
int count = 0;
|
||||
UriResource lastSegment = null;
|
||||
for (final String pathSegment : uri.pathSegmentListDecoded) {
|
||||
count++;
|
||||
final UriResource segment = resourcePathParser.parsePathSegment(pathSegment, lastSegment);
|
||||
if (segment != null) {
|
||||
if (segment instanceof UriResourceCount
|
||||
|| segment instanceof UriResourceRef
|
||||
|| segment instanceof UriResourceValue) {
|
||||
ensureLastSegment(pathSegment, count, uri.pathSegmentListDecoded.size());
|
||||
} else if (segment instanceof UriResourceAction
|
||||
|| segment instanceof UriResourceFunction
|
||||
&& !((UriResourceFunction) segment).getFunction().isComposable()) {
|
||||
if (count < uri.pathSegmentListDecoded.size()) {
|
||||
throw new UriValidationException(
|
||||
"The segment of an action or of a non-composable function must be the last resource-path segment.",
|
||||
UriValidationException.MessageKeys.UNALLOWED_RESOURCE_PATH,
|
||||
uri.pathSegmentListDecoded.get(count));
|
||||
}
|
||||
lastSegment = segment;
|
||||
} else if (segment instanceof UriResourceStartingTypeFilterImpl) {
|
||||
throw new UriParserSemanticException("First resource-path segment must not be namespace-qualified.",
|
||||
UriParserSemanticException.MessageKeys.NAMESPACE_NOT_ALLOWED_AT_FIRST_ELEMENT);
|
||||
} else {
|
||||
lastSegment = segment;
|
||||
}
|
||||
context.contextUriInfo.addResourcePart(segment);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastSegment instanceof UriResourcePartTyped) {
|
||||
UriResourcePartTyped typed = (UriResourcePartTyped) lastSegment;
|
||||
|
||||
UriParseTreeVisitor.TypeInformation myType = uriParseTreeVisitor.getTypeInformation(typed);
|
||||
UriParseTreeVisitor.TypeInformation typeInfo =
|
||||
uriParseTreeVisitor.new TypeInformation(myType.type, typed.isCollection());
|
||||
context.contextTypes.push(typeInfo);
|
||||
}
|
||||
context.contextUriInfo.addCustomQueryOption((CustomQueryOption) option);
|
||||
}
|
||||
|
||||
// second, read the system query options and the custom query options
|
||||
for (final RawUri.QueryOption option : uri.queryOptionListDecoded) {
|
||||
if (option.name.startsWith("$")) {
|
||||
SystemQueryOption systemOption = null;
|
||||
if (option.name.equals(SystemQueryOptionKind.FILTER.toString())) {
|
||||
FilterExpressionEOFContext ctxFilterExpression =
|
||||
(FilterExpressionEOFContext) parseRule(option.value, ParserEntryRules.FilterExpression);
|
||||
systemOption = (FilterOptionImpl) uriParseTreeVisitor.visitFilterExpressionEOF(ctxFilterExpression);
|
||||
|
||||
} else if (option.name.equals(SystemQueryOptionKind.FORMAT.toString())) {
|
||||
FormatOptionImpl formatOption = new FormatOptionImpl();
|
||||
formatOption.setName(option.name);
|
||||
formatOption.setText(option.value);
|
||||
if (option.value.equalsIgnoreCase(JSON)
|
||||
|| option.value.equalsIgnoreCase(XML)
|
||||
|| option.value.equalsIgnoreCase(ATOM)
|
||||
|| isFormatSyntaxValid(option.value)) {
|
||||
formatOption.setFormat(option.value);
|
||||
} else {
|
||||
throw new UriParserSyntaxException("Illegal value of $format option!",
|
||||
UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION_FORMAT, option.value);
|
||||
}
|
||||
systemOption = formatOption;
|
||||
|
||||
} else if (option.name.equals(SystemQueryOptionKind.EXPAND.toString())) {
|
||||
ExpandItemsEOFContext ctxExpandItems =
|
||||
(ExpandItemsEOFContext) parseRule(option.value, ParserEntryRules.ExpandItems);
|
||||
systemOption = (ExpandOptionImpl) uriParseTreeVisitor.visitExpandItemsEOF(ctxExpandItems);
|
||||
|
||||
} else if (option.name.equals(SystemQueryOptionKind.ID.toString())) {
|
||||
IdOptionImpl idOption = new IdOptionImpl();
|
||||
idOption.setName(option.name);
|
||||
idOption.setText(option.value);
|
||||
idOption.setValue(option.value);
|
||||
systemOption = idOption;
|
||||
|
||||
} else if (option.name.equals(SystemQueryOptionKind.LEVELS.toString())) {
|
||||
throw new UriParserSyntaxException("System query option '$levels' is allowed only inside '$expand'!",
|
||||
UriParserSyntaxException.MessageKeys.SYSTEM_QUERY_OPTION_LEVELS_NOT_ALLOWED_HERE);
|
||||
|
||||
} else if (option.name.equals(SystemQueryOptionKind.ORDERBY.toString())) {
|
||||
OrderByEOFContext ctxOrderByExpression =
|
||||
(OrderByEOFContext) parseRule(option.value, ParserEntryRules.Orderby);
|
||||
systemOption = (OrderByOptionImpl) uriParseTreeVisitor.visitOrderByEOF(ctxOrderByExpression);
|
||||
|
||||
} else if (option.name.equals(SystemQueryOptionKind.SEARCH.toString())) {
|
||||
systemOption = new SearchParser().parse(option.value);
|
||||
|
||||
} else if (option.name.equals(SystemQueryOptionKind.SELECT.toString())) {
|
||||
SelectEOFContext ctxSelectEOF =
|
||||
(SelectEOFContext) parseRule(option.value, ParserEntryRules.Select);
|
||||
systemOption = (SelectOptionImpl) uriParseTreeVisitor.visitSelectEOF(ctxSelectEOF);
|
||||
|
||||
} else if (option.name.equals(SystemQueryOptionKind.SKIP.toString())) {
|
||||
SkipOptionImpl skipOption = new SkipOptionImpl();
|
||||
skipOption.setName(option.name);
|
||||
skipOption.setText(option.value);
|
||||
try {
|
||||
skipOption.setValue(Integer.parseInt(option.value));
|
||||
} catch (final NumberFormatException e) {
|
||||
throw new UriParserSyntaxException("Illegal value of $skip option!", e,
|
||||
UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
|
||||
option.name, option.value);
|
||||
}
|
||||
systemOption = skipOption;
|
||||
|
||||
} else if (option.name.equals(SystemQueryOptionKind.SKIPTOKEN.toString())) {
|
||||
SkipTokenOptionImpl skipTokenOption = new SkipTokenOptionImpl();
|
||||
skipTokenOption.setName(option.name);
|
||||
skipTokenOption.setText(option.value);
|
||||
skipTokenOption.setValue(option.value);
|
||||
systemOption = skipTokenOption;
|
||||
|
||||
} else if (option.name.equals(SystemQueryOptionKind.TOP.toString())) {
|
||||
TopOptionImpl topOption = new TopOptionImpl();
|
||||
topOption.setName(option.name);
|
||||
topOption.setText(option.value);
|
||||
try {
|
||||
topOption.setValue(Integer.parseInt(option.value));
|
||||
} catch (final NumberFormatException e) {
|
||||
throw new UriParserSyntaxException("Illegal value of $top option!", e,
|
||||
UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
|
||||
option.name, option.value);
|
||||
}
|
||||
systemOption = topOption;
|
||||
|
||||
} else if (option.name.equals(SystemQueryOptionKind.COUNT.toString())) {
|
||||
CountOptionImpl inlineCountOption = new CountOptionImpl();
|
||||
inlineCountOption.setName(option.name);
|
||||
inlineCountOption.setText(option.value);
|
||||
if (option.value.equals("true") || option.value.equals("false")) {
|
||||
inlineCountOption.setValue(Boolean.parseBoolean(option.value));
|
||||
} else {
|
||||
throw new UriParserSyntaxException("Illegal value of $count option!",
|
||||
UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION,
|
||||
option.name, option.value);
|
||||
}
|
||||
systemOption = inlineCountOption;
|
||||
|
||||
} else {
|
||||
throw new UriParserSyntaxException("Unknown system query option!",
|
||||
UriParserSyntaxException.MessageKeys.UNKNOWN_SYSTEM_QUERY_OPTION, option.name);
|
||||
}
|
||||
try {
|
||||
context.contextUriInfo.setSystemQueryOption(systemOption);
|
||||
} catch (final ODataRuntimeException e) {
|
||||
throw new UriParserSyntaxException("Double system query option!", e,
|
||||
UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION, option.name);
|
||||
}
|
||||
|
||||
} else if (option.name.startsWith(AT)) {
|
||||
if (context.contextUriInfo.getAlias(option.name) == null) {
|
||||
// TODO: Create a proper alias-value parser that can parse also common expressions.
|
||||
Expression expression = null;
|
||||
if (!option.value.isEmpty() && (option.value.charAt(0) == '[' || option.value.charAt(0) == '{')) {
|
||||
UriTokenizer tokenizer = new UriTokenizer(option.value);
|
||||
if (!(tokenizer.next(TokenKind.jsonArrayOrObject) && tokenizer.next(TokenKind.EOF))) {
|
||||
throw new UriParserSyntaxException("Illegal value for alias '" + option.name + "'.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
} else {
|
||||
final FilterExpressionEOFContext filterExpCtx =
|
||||
(FilterExpressionEOFContext) parseRule(option.value, ParserEntryRules.FilterExpression);
|
||||
expression = ((FilterOption) uriParseTreeVisitor.visitFilterExpressionEOF(filterExpCtx))
|
||||
.getExpression();
|
||||
}
|
||||
context.contextUriInfo.addAlias((AliasQueryOption) new AliasQueryOptionImpl()
|
||||
.setAliasValue(expression)
|
||||
.setName(option.name)
|
||||
.setText(NULL.equals(option.value) ? null : option.value));
|
||||
} else {
|
||||
throw new UriParserSyntaxException("Alias already specified! Name: " + option.name,
|
||||
UriParserSyntaxException.MessageKeys.DUPLICATED_ALIAS, option.name);
|
||||
}
|
||||
|
||||
} else {
|
||||
context.contextUriInfo.addCustomQueryOption((CustomQueryOption)
|
||||
new CustomQueryOptionImpl()
|
||||
.setName(option.name)
|
||||
.setText(option.value));
|
||||
}
|
||||
}
|
||||
|
||||
return context.contextUriInfo;
|
||||
} catch (ParseCancellationException e) {
|
||||
throw e.getCause() instanceof UriParserException ?
|
||||
(UriParserException) e.getCause() :
|
||||
new UriParserSyntaxException("Syntax error", e, UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
|
||||
return context.contextUriInfo;
|
||||
}
|
||||
|
||||
private void ensureLastSegment(final String segment, final int pos, final int size)
|
||||
|
@ -349,6 +376,30 @@ public class Parser {
|
|||
return index > 0 && index < value.length() - 1 && index == value.lastIndexOf('/');
|
||||
}
|
||||
|
||||
protected static EdmType getTypeInformation(final UriResourcePartTyped resourcePart) {
|
||||
EdmType type = null;
|
||||
if (resourcePart instanceof UriResourceWithKeysImpl) {
|
||||
final UriResourceWithKeysImpl lastPartWithKeys = (UriResourceWithKeysImpl) resourcePart;
|
||||
if (lastPartWithKeys.getTypeFilterOnEntry() != null) {
|
||||
type = lastPartWithKeys.getTypeFilterOnEntry();
|
||||
} else if (lastPartWithKeys.getTypeFilterOnCollection() != null) {
|
||||
type = lastPartWithKeys.getTypeFilterOnCollection();
|
||||
} else {
|
||||
type = lastPartWithKeys.getType();
|
||||
}
|
||||
|
||||
} else if (resourcePart instanceof UriResourceTypedImpl) {
|
||||
final UriResourceTypedImpl lastPartTyped = (UriResourceTypedImpl) resourcePart;
|
||||
type = lastPartTyped.getTypeFilter() == null ?
|
||||
lastPartTyped.getType() :
|
||||
lastPartTyped.getTypeFilter();
|
||||
} else {
|
||||
type = resourcePart.getType();
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
private ParserRuleContext parseRule(final String input, final ParserEntryRules entryPoint)
|
||||
throws UriParserSyntaxException {
|
||||
UriParserParser parser = null;
|
||||
|
@ -362,17 +413,11 @@ public class Parser {
|
|||
try {
|
||||
|
||||
// create parser
|
||||
if (logLevel > 0) {
|
||||
//TODO: Discuss if we should keep this code
|
||||
lexer = new UriLexer(new ANTLRInputStream(input));
|
||||
showTokens(input, lexer.getAllTokens());
|
||||
}
|
||||
|
||||
lexer = new UriLexer(new ANTLRInputStream(input));
|
||||
parser = new UriParserParser(new CommonTokenStream(lexer));
|
||||
|
||||
// Set error strategy
|
||||
addStage1ErrorStategy(parser);
|
||||
addStage1ErrorStrategy(parser);
|
||||
|
||||
// Set error collector
|
||||
addStage1ErrorListener(parser);
|
||||
|
@ -394,9 +439,6 @@ public class Parser {
|
|||
lexer.mode(Lexer.DEFAULT_MODE);
|
||||
ret = parser.expandItemsEOF();
|
||||
break;
|
||||
case Select:
|
||||
ret = parser.selectEOF();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
|
@ -411,7 +453,7 @@ public class Parser {
|
|||
parser = new UriParserParser(new CommonTokenStream(lexer));
|
||||
|
||||
// Set error strategy
|
||||
addStage2ErrorStategy(parser);
|
||||
addStage2ErrorStrategy(parser);
|
||||
|
||||
// Set error collector
|
||||
addStage2ErrorListener(parser);
|
||||
|
@ -433,9 +475,6 @@ public class Parser {
|
|||
lexer.mode(Lexer.DEFAULT_MODE);
|
||||
ret = parser.expandItemsEOF();
|
||||
break;
|
||||
case Select:
|
||||
ret = parser.selectEOF();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -454,13 +493,13 @@ public class Parser {
|
|||
return ret;
|
||||
}
|
||||
|
||||
protected void addStage1ErrorStategy(final UriParserParser parser) {
|
||||
protected void addStage1ErrorStrategy(final UriParserParser parser) {
|
||||
// Throw exception at first syntax error
|
||||
parser.setErrorHandler(new BailErrorStrategy());
|
||||
|
||||
}
|
||||
|
||||
protected void addStage2ErrorStategy(final UriParserParser parser) {
|
||||
protected void addStage2ErrorStrategy(final UriParserParser parser) {
|
||||
// Throw exception at first syntax error
|
||||
parser.setErrorHandler(new BailErrorStrategy());
|
||||
}
|
||||
|
@ -468,36 +507,10 @@ public class Parser {
|
|||
protected void addStage1ErrorListener(final UriParserParser parser) {
|
||||
// No error logging to System.out or System.err, only exceptions used (depending on ErrorStrategy)
|
||||
parser.removeErrorListeners();
|
||||
parser.addErrorListener(new CheckFullContextListener());
|
||||
|
||||
}
|
||||
|
||||
protected void addStage2ErrorListener(final UriParserParser parser) {
|
||||
// No error logging to System.out or System.err, only exceptions used (depending on ErrorStrategy)
|
||||
parser.removeErrorListeners();
|
||||
}
|
||||
|
||||
public void showTokens(final String input, final List<? extends Token> list) {
|
||||
boolean first = true;
|
||||
System.out.println("input: " + input);
|
||||
String nL = "\n";
|
||||
StringBuilder out = new StringBuilder("[").append(nL);
|
||||
for (Token token : list) {
|
||||
if (!first) {
|
||||
out.append(",");
|
||||
first = false;
|
||||
}
|
||||
int index = token.getType();
|
||||
out.append("\"").append(token.getText()).append("\"").append(" ");
|
||||
if (index != -1) {
|
||||
out.append(UriLexer.VOCABULARY.getDisplayName(index));
|
||||
} else {
|
||||
out.append(index);
|
||||
}
|
||||
out.append(nL);
|
||||
}
|
||||
out.append(']');
|
||||
System.out.println("tokens: " + out.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
* 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 org.apache.olingo.server.core.uri.parser;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class RawUri {
|
||||
public String uri;
|
||||
public String scheme;
|
||||
public String authority;
|
||||
public String path;
|
||||
public String queryOptionString;
|
||||
public String fragment;
|
||||
public List<QueryOption> queryOptionList;
|
||||
public List<QueryOption> queryOptionListDecoded;
|
||||
|
||||
public List<String> pathSegmentList;
|
||||
public List<String> pathSegmentListDecoded;
|
||||
|
||||
public static class QueryOption {
|
||||
public String name;
|
||||
public String value;
|
||||
|
||||
QueryOption(final String name, final String value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
* 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 org.apache.olingo.server.core.uri.parser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.Edm;
|
||||
import org.apache.olingo.commons.api.edm.EdmAction;
|
||||
import org.apache.olingo.commons.api.edm.EdmComplexType;
|
||||
import org.apache.olingo.commons.api.edm.EdmFunction;
|
||||
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
|
||||
import org.apache.olingo.commons.api.edm.EdmProperty;
|
||||
import org.apache.olingo.commons.api.edm.EdmStructuredType;
|
||||
import org.apache.olingo.commons.api.edm.FullQualifiedName;
|
||||
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
|
||||
import org.apache.olingo.server.api.uri.UriInfoKind;
|
||||
import org.apache.olingo.server.api.uri.UriResourcePartTyped;
|
||||
import org.apache.olingo.server.api.uri.queryoption.SelectItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.SelectOption;
|
||||
import org.apache.olingo.server.core.uri.UriInfoImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceActionImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceComplexPropertyImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceFunctionImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceNavigationPropertyImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourcePrimitivePropertyImpl;
|
||||
import org.apache.olingo.server.core.uri.parser.UriTokenizer.TokenKind;
|
||||
import org.apache.olingo.server.core.uri.queryoption.SelectItemImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.SelectOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.validator.UriValidationException;
|
||||
|
||||
public class SelectParser {
|
||||
|
||||
private final Edm edm;
|
||||
|
||||
public SelectParser(final Edm edm) {
|
||||
this.edm = edm;
|
||||
}
|
||||
|
||||
public SelectOption parse(UriTokenizer tokenizer, final EdmStructuredType referencedType,
|
||||
final boolean referencedIsCollection) throws UriParserException, UriValidationException {
|
||||
List<SelectItem> selectItems = new ArrayList<SelectItem>();
|
||||
SelectItem item;
|
||||
do {
|
||||
item = parseItem(tokenizer, referencedType, referencedIsCollection);
|
||||
selectItems.add(item);
|
||||
} while (tokenizer.next(TokenKind.COMMA));
|
||||
|
||||
return new SelectOptionImpl().setSelectItems(selectItems);
|
||||
}
|
||||
|
||||
private SelectItem parseItem(UriTokenizer tokenizer,
|
||||
final EdmStructuredType referencedType, final boolean referencedIsCollection) throws UriParserException {
|
||||
SelectItemImpl item = new SelectItemImpl();
|
||||
if (tokenizer.next(TokenKind.STAR)) {
|
||||
item.setStar(true);
|
||||
|
||||
} else if (tokenizer.next(TokenKind.QualifiedName)) {
|
||||
// The namespace or its alias could consist of dot-separated OData identifiers.
|
||||
final FullQualifiedName allOperationsInSchema = parseAllOperationsInSchema(tokenizer);
|
||||
if (allOperationsInSchema != null) {
|
||||
item.addAllOperationsInSchema(allOperationsInSchema);
|
||||
|
||||
} else {
|
||||
ensureReferencedTypeNotNull(referencedType);
|
||||
final FullQualifiedName qualifiedName = new FullQualifiedName(tokenizer.getText());
|
||||
EdmStructuredType type = edm.getEntityType(qualifiedName);
|
||||
if (type == null) {
|
||||
type = edm.getComplexType(qualifiedName);
|
||||
}
|
||||
if (type == null) {
|
||||
item.setResourcePath(new UriInfoImpl().setKind(UriInfoKind.resource).addResourcePart(
|
||||
parseBoundOperation(tokenizer, qualifiedName, referencedType, referencedIsCollection)));
|
||||
|
||||
} else {
|
||||
if (type.compatibleTo(referencedType)) {
|
||||
item.setTypeFilter(type);
|
||||
if (tokenizer.next(TokenKind.SLASH)) {
|
||||
requireNext(tokenizer, TokenKind.ODataIdentifier);
|
||||
UriInfoImpl resource = new UriInfoImpl().setKind(UriInfoKind.resource);
|
||||
addSelectPath(tokenizer, type, resource);
|
||||
item.setResourcePath(resource);
|
||||
}
|
||||
} else {
|
||||
throw new UriParserSemanticException("The type cast is not compatible.",
|
||||
UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER, type.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
requireNext(tokenizer, TokenKind.ODataIdentifier);
|
||||
// The namespace or its alias could be a single OData identifier.
|
||||
final FullQualifiedName allOperationsInSchema = parseAllOperationsInSchema(tokenizer);
|
||||
if (allOperationsInSchema != null) {
|
||||
item.addAllOperationsInSchema(allOperationsInSchema);
|
||||
|
||||
} else {
|
||||
ensureReferencedTypeNotNull(referencedType);
|
||||
UriInfoImpl resource = new UriInfoImpl().setKind(UriInfoKind.resource);
|
||||
addSelectPath(tokenizer, referencedType, resource);
|
||||
item.setResourcePath(resource);
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private FullQualifiedName parseAllOperationsInSchema(UriTokenizer tokenizer) throws UriParserException {
|
||||
final String name = tokenizer.getText();
|
||||
if (tokenizer.next(TokenKind.DOT)) {
|
||||
if (tokenizer.next(TokenKind.STAR)) {
|
||||
// TODO: Validate the namespace without loading the whole schema.
|
||||
return new FullQualifiedName(name, tokenizer.getText());
|
||||
} else {
|
||||
throw new UriParserSemanticException("Expected star after dot.",
|
||||
UriParserSemanticException.MessageKeys.UNKNOWN_PART, "");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ensureReferencedTypeNotNull(final EdmStructuredType referencedType) throws UriParserException {
|
||||
if (referencedType == null) {
|
||||
throw new UriParserSemanticException("The referenced part is not typed.",
|
||||
UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS, "select");
|
||||
}
|
||||
}
|
||||
|
||||
private UriResourcePartTyped parseBoundOperation(UriTokenizer tokenizer, final FullQualifiedName qualifiedName,
|
||||
final EdmStructuredType referencedType, final boolean referencedIsCollection) throws UriParserException {
|
||||
final EdmAction boundAction = edm.getBoundAction(qualifiedName,
|
||||
referencedType.getFullQualifiedName(),
|
||||
referencedIsCollection);
|
||||
if (boundAction == null) {
|
||||
final List<String> parameterNames = parseFunctionParameterNames(tokenizer);
|
||||
final EdmFunction boundFunction = edm.getBoundFunction(qualifiedName,
|
||||
referencedType.getFullQualifiedName(), referencedIsCollection, parameterNames);
|
||||
if (boundFunction == null) {
|
||||
throw new UriParserSemanticException("Function not found.",
|
||||
UriParserSemanticException.MessageKeys.UNKNOWN_PART, qualifiedName.getFullQualifiedNameAsString());
|
||||
} else {
|
||||
return new UriResourceFunctionImpl().setFunction(boundFunction);
|
||||
}
|
||||
} else {
|
||||
return new UriResourceActionImpl().setAction(boundAction);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> parseFunctionParameterNames(UriTokenizer tokenizer) throws UriParserException {
|
||||
List<String> names = new ArrayList<String>();
|
||||
if (tokenizer.next(TokenKind.OPEN)) {
|
||||
do {
|
||||
requireNext(tokenizer, TokenKind.ODataIdentifier);
|
||||
names.add(tokenizer.getText());
|
||||
} while (tokenizer.next(TokenKind.COMMA));
|
||||
requireNext(tokenizer, TokenKind.CLOSE);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
private void addSelectPath(UriTokenizer tokenizer, final EdmStructuredType referencedType, UriInfoImpl resource)
|
||||
throws UriParserException {
|
||||
final String name = tokenizer.getText();
|
||||
final EdmProperty property = referencedType.getStructuralProperty(name);
|
||||
|
||||
if (property == null) {
|
||||
final EdmNavigationProperty navigationProperty = referencedType.getNavigationProperty(name);
|
||||
if (navigationProperty == null) {
|
||||
throw new UriParserSemanticException("Selected property not found.",
|
||||
UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE,
|
||||
referencedType.getName(), name);
|
||||
} else {
|
||||
resource.addResourcePart(new UriResourceNavigationPropertyImpl().setNavigationProperty(navigationProperty));
|
||||
}
|
||||
|
||||
} else if (property.isPrimitive()
|
||||
|| property.getType().getKind() == EdmTypeKind.ENUM
|
||||
|| property.getType().getKind() == EdmTypeKind.DEFINITION) {
|
||||
resource.addResourcePart(new UriResourcePrimitivePropertyImpl().setProperty(property));
|
||||
|
||||
} else {
|
||||
UriResourceComplexPropertyImpl complexPart = new UriResourceComplexPropertyImpl().setProperty(property);
|
||||
resource.addResourcePart(complexPart);
|
||||
if (tokenizer.next(TokenKind.SLASH)) {
|
||||
if (tokenizer.next(TokenKind.QualifiedName)) {
|
||||
final FullQualifiedName qualifiedName = new FullQualifiedName(tokenizer.getText());
|
||||
final EdmComplexType type = edm.getComplexType(qualifiedName);
|
||||
if (type == null) {
|
||||
throw new UriParserSemanticException("Type not found.",
|
||||
UriParserSemanticException.MessageKeys.UNKNOWN_TYPE, qualifiedName.getFullQualifiedNameAsString());
|
||||
} else if (type.compatibleTo(property.getType())) {
|
||||
complexPart.setTypeFilter(type);
|
||||
if (tokenizer.next(TokenKind.SLASH)) {
|
||||
if (tokenizer.next(TokenKind.ODataIdentifier)) {
|
||||
addSelectPath(tokenizer, type, resource);
|
||||
} else {
|
||||
throw new UriParserSemanticException("Unknown part after '/'.",
|
||||
UriParserSemanticException.MessageKeys.UNKNOWN_PART, "");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new UriParserSemanticException("The type cast is not compatible.",
|
||||
UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER, type.getName());
|
||||
}
|
||||
} else if (tokenizer.next(TokenKind.ODataIdentifier)) {
|
||||
addSelectPath(tokenizer, (EdmStructuredType) property.getType(), resource);
|
||||
} else if (tokenizer.next(TokenKind.SLASH)) {
|
||||
throw new UriParserSyntaxException("Illegal $select expression.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
} else {
|
||||
throw new UriParserSemanticException("Unknown part after '/'.",
|
||||
UriParserSemanticException.MessageKeys.UNKNOWN_PART, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void requireNext(UriTokenizer tokenizer, final TokenKind kind) throws UriParserSyntaxException {
|
||||
if (!tokenizer.next(kind)) {
|
||||
throw new UriParserSyntaxException("Illegal $select expression.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,11 +18,11 @@
|
|||
*/
|
||||
package org.apache.olingo.server.core.uri.parser;
|
||||
|
||||
import java.util.Stack;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.EdmType;
|
||||
import org.apache.olingo.server.core.uri.UriInfoImpl;
|
||||
import org.apache.olingo.server.core.uri.parser.UriParseTreeVisitor.TypeInformation;
|
||||
import org.apache.olingo.server.core.uri.queryoption.ExpandItemImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.SelectItemImpl;
|
||||
|
||||
|
@ -33,9 +33,9 @@ import org.apache.olingo.server.core.uri.queryoption.SelectItemImpl;
|
|||
public class UriContext {
|
||||
|
||||
public static class LambdaVariables {
|
||||
public boolean isCollection;
|
||||
public String name;
|
||||
public EdmType type;
|
||||
public boolean isCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,11 +43,14 @@ public class UriContext {
|
|||
* As lambda functions can be nested there may be more than one allowed lambda variables at a time while parsing a
|
||||
* $filter or $orderby expressions.
|
||||
*/
|
||||
public Stack<LambdaVariables> allowedLambdaVariables;
|
||||
public Deque<LambdaVariables> allowedLambdaVariables;
|
||||
/**
|
||||
* Used to stack type information for nested $expand, $filter query options and other cases.
|
||||
*/
|
||||
public Stack<TypeInformation> contextTypes;
|
||||
public Deque<EdmType> contextTypes;
|
||||
|
||||
/** Whether the context types are collections. */
|
||||
public boolean isCollection;
|
||||
|
||||
// CHECKSTYLE:OFF (Maven checkstyle)
|
||||
/**
|
||||
|
@ -106,8 +109,8 @@ public class UriContext {
|
|||
contextExpandItemPath = null;
|
||||
contextReadingFunctionParameters = false;
|
||||
contextSelectItem = null;
|
||||
contextTypes = new Stack<UriParseTreeVisitor.TypeInformation>();
|
||||
allowedLambdaVariables = new Stack<UriContext.LambdaVariables>();
|
||||
contextTypes = new ArrayDeque<EdmType>();
|
||||
allowedLambdaVariables = new ArrayDeque<UriContext.LambdaVariables>();
|
||||
|
||||
}
|
||||
}
|
|
@ -25,53 +25,42 @@ import java.util.LinkedList;
|
|||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.commons.core.Decoder;
|
||||
import org.apache.olingo.server.api.uri.queryoption.QueryOption;
|
||||
import org.apache.olingo.server.core.uri.queryoption.CustomQueryOptionImpl;
|
||||
|
||||
public class UriDecoder {
|
||||
|
||||
public static RawUri decodeUri(final String path, final String query, final String fragment,
|
||||
final int skipSegments) throws UriParserSyntaxException {
|
||||
RawUri rawUri = new RawUri();
|
||||
|
||||
rawUri.path = path;
|
||||
rawUri.queryOptionString = query;
|
||||
rawUri.fragment = fragment;
|
||||
|
||||
rawUri.pathSegmentList = splitPath(path, skipSegments);
|
||||
rawUri.queryOptionList = splitOptions(query);
|
||||
decode(rawUri);
|
||||
|
||||
return rawUri;
|
||||
/** Splits the path string at '/' characters and percent-decodes the resulting path segments. */
|
||||
protected static List<String> splitAndDecodePath(final String path) throws UriParserSyntaxException {
|
||||
List<String> pathSegmentsDecoded = new ArrayList<String>();
|
||||
for (final String segment : splitSkipEmpty(path, '/')) {
|
||||
pathSegmentsDecoded.add(decode(segment));
|
||||
}
|
||||
return pathSegmentsDecoded;
|
||||
}
|
||||
|
||||
private static void decode(final RawUri rawUri) throws UriParserSyntaxException {
|
||||
rawUri.pathSegmentListDecoded = new ArrayList<String>();
|
||||
for (String segment : rawUri.pathSegmentList) {
|
||||
rawUri.pathSegmentListDecoded.add(decode(segment));
|
||||
}
|
||||
|
||||
rawUri.queryOptionListDecoded = new ArrayList<RawUri.QueryOption>();
|
||||
for (RawUri.QueryOption optionPair : rawUri.queryOptionList) {
|
||||
rawUri.queryOptionListDecoded.add(new RawUri.QueryOption(
|
||||
decode(optionPair.name),
|
||||
decode(optionPair.value)));
|
||||
}
|
||||
}
|
||||
|
||||
private static List<RawUri.QueryOption> splitOptions(final String queryOptionString) {
|
||||
if (queryOptionString == null) {
|
||||
/**
|
||||
* Splits the query-option string at '&' characters, the resulting parts at '=' characters,
|
||||
* and separately percent-decodes names and values of the resulting name-value pairs.
|
||||
*/
|
||||
protected static List<QueryOption> splitAndDecodeOptions(final String queryOptionString)
|
||||
throws UriParserSyntaxException {
|
||||
if (queryOptionString == null || queryOptionString.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<RawUri.QueryOption> queryOptionList = new ArrayList<RawUri.QueryOption>();
|
||||
for (String option : splitSkipEmpty(queryOptionString, '&')) {
|
||||
List<QueryOption> queryOptions = new ArrayList<QueryOption>();
|
||||
for (final String option : splitSkipEmpty(queryOptionString, '&')) {
|
||||
final List<String> pair = splitFirst(option, '=');
|
||||
queryOptionList.add(new RawUri.QueryOption(pair.get(0), pair.get(1)));
|
||||
queryOptions.add(new CustomQueryOptionImpl()
|
||||
.setName(decode(pair.get(0)))
|
||||
.setText(decode(pair.get(1))));
|
||||
}
|
||||
return queryOptionList;
|
||||
return queryOptions;
|
||||
}
|
||||
|
||||
private static List<String> splitFirst(final String input, final char c) {
|
||||
int pos = input.indexOf(c, 0);
|
||||
int pos = input.indexOf(c);
|
||||
if (pos >= 0) {
|
||||
return Arrays.asList(input.substring(0, pos), input.substring(pos + 1));
|
||||
} else {
|
||||
|
@ -79,21 +68,14 @@ public class UriDecoder {
|
|||
}
|
||||
}
|
||||
|
||||
private static List<String> splitPath(final String path, final int skipSegments) {
|
||||
List<String> list = splitSkipEmpty(path, '/');
|
||||
|
||||
return skipSegments > 0 ? list.subList(skipSegments, list.size()) : list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the input string at given character and drop all empty elements.
|
||||
*
|
||||
* Splits the input string at the given character and drops all empty elements.
|
||||
* @param input string to split
|
||||
* @param c character at which to split
|
||||
* @return list of elements (can be empty)
|
||||
*/
|
||||
static List<String> splitSkipEmpty(final String input, final char c) {
|
||||
if(input.isEmpty() || input.length() == 1 && input.charAt(0) == c) {
|
||||
private static List<String> splitSkipEmpty(final String input, final char c) {
|
||||
if (input.isEmpty() || input.length() == 1 && input.charAt(0) == c) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
@ -103,20 +85,20 @@ public class UriDecoder {
|
|||
int end;
|
||||
|
||||
while ((end = input.indexOf(c, start)) >= 0) {
|
||||
if(start != end) {
|
||||
if (start != end) {
|
||||
list.add(input.substring(start, end));
|
||||
}
|
||||
start = end + 1;
|
||||
}
|
||||
|
||||
if(input.charAt(input.length()-1) != c) {
|
||||
if (input.charAt(input.length() - 1) != c) {
|
||||
list.add(input.substring(start));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static String decode(final String encoded) throws UriParserSyntaxException {
|
||||
private static String decode(final String encoded) throws UriParserSyntaxException {
|
||||
try {
|
||||
return Decoder.decode(encoded);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
|
|
|
@ -82,9 +82,7 @@ import org.apache.olingo.server.core.uri.UriResourceStartingTypeFilterImpl;
|
|||
import org.apache.olingo.server.core.uri.UriResourceTypedImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceValueImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceWithKeysImpl;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriLexer;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserBaseVisitor;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser;
|
||||
import org.apache.olingo.server.core.uri.antlr.*;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.AllEOFContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.AllExprContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.AltAddContext;
|
||||
|
@ -106,7 +104,6 @@ import org.apache.olingo.server.core.uri.antlr.UriParserParser.CeilingMethodCall
|
|||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.ConcatMethodCallExprContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.ConstSegmentContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.ContainsMethodCallExprContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.CrossjoinEOFContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.DateLiteralContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.DateMethodCallExprContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.DatetimeoffsetLiteralContext;
|
||||
|
@ -151,7 +148,6 @@ import org.apache.olingo.server.core.uri.antlr.UriParserParser.NamespaceContext;
|
|||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.NaninfinityLiteralContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.NowMethodCallExprContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.NullruleLiteralContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.OdataIdentifierContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.OrderByContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.OrderByEOFContext;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser.OrderByItemContext;
|
||||
|
@ -226,20 +222,6 @@ import org.apache.olingo.server.core.uri.queryoption.expression.UnaryImpl;
|
|||
*/
|
||||
public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
||||
|
||||
public class TypeInformation {
|
||||
|
||||
boolean isCollection;
|
||||
|
||||
EdmType type;
|
||||
|
||||
TypeInformation(final EdmType type, final boolean isCollection) {
|
||||
this.type = type;
|
||||
this.isCollection = isCollection;
|
||||
}
|
||||
|
||||
public TypeInformation() {}
|
||||
}
|
||||
|
||||
public UriContext context = null;
|
||||
|
||||
public Edm edm;
|
||||
|
@ -277,36 +259,6 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
return null;
|
||||
}
|
||||
|
||||
TypeInformation getTypeInformation(final UriResource lastResourcePart) {
|
||||
|
||||
TypeInformation typeInformation = new TypeInformation();
|
||||
if (lastResourcePart instanceof UriResourceWithKeysImpl) {
|
||||
UriResourceWithKeysImpl lastPartWithKeys = (UriResourceWithKeysImpl) lastResourcePart;
|
||||
|
||||
if (lastPartWithKeys.getTypeFilterOnEntry() != null) {
|
||||
typeInformation.type = lastPartWithKeys.getTypeFilterOnEntry();
|
||||
} else if (lastPartWithKeys.getTypeFilterOnCollection() != null) {
|
||||
typeInformation.type = lastPartWithKeys.getTypeFilterOnCollection();
|
||||
} else {
|
||||
typeInformation.type = lastPartWithKeys.getType();
|
||||
}
|
||||
typeInformation.isCollection = lastPartWithKeys.isCollection();
|
||||
|
||||
} else if (lastResourcePart instanceof UriResourceTypedImpl) {
|
||||
UriResourceTypedImpl lastPartTyped = (UriResourceTypedImpl) lastResourcePart;
|
||||
|
||||
if (lastPartTyped.getTypeFilter() != null) {
|
||||
typeInformation.type = lastPartTyped.getTypeFilter();
|
||||
} else {
|
||||
typeInformation.type = lastPartTyped.getType();
|
||||
}
|
||||
|
||||
typeInformation.isCollection = lastPartTyped.isCollection();
|
||||
}
|
||||
|
||||
return typeInformation;
|
||||
}
|
||||
|
||||
public UriResourceTypedImpl readResourcePathSegment(final PathSegmentContext ctx) {
|
||||
|
||||
final boolean checkFirst =
|
||||
|
@ -318,10 +270,10 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
boolean searchInContainer = true;
|
||||
// validate if context type and according property is available
|
||||
// otherwise search in container for first element
|
||||
if (checkFirst && ctx.vNS == null && !context.contextTypes.empty()) {
|
||||
TypeInformation source = context.contextTypes.peek();
|
||||
if (source.type instanceof EdmStructuredType) {
|
||||
EdmStructuredType str = (EdmStructuredType) source.type;
|
||||
if (checkFirst && ctx.vNS == null && !context.contextTypes.isEmpty()) {
|
||||
EdmType sourceType = context.contextTypes.peek();
|
||||
if (sourceType instanceof EdmStructuredType) {
|
||||
EdmStructuredType str = (EdmStructuredType) sourceType;
|
||||
EdmElement property = str.getProperty(odi);
|
||||
if (property != null) {
|
||||
searchInContainer = false;
|
||||
|
@ -418,11 +370,12 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
}
|
||||
}
|
||||
|
||||
final TypeInformation source;
|
||||
EdmType sourceType;
|
||||
boolean sourceIsCollection = false;
|
||||
final UriResource lastResourcePart = context.contextUriInfo.getLastResourcePart();
|
||||
|
||||
if (lastResourcePart == null) {
|
||||
if (context.contextTypes.empty()) {
|
||||
if (context.contextTypes.isEmpty()) {
|
||||
if (checkFirst && ctx.vNS == null) {
|
||||
throw wrap(new UriParserSemanticException(
|
||||
"Cannot find EntitySet, Singleton, ActionImport or FunctionImport with name '" + odi + "'.",
|
||||
|
@ -432,15 +385,15 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
"Resource part '" + odi + "' can only applied on typed resource parts",
|
||||
UriParserSemanticException.MessageKeys.RESOURCE_PART_ONLY_FOR_TYPED_PARTS, odi));
|
||||
}
|
||||
source = context.contextTypes.peek();
|
||||
sourceType = context.contextTypes.peek();
|
||||
sourceIsCollection = context.isCollection;
|
||||
} else if (lastResourcePart instanceof UriResourcePartTyped) {
|
||||
sourceType = Parser.getTypeInformation((UriResourcePartTyped) lastResourcePart);
|
||||
sourceIsCollection = ((UriResourcePartTyped) lastResourcePart).isCollection();
|
||||
} else {
|
||||
source = getTypeInformation(lastResourcePart);
|
||||
|
||||
if (source.type == null) {
|
||||
throw wrap(new UriParserSemanticException(
|
||||
"Resource part '" + odi + "' can only be applied on typed resource parts.",
|
||||
UriParserSemanticException.MessageKeys.RESOURCE_PART_ONLY_FOR_TYPED_PARTS, odi));
|
||||
}
|
||||
throw wrap(new UriParserSemanticException(
|
||||
"Resource part '" + odi + "' can only be applied on typed resource parts.",
|
||||
UriParserSemanticException.MessageKeys.RESOURCE_PART_ONLY_FOR_TYPED_PARTS, odi));
|
||||
}
|
||||
|
||||
if (ctx.vNS == null) { // without namespace
|
||||
|
@ -456,7 +409,7 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (!(source.type instanceof EdmStructuredType)) {
|
||||
if (!(sourceType instanceof EdmStructuredType)) {
|
||||
throw wrap(new UriParserSemanticException(
|
||||
"Cannot parse '" + odi + "'; previous path segment is not a structural type.",
|
||||
UriParserSemanticException.MessageKeys.RESOURCE_PART_MUST_BE_PRECEDED_BY_STRUCTURAL_TYPE, odi));
|
||||
|
@ -465,12 +418,12 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
if ((ctx.depth() <= 2 // path evaluation for the resource path
|
||||
|| lastResourcePart instanceof UriResourceTypedImpl
|
||||
|| lastResourcePart instanceof UriResourceNavigationPropertyImpl)
|
||||
&& source.isCollection) {
|
||||
&& sourceIsCollection) {
|
||||
throw wrap(new UriParserSemanticException("Property '" + odi + "' is not allowed after collection.",
|
||||
UriParserSemanticException.MessageKeys.PROPERTY_AFTER_COLLECTION, odi));
|
||||
}
|
||||
|
||||
EdmStructuredType structType = (EdmStructuredType) source.type;
|
||||
EdmStructuredType structType = (EdmStructuredType) sourceType;
|
||||
|
||||
EdmElement property = structType.getProperty(odi);
|
||||
if (property == null) {
|
||||
|
@ -520,12 +473,12 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
FullQualifiedName fullFilterName = getFullNameFromContext(ctx.vNS, odi);
|
||||
|
||||
// EdmType lastType = getLastType(lastTyped);
|
||||
if (source.type instanceof EdmEntityType) {
|
||||
if (sourceType instanceof EdmEntityType) {
|
||||
|
||||
EdmEntityType filterEntityType = edm.getEntityType(fullFilterName);
|
||||
if (filterEntityType != null) {
|
||||
// is entity type cast
|
||||
if (!(filterEntityType.compatibleTo(source.type))) {
|
||||
if (!(filterEntityType.compatibleTo(sourceType))) {
|
||||
throw wrap(new UriParserSemanticException(
|
||||
"Entity typefilter not compatible to previous path segment: " + fullFilterName.toString(),
|
||||
UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER, fullFilterName.toString()));
|
||||
|
@ -535,8 +488,8 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
// this may be the case if a member expression within a filter starts with a typeCast
|
||||
UriResourceStartingTypeFilterImpl uriResource = new UriResourceStartingTypeFilterImpl()
|
||||
.setType(filterEntityType)
|
||||
.setCollection(source.isCollection);
|
||||
if (source.isCollection) {
|
||||
.setCollection(sourceIsCollection);
|
||||
if (sourceIsCollection) {
|
||||
uriResource.setCollectionTypeFilter(filterEntityType);
|
||||
} else {
|
||||
uriResource.setEntryTypeFilter(filterEntityType);
|
||||
|
@ -590,18 +543,18 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
}
|
||||
}
|
||||
|
||||
} else if (source.type instanceof EdmComplexType) {
|
||||
} else if (sourceType instanceof EdmComplexType) {
|
||||
|
||||
EdmComplexType filterComplexType = edm.getComplexType(fullFilterName);
|
||||
|
||||
if (filterComplexType != null) {
|
||||
|
||||
// is complex type cast
|
||||
if (!(filterComplexType.compatibleTo(source.type))) {
|
||||
if (!(filterComplexType.compatibleTo(sourceType))) {
|
||||
throw wrap(new UriParserSemanticException(
|
||||
"Complex typefilter '" + getName(source.type) + "'not compatible type of previous path segment '"
|
||||
"Complex typefilter '" + getName(sourceType) + "'not compatible type of previous path segment '"
|
||||
+ getName(filterComplexType) + "'",
|
||||
UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER, getName(source.type)));
|
||||
UriParserSemanticException.MessageKeys.INCOMPATIBLE_TYPE_FILTER, getName(sourceType)));
|
||||
}
|
||||
|
||||
// is simple complex type cast
|
||||
|
@ -609,9 +562,9 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
// this may be the case if a member expression within a filter starts with a typeCast
|
||||
UriResourceStartingTypeFilterImpl uriResource = new UriResourceStartingTypeFilterImpl()
|
||||
.setType(filterComplexType)
|
||||
.setCollection(source.isCollection);
|
||||
.setCollection(sourceIsCollection);
|
||||
|
||||
if (source.isCollection) {
|
||||
if (sourceIsCollection) {
|
||||
uriResource.setCollectionTypeFilter(filterComplexType);
|
||||
} else {
|
||||
uriResource.setEntryTypeFilter(filterComplexType);
|
||||
|
@ -666,10 +619,10 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
}
|
||||
}
|
||||
|
||||
FullQualifiedName fullBindingTypeName = new FullQualifiedName(source.type.getNamespace(), source.type.getName());
|
||||
FullQualifiedName fullBindingTypeName = sourceType.getFullQualifiedName();
|
||||
|
||||
// check for action
|
||||
EdmAction action = edm.getBoundAction(fullFilterName, fullBindingTypeName, source.isCollection);
|
||||
EdmAction action = edm.getBoundAction(fullFilterName, fullBindingTypeName, sourceIsCollection);
|
||||
if (action != null) {
|
||||
UriResourceActionImpl pathInfoAction = new UriResourceActionImpl();
|
||||
pathInfoAction.setAction(action);
|
||||
|
@ -694,7 +647,7 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
names.add(item.getName());
|
||||
}
|
||||
|
||||
EdmFunction function = edm.getBoundFunction(fullFilterName, fullBindingTypeName, source.isCollection, names);
|
||||
EdmFunction function = edm.getBoundFunction(fullFilterName, fullBindingTypeName, sourceIsCollection, names);
|
||||
|
||||
if (function != null) {
|
||||
UriResourceFunctionImpl pathInfoFunction = new UriResourceFunctionImpl()
|
||||
|
@ -767,7 +720,7 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
|
||||
UriContext.LambdaVariables var = new UriContext.LambdaVariables();
|
||||
var.name = ctx.vLV.getText();
|
||||
var.type = getTypeInformation(obj).type;
|
||||
var.type = Parser.getTypeInformation((UriResourcePartTyped) obj);
|
||||
var.isCollection = false;
|
||||
|
||||
all.setLamdaVariable(ctx.vLV.getText());
|
||||
|
@ -907,11 +860,9 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
context.contextUriInfo.setEntityTypeCast(type);
|
||||
|
||||
// contextUriInfo = uriInfo;
|
||||
context.contextTypes.push(new TypeInformation(context.contextUriInfo.getEntityTypeCast(), true));
|
||||
context.contextTypes.push(context.contextUriInfo.getEntityTypeCast());
|
||||
context.isCollection = true; // TODO: check!
|
||||
|
||||
// @SuppressWarnings("unchecked")
|
||||
// List<QueryOptionImpl> list = (List<QueryOptionImpl>) ctx.vEO.accept(this);
|
||||
// uriInfo.setQueryOptions(list);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -998,7 +949,7 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
|
||||
UriContext.LambdaVariables var = new UriContext.LambdaVariables();
|
||||
var.name = ctx.vLV.getText();
|
||||
var.type = getTypeInformation(lastResourcePart).type;
|
||||
var.type = Parser.getTypeInformation((UriResourcePartTyped) lastResourcePart);
|
||||
var.isCollection = false;
|
||||
|
||||
any.setLamdaVariable(ctx.vLV.getText());
|
||||
|
@ -1146,33 +1097,6 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
.addParameter((ExpressionImpl) ctx.vE2.accept(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitCrossjoinEOF(final CrossjoinEOFContext ctx) {
|
||||
UriInfoImpl crossJoin = new UriInfoImpl().setKind(UriInfoKind.crossjoin);
|
||||
|
||||
for (OdataIdentifierContext obj : ctx.vlODI) {
|
||||
String odi = obj.getText();
|
||||
crossJoin.addEntitySetName(odi);
|
||||
|
||||
EdmEntitySet edmEntitySet = edmEntityContainer.getEntitySet(odi);
|
||||
if (edmEntitySet == null) {
|
||||
throw wrap(new UriParserSemanticException("Expected EntityTypeName",
|
||||
UriParserSemanticException.MessageKeys.UNKNOWN_PART, odi));
|
||||
}
|
||||
|
||||
EdmEntityType type = edmEntitySet.getEntityType();
|
||||
if (type == null) {
|
||||
throw wrap(new UriParserSemanticException("Expected EntityTypeName",
|
||||
UriParserSemanticException.MessageKeys.UNKNOWN_ENTITY_TYPE, odi));
|
||||
}
|
||||
// contextUriInfo = uriInfo;
|
||||
context.contextTypes.push(new TypeInformation(type, true));
|
||||
}
|
||||
|
||||
context.contextUriInfo = crossJoin;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitDateMethodCallExpr(final DateMethodCallExprContext ctx) {
|
||||
return new MethodImpl()
|
||||
|
@ -1372,23 +1296,24 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
if (context.contextExpandItemPath == null) {
|
||||
// use the type of the last resource path segement
|
||||
UriResourceTypedImpl lastSegment = (UriResourceTypedImpl) context.contextUriInfo.getLastResourcePart();
|
||||
targetType = getTypeInformation(lastSegment).type;
|
||||
targetType = Parser.getTypeInformation(lastSegment);
|
||||
isColl = lastSegment.isCollection();
|
||||
} else {
|
||||
if (context.contextExpandItemPath.getResourcePath() == null) {
|
||||
// use the type of the last resource path segement
|
||||
UriResourceTypedImpl lastSegment = (UriResourceTypedImpl) context.contextUriInfo.getLastResourcePart();
|
||||
targetType = getTypeInformation(lastSegment).type;
|
||||
targetType = Parser.getTypeInformation(lastSegment);
|
||||
isColl = lastSegment.isCollection();
|
||||
} else {
|
||||
// use the type of the last ''expand'' path segement
|
||||
UriInfoImpl info = (UriInfoImpl) context.contextExpandItemPath.getResourcePath();
|
||||
targetType = getTypeInformation(info.getLastResourcePart()).type;
|
||||
targetType = Parser.getTypeInformation((UriResourcePartTyped) info.getLastResourcePart());
|
||||
isColl = ((UriResourcePartTyped) info.getLastResourcePart()).isCollection();
|
||||
}
|
||||
}
|
||||
|
||||
context.contextTypes.push(new TypeInformation(targetType, isColl));
|
||||
context.contextTypes.push(targetType);
|
||||
context.isCollection = isColl;
|
||||
|
||||
if (ctx.vC != null) {
|
||||
UriInfoImpl resourcePath = (UriInfoImpl) context.contextExpandItemPath.getResourcePath();
|
||||
|
@ -1546,12 +1471,11 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
throw wrap(new UriParserSemanticException("Expression '" + ctx.getText() + "' is not allowed as key value.",
|
||||
UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE, ctx.getText()));
|
||||
}
|
||||
TypeInformation lastTypeInfo = context.contextTypes.peek();
|
||||
|
||||
if (ctx.vIt != null || ctx.vIts != null) {
|
||||
UriResourceItImpl pathInfoIT = new UriResourceItImpl();
|
||||
pathInfoIT.setType(lastTypeInfo.type);
|
||||
pathInfoIT.setCollection(lastTypeInfo.isCollection);
|
||||
pathInfoIT.setType(context.contextTypes.peek());
|
||||
pathInfoIT.setCollection(context.isCollection);
|
||||
uriInfoImplpath.addResourcePart(pathInfoIT);
|
||||
}
|
||||
|
||||
|
@ -2101,7 +2025,7 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
|
||||
UriResourceRootImpl pathInfoRoot = new UriResourceRootImpl();
|
||||
pathInfoRoot.setCollection(lastType.isCollection());
|
||||
pathInfoRoot.setType(getTypeInformation(lastType).type);
|
||||
pathInfoRoot.setType(Parser.getTypeInformation(lastType));
|
||||
|
||||
UriInfoImpl uriInfoImplpath = new UriInfoImpl().setKind(UriInfoKind.resource);
|
||||
uriInfoImplpath.addResourcePart(pathInfoRoot);
|
||||
|
@ -2194,12 +2118,12 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
|
||||
EdmType prevType = null;
|
||||
if (context.contextSelectItem.getResourcePath() == null) {
|
||||
prevType = context.contextTypes.peek().type;
|
||||
prevType = context.contextTypes.peek();
|
||||
} else {
|
||||
UriInfoImpl uriInfo = (UriInfoImpl) context.contextSelectItem.getResourcePath();
|
||||
UriResource last = uriInfo.getLastResourcePart();
|
||||
|
||||
prevType = getTypeInformation(last).type;
|
||||
prevType = Parser.getTypeInformation((UriResourcePartTyped) last);
|
||||
if (prevType == null) {
|
||||
throw wrap(new UriParserSemanticException("prev segment not typed",
|
||||
UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS, "select"));
|
||||
|
@ -2278,7 +2202,7 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
// contextSelectItem.addQualifiedThing(fullName);
|
||||
|
||||
if (context.contextSelectItem.getResourcePath() == null) {
|
||||
EdmType prevType = context.contextTypes.peek().type;
|
||||
EdmType prevType = context.contextTypes.peek();
|
||||
|
||||
// check for complex type cast
|
||||
if (prevType instanceof EdmComplexType) {
|
||||
|
@ -2331,7 +2255,7 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
throw wrap(new UriParserSemanticException("prev segment typed",
|
||||
UriParserSemanticException.MessageKeys.ONLY_FOR_TYPED_PARTS, "select"));
|
||||
}
|
||||
EdmType prevType = getTypeInformation(last).type;
|
||||
EdmType prevType = Parser.getTypeInformation((UriResourcePartTyped) last);
|
||||
|
||||
if (prevType instanceof EdmComplexType) {
|
||||
EdmComplexType ct = edm.getComplexType(fullName);
|
||||
|
@ -2367,7 +2291,7 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
|
||||
EdmType prevType = null;
|
||||
if (context.contextSelectItem.getResourcePath() == null) {
|
||||
prevType = context.contextTypes.peek().type;
|
||||
prevType = context.contextTypes.peek();
|
||||
} else {
|
||||
UriInfoImpl uriInfo = (UriInfoImpl) context.contextSelectItem.getResourcePath();
|
||||
UriResource last = uriInfo.getLastResourcePart();
|
||||
|
@ -2375,7 +2299,7 @@ public class UriParseTreeVisitor extends UriParserBaseVisitor<Object> {
|
|||
throw wrap(new UriParserSemanticException("prev segment typed",
|
||||
UriParserSemanticException.MessageKeys.PREVIOUS_PART_TYPED));
|
||||
}
|
||||
prevType = getTypeInformation(last).type;
|
||||
prevType = Parser.getTypeInformation((UriResourcePartTyped) last);
|
||||
}
|
||||
|
||||
final FullQualifiedName finalTypeName = prevType.getFullQualifiedName();
|
||||
|
|
|
@ -39,7 +39,11 @@ public class UriTokenizer {
|
|||
CLOSE,
|
||||
COMMA,
|
||||
SEMI,
|
||||
DOT,
|
||||
SLASH,
|
||||
EQ,
|
||||
STAR,
|
||||
PLUS,
|
||||
NULL,
|
||||
|
||||
// variable-value tokens (convention: mixed case)
|
||||
|
@ -63,18 +67,18 @@ public class UriTokenizer {
|
|||
jsonArrayOrObject
|
||||
}
|
||||
|
||||
private final String pathSegment;
|
||||
private final String parseString;
|
||||
|
||||
private int startIndex = 0;
|
||||
private int index = 0;
|
||||
|
||||
public UriTokenizer(final String pathSegment) {
|
||||
this.pathSegment = pathSegment == null ? "" : pathSegment;
|
||||
public UriTokenizer(final String parseString) {
|
||||
this.parseString = parseString == null ? "" : parseString;
|
||||
}
|
||||
|
||||
/** Returns the string value corresponding to the last successful {@link #next(TokenKind)} call. */
|
||||
public String getText() {
|
||||
return pathSegment.substring(startIndex, index);
|
||||
return parseString.substring(startIndex, index);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,14 +123,26 @@ public class UriTokenizer {
|
|||
case SEMI:
|
||||
found = nextCharacter(';');
|
||||
break;
|
||||
case DOT:
|
||||
found = nextCharacter('.');
|
||||
break;
|
||||
case SLASH:
|
||||
found = nextCharacter('/');
|
||||
break;
|
||||
case EQ:
|
||||
found = nextCharacter('=');
|
||||
break;
|
||||
case STAR:
|
||||
found = nextCharacter('*');
|
||||
break;
|
||||
case PLUS:
|
||||
found = nextCharacter('+');
|
||||
break;
|
||||
case NULL:
|
||||
found = nextConstant("null");
|
||||
break;
|
||||
case EOF:
|
||||
found = index >= pathSegment.length();
|
||||
found = index >= parseString.length();
|
||||
break;
|
||||
|
||||
// Identifiers
|
||||
|
@ -192,8 +208,12 @@ public class UriTokenizer {
|
|||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past the given string constant if found; otherwise leaves the index unchanged.
|
||||
* @return whether the constant has been found at the current index
|
||||
*/
|
||||
private boolean nextConstant(final String constant) {
|
||||
if (pathSegment.startsWith(constant, index)) {
|
||||
if (parseString.startsWith(constant, index)) {
|
||||
index += constant.length();
|
||||
return true;
|
||||
} else {
|
||||
|
@ -201,10 +221,14 @@ public class UriTokenizer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past the given string constant, ignoring case, if found; otherwise leaves the index unchanged.
|
||||
* @return whether the constant has been found at the current index
|
||||
*/
|
||||
private boolean nextConstantIgnoreCase(final String constant) {
|
||||
final int length = constant.length();
|
||||
if (index + length <= pathSegment.length()
|
||||
&& constant.equalsIgnoreCase(pathSegment.substring(index, index + length))) {
|
||||
if (index + length <= parseString.length()
|
||||
&& constant.equalsIgnoreCase(parseString.substring(index, index + length))) {
|
||||
index += length;
|
||||
return true;
|
||||
} else {
|
||||
|
@ -217,7 +241,7 @@ public class UriTokenizer {
|
|||
* @return whether the given character has been found at the current index
|
||||
*/
|
||||
private boolean nextCharacter(final char character) {
|
||||
if (index < pathSegment.length() && pathSegment.charAt(index) == character) {
|
||||
if (index < parseString.length() && parseString.charAt(index) == character) {
|
||||
index++;
|
||||
return true;
|
||||
} else {
|
||||
|
@ -231,8 +255,8 @@ public class UriTokenizer {
|
|||
* @return whether the given character has been found at the current index
|
||||
*/
|
||||
private boolean nextCharacterRange(final char from, final char to) {
|
||||
if (index < pathSegment.length()) {
|
||||
final char code = pathSegment.charAt(index);
|
||||
if (index < parseString.length()) {
|
||||
final char code = parseString.charAt(index);
|
||||
if (code >= from && code <= to) {
|
||||
index++;
|
||||
return true;
|
||||
|
@ -276,16 +300,20 @@ public class UriTokenizer {
|
|||
return nextCharacter('+') || nextCharacter('-');
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past an OData identifier if found; otherwise leaves the index unchanged.
|
||||
* @return whether an OData identifier has been found at the current index
|
||||
*/
|
||||
private boolean nextODataIdentifier() {
|
||||
int count = 0;
|
||||
if (index < pathSegment.length()) {
|
||||
int code = pathSegment.codePointAt(index);
|
||||
if (index < parseString.length()) {
|
||||
int code = parseString.codePointAt(index);
|
||||
if (Character.isUnicodeIdentifierStart(code) || code == '_') {
|
||||
count++;
|
||||
// Unicode characters outside of the Basic Multilingual Plane are represented as two Java characters.
|
||||
index += Character.isSupplementaryCodePoint(code) ? 2 : 1;
|
||||
while (index < pathSegment.length() && count < 128) {
|
||||
code = pathSegment.codePointAt(index);
|
||||
while (index < parseString.length() && count < 128) {
|
||||
code = parseString.codePointAt(index);
|
||||
if (Character.isUnicodeIdentifierPart(code) && !Character.isISOControl(code)) {
|
||||
count++;
|
||||
// Unicode characters outside of the Basic Multilingual Plane are represented as two Java characters.
|
||||
|
@ -299,16 +327,30 @@ public class UriTokenizer {
|
|||
return count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past a qualified name if found; otherwise leaves the index unchanged.
|
||||
* @return whether a qualified name has been found at the current index
|
||||
*/
|
||||
private boolean nextQualifiedName() {
|
||||
int count = 0;
|
||||
do {
|
||||
final int lastGoodIndex = index;
|
||||
if (!nextODataIdentifier()) {
|
||||
return false;
|
||||
}
|
||||
int count = 1;
|
||||
while (nextCharacter('.')) {
|
||||
if (nextODataIdentifier()) {
|
||||
count++;
|
||||
} else {
|
||||
return false;
|
||||
index--;
|
||||
break;
|
||||
}
|
||||
} while (nextCharacter('.'));
|
||||
return count >= 2;
|
||||
}
|
||||
if (count >= 2) {
|
||||
return true;
|
||||
} else {
|
||||
index = lastGoodIndex;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean nextParameterAliasName() {
|
||||
|
@ -323,12 +365,12 @@ public class UriTokenizer {
|
|||
if (!nextCharacter('\'')) {
|
||||
return false;
|
||||
}
|
||||
while (index < pathSegment.length()) {
|
||||
if (pathSegment.charAt(index) == '\'') {
|
||||
while (index < parseString.length()) {
|
||||
if (parseString.charAt(index) == '\'') {
|
||||
// If a single quote is followed by another single quote,
|
||||
// it represents one single quote within the string literal,
|
||||
// otherwise it marks the end of the string literal.
|
||||
if (index + 1 < pathSegment.length() && pathSegment.charAt(index + 1) == '\'') {
|
||||
if (index + 1 < parseString.length() && parseString.charAt(index + 1) == '\'') {
|
||||
index++;
|
||||
} else {
|
||||
break;
|
||||
|
@ -339,7 +381,13 @@ public class UriTokenizer {
|
|||
return nextCharacter('\'');
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past an integer value if found; otherwise leaves the index unchanged.
|
||||
* @param signed whether a sign character ('+' or '-') at the beginning is allowed
|
||||
* @return whether an integer value has been found at the current index
|
||||
*/
|
||||
private boolean nextIntegerValue(final boolean signed) {
|
||||
final int lastGoodIndex = index;
|
||||
if (signed) {
|
||||
nextSign();
|
||||
}
|
||||
|
@ -347,33 +395,53 @@ public class UriTokenizer {
|
|||
while (nextDigit()) {
|
||||
hasDigits = true;
|
||||
}
|
||||
return hasDigits;
|
||||
}
|
||||
|
||||
/** Finds and returns only decimal-number tokens with a fractional part.
|
||||
* Whole numbers must be found with {@link #nextIntegerValue()}.
|
||||
*/
|
||||
private boolean nextDecimalValue() {
|
||||
return nextIntegerValue(true) && nextCharacter('.') && nextIntegerValue(false);
|
||||
if (hasDigits) {
|
||||
return true;
|
||||
} else {
|
||||
index = lastGoodIndex;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns only floating-point-number tokens with an exponential part
|
||||
* and the special three constants "NaN", "-INF", and "INF".
|
||||
* Whole numbers must be found with {@link #nextIntegerValue()}.
|
||||
* Decimal numbers must be found with {@link #nextDecimalValue()}.
|
||||
* Moves past a decimal value with a fractional part if found; otherwise leaves the index unchanged.
|
||||
* Whole numbers must be found with {@link #nextIntegerValue()}.
|
||||
*/
|
||||
private boolean nextDecimalValue() {
|
||||
final int lastGoodIndex = index;
|
||||
if (nextIntegerValue(true) && nextCharacter('.') && nextIntegerValue(false)) {
|
||||
return true;
|
||||
} else {
|
||||
index = lastGoodIndex;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past a floating-point-number value with an exponential part
|
||||
* or one of the special constants "NaN", "-INF", and "INF"
|
||||
* if found; otherwise leaves the index unchanged.
|
||||
* Whole numbers must be found with {@link #nextIntegerValue()}.
|
||||
* Decimal numbers must be found with {@link #nextDecimalValue()}.
|
||||
*/
|
||||
private boolean nextDoubleValue() {
|
||||
if (nextConstant("NaN") || nextConstant("-INF") || nextConstant("INF")) {
|
||||
return true;
|
||||
} else {
|
||||
final int lastGoodIndex = index;
|
||||
if (!nextIntegerValue(true)) {
|
||||
return false;
|
||||
}
|
||||
if (nextCharacter('.') && !nextIntegerValue(false)) {
|
||||
index = lastGoodIndex;
|
||||
return false;
|
||||
}
|
||||
if ((nextCharacter('E') || nextCharacter('e')) && nextIntegerValue(true)) {
|
||||
return true;
|
||||
} else {
|
||||
index = lastGoodIndex;
|
||||
return false;
|
||||
}
|
||||
return (nextCharacter('E') || nextCharacter('e')) && nextIntegerValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -533,7 +601,12 @@ public class UriTokenizer {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past a JSON string if found; otherwise leaves the index unchanged.
|
||||
* @return whether a JSON string has been found at the current index
|
||||
*/
|
||||
private boolean nextJsonString() {
|
||||
final int lastGoodIndex = index;
|
||||
if (nextCharacter('"')) {
|
||||
do {
|
||||
if (nextCharacter('\\')) {
|
||||
|
@ -541,6 +614,7 @@ public class UriTokenizer {
|
|||
|| nextCharacter('n') || nextCharacter('f') || nextCharacter('r')
|
||||
|| nextCharacter('"') || nextCharacter('/') || nextCharacter('\\')
|
||||
|| nextCharacter('u') && nextHexDigit() && nextHexDigit() && nextHexDigit() && nextHexDigit())) {
|
||||
index = lastGoodIndex;
|
||||
return false;
|
||||
}
|
||||
} else if (nextCharacter('"')) {
|
||||
|
@ -548,16 +622,17 @@ public class UriTokenizer {
|
|||
} else {
|
||||
index++;
|
||||
}
|
||||
} while (index < pathSegment.length());
|
||||
} while (index < parseString.length());
|
||||
index = lastGoodIndex;
|
||||
return false;
|
||||
}
|
||||
index = lastGoodIndex;
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean nextJsonValue() {
|
||||
return nextConstant("null") || nextConstant("true") || nextConstant("false")
|
||||
// If a double or decimal number is not found, the index must be reset; the internal methods don't do that.
|
||||
|| next(TokenKind.PrimitiveDoubleValue) || next(TokenKind.PrimitiveDecimalValue) || nextIntegerValue(true)
|
||||
|| nextDoubleValue() || nextDecimalValue() || nextIntegerValue(true)
|
||||
|| nextJsonString()
|
||||
|| nextJsonArrayOrObject();
|
||||
}
|
||||
|
@ -566,25 +641,42 @@ public class UriTokenizer {
|
|||
return nextJsonString() && nextCharacter(':') && nextJsonValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves past a JSON array or object if found; otherwise leaves the index unchanged.
|
||||
* @return whether a JSON array or object has been found at the current index
|
||||
*/
|
||||
private boolean nextJsonArrayOrObject() {
|
||||
final int lastGoodIndex = index;
|
||||
if (nextCharacter('[')) {
|
||||
if (nextJsonValue()) {
|
||||
while (nextCharacter(',')) {
|
||||
if (!nextJsonValue()) {
|
||||
index = lastGoodIndex;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nextCharacter(']');
|
||||
if (nextCharacter(']')) {
|
||||
return true;
|
||||
} else {
|
||||
index = lastGoodIndex;
|
||||
return false;
|
||||
}
|
||||
} else if (nextCharacter('{')) {
|
||||
if (nextJsonMember()) {
|
||||
while (nextCharacter(',')) {
|
||||
if (!nextJsonMember()) {
|
||||
index = lastGoodIndex;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nextCharacter('}');
|
||||
if (nextCharacter('}')) {
|
||||
return true;
|
||||
} else {
|
||||
index = lastGoodIndex;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -20,24 +20,22 @@ package org.apache.olingo.server.core.uri;
|
|||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.Edm;
|
||||
import org.apache.olingo.commons.api.edm.EdmEntityType;
|
||||
import org.apache.olingo.commons.api.ex.ODataRuntimeException;
|
||||
import org.apache.olingo.server.api.OData;
|
||||
import org.apache.olingo.server.api.edmx.EdmxReference;
|
||||
import org.apache.olingo.server.api.uri.UriInfo;
|
||||
import org.apache.olingo.server.api.uri.UriInfoKind;
|
||||
import org.apache.olingo.server.api.uri.UriResourceAction;
|
||||
import org.apache.olingo.server.api.uri.UriResourceEntitySet;
|
||||
import org.apache.olingo.server.api.uri.queryoption.AliasQueryOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.QueryOption;
|
||||
import org.apache.olingo.server.core.uri.UriInfoImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceActionImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceEntitySetImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.AliasQueryOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.CustomQueryOptionImpl;
|
||||
|
@ -52,15 +50,11 @@ import org.apache.olingo.server.core.uri.queryoption.SelectOptionImpl;
|
|||
import org.apache.olingo.server.core.uri.queryoption.SkipOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.SkipTokenOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.TopOptionImpl;
|
||||
import org.apache.olingo.server.tecsvc.provider.EdmTechProvider;
|
||||
import org.apache.olingo.server.tecsvc.provider.EntityTypeProvider;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
public class UriInfoImplTest {
|
||||
|
||||
private static final Edm edm = OData.newInstance().createServiceMetadata(
|
||||
new EdmTechProvider(), Collections.<EdmxReference> emptyList()).getEdm();
|
||||
|
||||
@Test
|
||||
public void kind() {
|
||||
final UriInfo uriInfo = new UriInfoImpl().setKind(UriInfoKind.all);
|
||||
|
@ -184,9 +178,7 @@ public class UriInfoImplTest {
|
|||
|
||||
@Test
|
||||
public void entityTypeCast() {
|
||||
final EdmEntityType entityType = edm.getEntityType(EntityTypeProvider.nameETKeyNav);
|
||||
assertNotNull(entityType);
|
||||
|
||||
final EdmEntityType entityType = Mockito.mock(EdmEntityType.class);
|
||||
final UriInfo uriInfo = new UriInfoImpl()
|
||||
.setEntityTypeCast(entityType);
|
||||
assertEquals(entityType, uriInfo.getEntityTypeCast());
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
* 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 org.apache.olingo.server.core.uri.parser;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.QueryOption;
|
||||
import org.junit.Test;
|
||||
|
||||
public class UriDecoderTest {
|
||||
|
||||
@Test
|
||||
public void split() throws Exception {
|
||||
assertTrue(UriDecoder.splitAndDecodePath("").isEmpty());
|
||||
assertTrue(UriDecoder.splitAndDecodePath("/").isEmpty());
|
||||
assertEquals(Arrays.asList("a"), UriDecoder.splitAndDecodePath("a"));
|
||||
assertEquals(Arrays.asList("a"), UriDecoder.splitAndDecodePath("a/"));
|
||||
assertEquals(Arrays.asList("a"), UriDecoder.splitAndDecodePath("/a"));
|
||||
assertEquals(Arrays.asList("a", "a"), UriDecoder.splitAndDecodePath("a/a"));
|
||||
assertEquals(Arrays.asList("a", "a"), UriDecoder.splitAndDecodePath("/a/a"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void path() throws Exception {
|
||||
assertEquals(Arrays.asList("a", "entitySet('/')", "bcd"),
|
||||
UriDecoder.splitAndDecodePath("a/entitySet('%2F')/b%63d"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void options() throws Exception {
|
||||
assertTrue(UriDecoder.splitAndDecodeOptions("").isEmpty());
|
||||
|
||||
checkOption("a", "a", "");
|
||||
checkOption("a=b", "a", "b");
|
||||
checkOption("=", "", "");
|
||||
checkOption("=b", "", "b");
|
||||
|
||||
checkOption("a&c", "a", "");
|
||||
checkOption("a&c", "c", "");
|
||||
|
||||
checkOption("a=b&c", "a", "b");
|
||||
checkOption("a=b&c", "c", "");
|
||||
|
||||
checkOption("a=b&c=d", "a", "b");
|
||||
checkOption("a=b&c=d", "c", "d");
|
||||
|
||||
checkOption("=&=", "", "");
|
||||
assertEquals(2, UriDecoder.splitAndDecodeOptions("=&=").size());
|
||||
|
||||
checkOption("=&c=d", "", "");
|
||||
checkOption("=&c=d", "c", "d");
|
||||
|
||||
checkOption("a%62c=d%65f", "abc", "def");
|
||||
checkOption("a='%26%3D'", "a", "'&='");
|
||||
}
|
||||
|
||||
@Test(expected = UriParserSyntaxException.class)
|
||||
public void wrongPercentEncoding() throws Exception {
|
||||
UriDecoder.splitAndDecodePath("%wrong");
|
||||
}
|
||||
|
||||
private void checkOption(final String query, final String name, final String value)
|
||||
throws UriParserSyntaxException {
|
||||
final List<QueryOption> options = UriDecoder.splitAndDecodeOptions(query);
|
||||
for (final QueryOption option : options) {
|
||||
if (option.getName().equals(name)) {
|
||||
assertEquals(value, option.getText());
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail("Option " + name + " not found!");
|
||||
}
|
||||
}
|
|
@ -51,7 +51,7 @@ public class UriTokenizerTest {
|
|||
|
||||
@Test
|
||||
public void sequence() {
|
||||
final UriTokenizer tokenizer = new UriTokenizer("(A=1,B=2);");
|
||||
final UriTokenizer tokenizer = new UriTokenizer("(A=1,B=2);.*/+");
|
||||
assertTrue(tokenizer.next(TokenKind.OPEN));
|
||||
assertFalse(tokenizer.next(TokenKind.OPEN));
|
||||
assertTrue(tokenizer.next(TokenKind.ODataIdentifier));
|
||||
|
@ -68,6 +68,10 @@ public class UriTokenizerTest {
|
|||
assertFalse(tokenizer.next(TokenKind.EOF));
|
||||
assertTrue(tokenizer.next(TokenKind.CLOSE));
|
||||
assertTrue(tokenizer.next(TokenKind.SEMI));
|
||||
assertTrue(tokenizer.next(TokenKind.DOT));
|
||||
assertTrue(tokenizer.next(TokenKind.STAR));
|
||||
assertTrue(tokenizer.next(TokenKind.SLASH));
|
||||
assertTrue(tokenizer.next(TokenKind.PLUS));
|
||||
assertTrue(tokenizer.next(TokenKind.EOF));
|
||||
}
|
||||
|
||||
|
@ -100,8 +104,10 @@ public class UriTokenizerTest {
|
|||
public void qualifiedName() {
|
||||
assertTrue(new UriTokenizer("namespace.name").next(TokenKind.QualifiedName));
|
||||
|
||||
final UriTokenizer tokenizer = new UriTokenizer("multi.part.namespace.name");
|
||||
final UriTokenizer tokenizer = new UriTokenizer("multi.part.namespace.name.1");
|
||||
assertTrue(tokenizer.next(TokenKind.QualifiedName));
|
||||
assertTrue(tokenizer.next(TokenKind.DOT));
|
||||
assertTrue(tokenizer.next(TokenKind.PrimitiveIntegerValue));
|
||||
assertTrue(tokenizer.next(TokenKind.EOF));
|
||||
|
||||
assertFalse(new UriTokenizer("name").next(TokenKind.QualifiedName));
|
||||
|
@ -334,6 +340,7 @@ public class UriTokenizerTest {
|
|||
assertFalse(new UriTokenizer("[,1]").next(TokenKind.jsonArrayOrObject));
|
||||
assertFalse(new UriTokenizer("[1,,2]").next(TokenKind.jsonArrayOrObject));
|
||||
assertFalse(new UriTokenizer("[1,x]").next(TokenKind.jsonArrayOrObject));
|
||||
assertFalse(new UriTokenizer("[+\"x\"]").next(TokenKind.jsonArrayOrObject));
|
||||
assertFalse(new UriTokenizer("{\"name\":1,}").next(TokenKind.jsonArrayOrObject));
|
||||
assertFalse(new UriTokenizer("{,\"name\":1}").next(TokenKind.jsonArrayOrObject));
|
||||
assertFalse(new UriTokenizer("{\"name\":1,,\"name2\":2}").next(TokenKind.jsonArrayOrObject));
|
||||
|
@ -350,6 +357,7 @@ public class UriTokenizerTest {
|
|||
assertFalse(new UriTokenizer("[\"\\u1\"]").next(TokenKind.jsonArrayOrObject));
|
||||
assertFalse(new UriTokenizer("[\"\\u12x\"]").next(TokenKind.jsonArrayOrObject));
|
||||
assertFalse(new UriTokenizer("[\"\\u123x\"]").next(TokenKind.jsonArrayOrObject));
|
||||
wrongToken(TokenKind.jsonArrayOrObject, "[{\"name\":+123.456},null]", '\\');
|
||||
}
|
||||
|
||||
private void wrongToken(final TokenKind kind, final String value, final char disturbCharacter) {
|
||||
|
@ -358,6 +366,7 @@ public class UriTokenizerTest {
|
|||
final UriTokenizer tokenizer = new UriTokenizer(value + disturbCharacter);
|
||||
assertTrue(tokenizer.next(kind));
|
||||
assertEquals(value, tokenizer.getText());
|
||||
assertFalse(tokenizer.next(TokenKind.EOF));
|
||||
|
||||
// Place the disturbing character at every position in the value string
|
||||
// and check that this leads to a failed token recognition.
|
||||
|
|
|
@ -40,18 +40,19 @@ import org.apache.olingo.server.api.uri.UriResourceKind;
|
|||
import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
|
||||
import org.apache.olingo.server.core.uri.parser.UriParserException;
|
||||
import org.apache.olingo.server.core.uri.parser.UriParserSemanticException;
|
||||
import org.apache.olingo.server.core.uri.parser.UriParserSemanticException.MessageKeys;
|
||||
import org.apache.olingo.server.core.uri.parser.UriParserSyntaxException;
|
||||
import org.apache.olingo.server.core.uri.parser.search.SearchParserException;
|
||||
import org.apache.olingo.server.core.uri.testutil.FilterValidator;
|
||||
import org.apache.olingo.server.core.uri.testutil.TestUriValidator;
|
||||
import org.apache.olingo.server.core.uri.validator.UriValidationException;
|
||||
import org.apache.olingo.server.tecsvc.provider.ActionProvider;
|
||||
import org.apache.olingo.server.tecsvc.provider.ComplexTypeProvider;
|
||||
import org.apache.olingo.server.tecsvc.provider.ContainerProvider;
|
||||
import org.apache.olingo.server.tecsvc.provider.EdmTechProvider;
|
||||
import org.apache.olingo.server.tecsvc.provider.EntityTypeProvider;
|
||||
import org.apache.olingo.server.tecsvc.provider.EnumTypeProvider;
|
||||
import org.apache.olingo.server.tecsvc.provider.FunctionProvider;
|
||||
import org.apache.olingo.server.tecsvc.provider.PropertyProvider;
|
||||
import org.apache.olingo.server.tecsvc.provider.TypeDefinitionProvider;
|
||||
import org.junit.Ignore;
|
||||
|
@ -2731,17 +2732,6 @@ public class TestFullResourcePath {
|
|||
.isNavProperty("NavPropertyETKeyNavOne", EntityTypeProvider.nameETKeyNav, false)
|
||||
.isType(EntityTypeProvider.nameETKeyNav);
|
||||
|
||||
testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='2')", "$select=olingo.odata.test1.ETBaseTwoKeyNav"
|
||||
+ "/PropertyInt16")
|
||||
.isKind(UriInfoKind.resource).goPath()
|
||||
.first()
|
||||
.isKeyPredicate(0, "PropertyInt16", "1")
|
||||
.isKeyPredicate(1, "PropertyString", "'2'")
|
||||
.isSelectStartType(0, EntityTypeProvider.nameETBaseTwoKeyNav)
|
||||
.goSelectItem(0)
|
||||
.first()
|
||||
.isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
|
||||
testUri.run("ESKeyNav", "$expand=NavPropertyETKeyNavOne($select=PropertyInt16)")
|
||||
.isKind(UriInfoKind.resource)
|
||||
.goPath().first()
|
||||
|
@ -2763,17 +2753,6 @@ public class TestFullResourcePath {
|
|||
.goUpExpandValidator()
|
||||
.isSelectText("PropertyCompNav/PropertyInt16");
|
||||
|
||||
testUri.run("ESMixEnumDefCollComp",
|
||||
"$select=PropertyEnumString,PropertyDefString,CollPropertyEnumString,CollPropertyDefString")
|
||||
.isKind(UriInfoKind.resource)
|
||||
.goSelectItemPath(0).isPrimitiveProperty("PropertyEnumString", EnumTypeProvider.nameENString, false)
|
||||
.goUpUriValidator()
|
||||
.goSelectItemPath(1).isPrimitiveProperty("PropertyDefString", TypeDefinitionProvider.nameTDString, false)
|
||||
.goUpUriValidator()
|
||||
.goSelectItemPath(2).isPrimitiveProperty("CollPropertyEnumString", EnumTypeProvider.nameENString, true)
|
||||
.goUpUriValidator()
|
||||
.goSelectItemPath(3).isPrimitiveProperty("CollPropertyDefString", TypeDefinitionProvider.nameTDString, true);
|
||||
|
||||
testUri.runEx("ESKeyNav", "$expand=undefined")
|
||||
.isExSemantic(MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
|
||||
testUri.runEx("ESTwoKeyNav", "$expand=PropertyCompNav/undefined")
|
||||
|
@ -2814,6 +2793,134 @@ public class TestFullResourcePath {
|
|||
.isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void select() throws Exception {
|
||||
testUri.run("ESTwoKeyNav", "$select=*")
|
||||
.isSelectItemStar(0);
|
||||
|
||||
testUri.run("ESTwoKeyNav", "$select=olingo.odata.test1.*")
|
||||
.isSelectItemAllOp(0, new FullQualifiedName("olingo.odata.test1", "*"));
|
||||
testUri.run("ESTwoKeyNav", "$select=Namespace1_Alias.*")
|
||||
.isSelectItemAllOp(0, new FullQualifiedName("Namespace1_Alias", "*"));
|
||||
|
||||
testUri.run("ESTwoKeyNav", "$select=PropertyString")
|
||||
.goSelectItemPath(0).isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
|
||||
|
||||
testUri.run("ESTwoKeyNav", "$select=PropertyComp")
|
||||
.goSelectItemPath(0).isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false);
|
||||
|
||||
testUri.run("ESAllPrim", "$select=PropertyTimeOfDay,PropertyDate,NavPropertyETTwoPrimOne")
|
||||
.isKind(UriInfoKind.resource)
|
||||
.goSelectItemPath(0).first().isPrimitiveProperty("PropertyTimeOfDay", PropertyProvider.nameTimeOfDay, false)
|
||||
.goUpUriValidator()
|
||||
.goSelectItemPath(1).first().isPrimitiveProperty("PropertyDate", PropertyProvider.nameDate, false)
|
||||
.goUpUriValidator()
|
||||
.goSelectItemPath(2).first().isNavProperty("NavPropertyETTwoPrimOne", EntityTypeProvider.nameETTwoPrim, false);
|
||||
|
||||
testUri.run("ESMixEnumDefCollComp",
|
||||
"$select=PropertyEnumString,PropertyDefString,CollPropertyEnumString,CollPropertyDefString")
|
||||
.isKind(UriInfoKind.resource)
|
||||
.goSelectItemPath(0).isPrimitiveProperty("PropertyEnumString", EnumTypeProvider.nameENString, false)
|
||||
.goUpUriValidator()
|
||||
.goSelectItemPath(1).isPrimitiveProperty("PropertyDefString", TypeDefinitionProvider.nameTDString, false)
|
||||
.goUpUriValidator()
|
||||
.goSelectItemPath(2).isPrimitiveProperty("CollPropertyEnumString", EnumTypeProvider.nameENString, true)
|
||||
.goUpUriValidator()
|
||||
.goSelectItemPath(3).isPrimitiveProperty("CollPropertyDefString", TypeDefinitionProvider.nameTDString, true);
|
||||
|
||||
testUri.run("ESTwoKeyNav", "$select=PropertyComp/PropertyInt16")
|
||||
.goSelectItemPath(0)
|
||||
.first()
|
||||
.isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
|
||||
.n()
|
||||
.isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
|
||||
testUri.run("ESTwoKeyNav", "$select=PropertyComp/PropertyComp")
|
||||
.goSelectItemPath(0)
|
||||
.first()
|
||||
.isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
|
||||
.n()
|
||||
.isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTAllPrim, false);
|
||||
|
||||
testUri.run("ESTwoKeyNav", "$select=olingo.odata.test1.ETBaseTwoKeyNav")
|
||||
.isSelectStartType(0, EntityTypeProvider.nameETBaseTwoKeyNav);
|
||||
|
||||
testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='2')",
|
||||
"$select=olingo.odata.test1.ETBaseTwoKeyNav/PropertyInt16")
|
||||
.isKind(UriInfoKind.resource).goPath()
|
||||
.first()
|
||||
.isKeyPredicate(0, "PropertyInt16", "1")
|
||||
.isKeyPredicate(1, "PropertyString", "'2'")
|
||||
.isSelectStartType(0, EntityTypeProvider.nameETBaseTwoKeyNav)
|
||||
.goSelectItem(0)
|
||||
.first()
|
||||
.isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
|
||||
testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='1')/PropertyCompNav",
|
||||
"$select=olingo.odata.test1.CTTwoBasePrimCompNav")
|
||||
.isSelectStartType(0, ComplexTypeProvider.nameCTTwoBasePrimCompNav);
|
||||
|
||||
testUri.run("ESTwoKeyNav", "$select=PropertyCompNav/olingo.odata.test1.CTTwoBasePrimCompNav")
|
||||
.goSelectItemPath(0)
|
||||
.first()
|
||||
.isComplexProperty("PropertyCompNav", ComplexTypeProvider.nameCTBasePrimCompNav, false)
|
||||
.isTypeFilter(ComplexTypeProvider.nameCTTwoBasePrimCompNav);
|
||||
|
||||
testUri.run("ESTwoKeyNav", "$select=PropertyCompNav/Namespace1_Alias.CTTwoBasePrimCompNav/PropertyInt16")
|
||||
.goSelectItemPath(0)
|
||||
.first()
|
||||
.isComplexProperty("PropertyCompNav", ComplexTypeProvider.nameCTBasePrimCompNav, false)
|
||||
.isTypeFilter(ComplexTypeProvider.nameCTTwoBasePrimCompNav)
|
||||
.n()
|
||||
.isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
|
||||
testUri.run("ESAllPrim", "$select=olingo.odata.test1.BAESAllPrimRTETAllPrim")
|
||||
.goSelectItemPath(0)
|
||||
.first()
|
||||
.isAction(ActionProvider.nameBAESAllPrimRTETAllPrim.getName());
|
||||
testUri.run("ESTwoKeyNav", "$select=Namespace1_Alias.BFCESTwoKeyNavRTString")
|
||||
.goSelectItemPath(0)
|
||||
.first()
|
||||
.isFunction(FunctionProvider.nameBFCESTwoKeyNavRTString.getName());
|
||||
testUri.run("ESTwoKeyNav", "$select=olingo.odata.test1.BFCESTwoKeyNavRTStringParam(ParameterComp)")
|
||||
.goSelectItemPath(0)
|
||||
.first()
|
||||
.isFunction(FunctionProvider.nameBFCESTwoKeyNavRTStringParam.getName());
|
||||
|
||||
testUri.runEx("ESMixPrimCollComp", "$select=wrong")
|
||||
.isExSemantic(MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
|
||||
testUri.runEx("ESMixPrimCollComp", "$select=PropertyComp/wrong")
|
||||
.isExSemantic(MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
|
||||
testUri.runEx("ESMixPrimCollComp", "$select=PropertyComp///PropertyInt16")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
testUri.runEx("ESMixPrimCollComp", "$select=/PropertyInt16")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
testUri.runEx("ESMixPrimCollComp", "$select=PropertyInt16+")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
|
||||
testUri.runEx("ESTwoKeyNav", "$select=olingo.odata.test1.1")
|
||||
.isExSemantic(MessageKeys.UNKNOWN_PART);
|
||||
testUri.runEx("ESTwoKeyNav", "$select=olingo.odata.test1.ETKeyNav")
|
||||
.isExSemantic(MessageKeys.INCOMPATIBLE_TYPE_FILTER);
|
||||
testUri.runEx("ESTwoKeyNav", "$select=PropertyCompNav/olingo.odata.test1.CTTwoPrim")
|
||||
.isExSemantic(MessageKeys.INCOMPATIBLE_TYPE_FILTER);
|
||||
testUri.runEx("ESTwoKeyNav", "$select=PropertyCompNav/olingo.odata.test1.CTwrong")
|
||||
.isExSemantic(MessageKeys.UNKNOWN_TYPE);
|
||||
testUri.runEx("ESTwoKeyNav", "$select=PropertyCompNav/.")
|
||||
.isExSemantic(MessageKeys.UNKNOWN_PART);
|
||||
testUri.runEx("ESTwoKeyNav", "$select=PropertyCompNav/olingo.odata.test1.CTTwoBasePrimCompNav/.")
|
||||
.isExSemantic(MessageKeys.UNKNOWN_PART);
|
||||
testUri.runEx("AIRT", "$select=wrong")
|
||||
.isExSemantic(MessageKeys.ONLY_FOR_TYPED_PARTS);
|
||||
testUri.runEx("AIRT", "$select=olingo.odata.test1.BAESAllPrimRT")
|
||||
.isExSemantic(MessageKeys.ONLY_FOR_TYPED_PARTS);
|
||||
testUri.runEx("ESTwoKeyNav", "$select=olingo.odata.test1.BFwrong")
|
||||
.isExSemantic(MessageKeys.UNKNOWN_PART);
|
||||
testUri.runEx("ESTwoKeyNav", "$select=olingo.odata.test1.BFCESTwoKeyNavRTStringParam()")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
testUri.runEx("ESTwoKeyNav", "$select=Namespace1_Alias.BFCESTwoKeyNavRTStringParam(ParameterComp,...)")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runTop() throws Exception {
|
||||
// top
|
||||
|
@ -5950,7 +6057,7 @@ public class TestFullResourcePath {
|
|||
|
||||
testUri.runEx("ESTwoKeyNav/olingo.odata.test1.BFCESTwoKeyNavRTStringParam"
|
||||
+ "(ParameterComp={\"PropertyInt16\":1,\"PropertyString\":\"Test\"})")
|
||||
.isExSemantic(UriParserSemanticException.MessageKeys.INVALID_KEY_VALUE);
|
||||
.isExSemantic(MessageKeys.INVALID_KEY_VALUE);
|
||||
|
||||
testUri.runEx("FICRTCTTwoPrimTwoParam(ParameterInt16=1,ParameterString=null)")
|
||||
.isExValidation(UriValidationException.MessageKeys.MISSING_PARAMETER);
|
||||
|
@ -5964,7 +6071,7 @@ public class TestFullResourcePath {
|
|||
testUri.run("FICRTCTTwoPrimTwoParam(ParameterInt16=1,ParameterString=@test)", "@test='null'");
|
||||
|
||||
testUri.runEx("FICRTCTTwoPrimTwoParam(ParameterInt16=1,ParameterString=@test,UnknownParam=1)", "@test='null'")
|
||||
.isExSemantic(UriParserSemanticException.MessageKeys.FUNCTION_NOT_FOUND);
|
||||
.isExSemantic(MessageKeys.FUNCTION_NOT_FOUND);
|
||||
|
||||
testUri.run("FICRTCollCTTwoPrimTwoParam(ParameterInt16=1,ParameterString=@test)", "@test='null'");
|
||||
testUri.run("FICRTCollCTTwoPrimTwoParam(ParameterInt16=1,ParameterString=@test)", "@test=null");
|
||||
|
@ -5975,7 +6082,7 @@ public class TestFullResourcePath {
|
|||
.isExSyntax(UriParserSyntaxException.MessageKeys.DUPLICATED_ALIAS);
|
||||
|
||||
testUri.runEx("ESAllPrim", "$filter=FINRTInt16() eq 0")
|
||||
.isExSemantic(UriParserSemanticException.MessageKeys.FUNCTION_IMPORT_NOT_ALLOWED);
|
||||
.isExSemantic(MessageKeys.FUNCTION_IMPORT_NOT_ALLOWED);
|
||||
|
||||
testUri.runEx("ESTwoKeyNav", "$filter=olingo.odata.test1.BFCESTwoKeyNavRTStringParam"
|
||||
+ "(ParameterComp=@p1) eq 0&@p1={\"PropertyInt16\":1,\"PropertyString\":\"1\"")
|
||||
|
|
|
@ -22,13 +22,11 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.Edm;
|
||||
import org.apache.olingo.commons.api.edm.FullQualifiedName;
|
||||
import org.apache.olingo.server.api.OData;
|
||||
import org.apache.olingo.server.api.edmx.EdmxReference;
|
||||
import org.apache.olingo.server.api.uri.UriInfoKind;
|
||||
import org.apache.olingo.server.api.uri.UriResourceKind;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
|
||||
import org.apache.olingo.server.core.uri.parser.UriParserSemanticException;
|
||||
import org.apache.olingo.server.core.uri.parser.UriParserSyntaxException;
|
||||
import org.apache.olingo.server.core.uri.testutil.FilterValidator;
|
||||
import org.apache.olingo.server.core.uri.testutil.ResourceValidator;
|
||||
|
@ -1112,64 +1110,6 @@ public class TestUriParserImpl {
|
|||
.isMethod(MethodKind.GEOINTERSECTS, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelect() throws Exception {
|
||||
testUri.run("ESTwoKeyNav", "$select=*")
|
||||
.isSelectItemStar(0);
|
||||
|
||||
testUri.run("ESTwoKeyNav", "$select=olingo.odata.test1.*")
|
||||
.isSelectItemAllOp(0, new FullQualifiedName("olingo.odata.test1", "*"));
|
||||
|
||||
testUri.run("ESTwoKeyNav", "$select=PropertyString")
|
||||
.goSelectItemPath(0).isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
|
||||
|
||||
testUri.run("ESTwoKeyNav", "$select=PropertyComp")
|
||||
.goSelectItemPath(0).isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false);
|
||||
|
||||
testUri.run("ESTwoKeyNav", "$select=PropertyComp/PropertyInt16")
|
||||
.goSelectItemPath(0)
|
||||
.first()
|
||||
.isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
|
||||
.n()
|
||||
.isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
|
||||
testUri.run("ESTwoKeyNav", "$select=PropertyComp/PropertyComp")
|
||||
.goSelectItemPath(0)
|
||||
.first()
|
||||
.isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
|
||||
.n()
|
||||
.isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTAllPrim, false);
|
||||
|
||||
testUri.run("ESTwoKeyNav", "$select=olingo.odata.test1.ETBaseTwoKeyNav")
|
||||
.isSelectStartType(0, EntityTypeProvider.nameETBaseTwoKeyNav);
|
||||
|
||||
testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='1')/PropertyCompNav",
|
||||
"$select=olingo.odata.test1.CTTwoBasePrimCompNav")
|
||||
.isSelectStartType(0, ComplexTypeProvider.nameCTTwoBasePrimCompNav);
|
||||
|
||||
testUri.run("ESTwoKeyNav", "$select=PropertyCompNav/olingo.odata.test1.CTTwoBasePrimCompNav")
|
||||
.goSelectItemPath(0)
|
||||
.first()
|
||||
.isComplexProperty("PropertyCompNav", ComplexTypeProvider.nameCTBasePrimCompNav, false)
|
||||
.n()
|
||||
.isTypeFilterOnCollection(ComplexTypeProvider.nameCTTwoBasePrimCompNav);
|
||||
|
||||
testUri.run("ESAllPrim", "$select=PropertyTimeOfDay,PropertyDate,PropertyTimeOfDay")
|
||||
.isKind(UriInfoKind.resource)
|
||||
.goSelectItemPath(0).first().isPrimitiveProperty("PropertyTimeOfDay", PropertyProvider.nameTimeOfDay, false)
|
||||
.goUpUriValidator()
|
||||
.goSelectItemPath(1).first().isPrimitiveProperty("PropertyDate", PropertyProvider.nameDate, false);
|
||||
|
||||
testUri.runEx("ESMixPrimCollComp", "$select=wrong")
|
||||
.isExSemantic(UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
|
||||
testUri.runEx("ESMixPrimCollComp", "$select=PropertyComp/wrong")
|
||||
.isExSemantic(UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
|
||||
testUri.runEx("ESMixPrimCollComp", "$select=PropertyComp///PropertyInt16")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
testUri.runEx("ESMixPrimCollComp", "$select=/PropertyInt16")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
|
||||
private final String encode(final String uriPart) {
|
||||
return uriPart.replaceAll(":", "%3A");
|
||||
}
|
||||
|
|
|
@ -1,150 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
* 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 org.apache.olingo.server.core.uri.parser;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class RawUriTest {
|
||||
|
||||
private RawUri runRawParser(final String path, final String query, final int skipSegments)
|
||||
throws UriParserSyntaxException {
|
||||
return UriDecoder.decodeUri(path, query, null, skipSegments);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOption() throws Exception {
|
||||
RawUri rawUri;
|
||||
rawUri = runRawParser("", "", 0);
|
||||
checkOptionCount(rawUri, 0);
|
||||
|
||||
rawUri = runRawParser("", "a", 0);
|
||||
checkOption(rawUri, 0, "a", "");
|
||||
|
||||
rawUri = runRawParser("", "a=b", 0);
|
||||
checkOption(rawUri, 0, "a", "b");
|
||||
|
||||
rawUri = runRawParser("", "=", 0);
|
||||
checkOption(rawUri, 0, "", "");
|
||||
|
||||
rawUri = runRawParser("", "=b", 0);
|
||||
checkOption(rawUri, 0, "", "b");
|
||||
|
||||
rawUri = runRawParser("", "a&c", 0);
|
||||
checkOption(rawUri, 0, "a", "");
|
||||
checkOption(rawUri, 1, "c", "");
|
||||
|
||||
rawUri = runRawParser("", "a=b&c", 0);
|
||||
checkOption(rawUri, 0, "a", "b");
|
||||
checkOption(rawUri, 1, "c", "");
|
||||
|
||||
rawUri = runRawParser("", "a=b&c=d", 0);
|
||||
checkOption(rawUri, 0, "a", "b");
|
||||
checkOption(rawUri, 1, "c", "d");
|
||||
|
||||
rawUri = runRawParser("", "=&=", 0);
|
||||
checkOption(rawUri, 0, "", "");
|
||||
checkOption(rawUri, 1, "", "");
|
||||
|
||||
rawUri = runRawParser("", "=&c=d", 0);
|
||||
checkOption(rawUri, 0, "", "");
|
||||
checkOption(rawUri, 1, "c", "d");
|
||||
}
|
||||
|
||||
private void checkOption(final RawUri rawUri, final int index, final String name, final String value) {
|
||||
RawUri.QueryOption option = rawUri.queryOptionListDecoded.get(index);
|
||||
|
||||
assertEquals(name, option.name);
|
||||
assertEquals(value, option.value);
|
||||
}
|
||||
|
||||
private void checkOptionCount(final RawUri rawUri, final int count) {
|
||||
assertEquals(count, rawUri.queryOptionListDecoded.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPath() throws Exception {
|
||||
RawUri rawUri;
|
||||
|
||||
rawUri = runRawParser("", null, 0);
|
||||
checkPath(rawUri, "", Collections.<String> emptyList());
|
||||
|
||||
rawUri = runRawParser("/", null, 0);
|
||||
checkPath(rawUri, "/", Collections.<String> emptyList());
|
||||
|
||||
rawUri = runRawParser("/entitySet", null, 0);
|
||||
checkPath(rawUri, "/entitySet", Arrays.asList("entitySet"));
|
||||
|
||||
rawUri = runRawParser("//entitySet", null, 0);
|
||||
checkPath(rawUri, "//entitySet", Arrays.asList("entitySet"));
|
||||
|
||||
rawUri = runRawParser("entitySet", null, 0);
|
||||
checkPath(rawUri, "entitySet", Arrays.asList("entitySet"));
|
||||
|
||||
rawUri = runRawParser("/nonServiceSegment/entitySet", null, 0);
|
||||
checkPath(rawUri, "/nonServiceSegment/entitySet", Arrays.asList("nonServiceSegment", "entitySet"));
|
||||
|
||||
rawUri = runRawParser("/nonServiceSegment/entitySet", null, 1);
|
||||
checkPath(rawUri, "/nonServiceSegment/entitySet", Arrays.asList("entitySet"));
|
||||
|
||||
rawUri = runRawParser("nonServiceSegment/entitySet", null, 0);
|
||||
checkPath(rawUri, "nonServiceSegment/entitySet", Arrays.asList("nonServiceSegment", "entitySet"));
|
||||
|
||||
rawUri = runRawParser("nonServiceSegment/entitySet", null, 1);
|
||||
checkPath(rawUri, "nonServiceSegment/entitySet", Arrays.asList("entitySet"));
|
||||
|
||||
rawUri = runRawParser("non//Service/Segment///entitySet/", null, 3);
|
||||
checkPath(rawUri, "non//Service/Segment///entitySet/", Arrays.asList("entitySet"));
|
||||
|
||||
rawUri = runRawParser("/a", "abc=xx+yz", 0);
|
||||
checkPath(rawUri, "/a", Arrays.asList("a"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplit() {
|
||||
assertTrue(UriDecoder.splitSkipEmpty("", '/').isEmpty());
|
||||
assertTrue(UriDecoder.splitSkipEmpty("/", '/').isEmpty());
|
||||
assertEquals(Arrays.asList("a"), UriDecoder.splitSkipEmpty("a", '/'));
|
||||
assertEquals(Arrays.asList("a"), UriDecoder.splitSkipEmpty("a/", '/'));
|
||||
assertEquals(Arrays.asList("a"), UriDecoder.splitSkipEmpty("/a", '/'));
|
||||
assertEquals(Arrays.asList("a", "a"), UriDecoder.splitSkipEmpty("a/a", '/'));
|
||||
assertEquals(Arrays.asList("a", "a"), UriDecoder.splitSkipEmpty("/a/a", '/'));
|
||||
}
|
||||
|
||||
private void checkPath(final RawUri rawUri, final String path, final List<String> list) {
|
||||
assertEquals(path, rawUri.path);
|
||||
|
||||
assertEquals(list.size(), rawUri.pathSegmentListDecoded.size());
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
assertEquals(list.get(i), rawUri.pathSegmentListDecoded.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = UriParserSyntaxException.class)
|
||||
public void wrongPercentEncoding() throws Exception {
|
||||
runRawParser("%wrong", null, 0);
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
* 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 org.apache.olingo.server.core.uri.testutil;
|
||||
|
||||
import org.antlr.v4.runtime.DefaultErrorStrategy;
|
||||
import org.antlr.v4.runtime.DiagnosticErrorListener;
|
||||
import org.apache.olingo.commons.api.edm.Edm;
|
||||
import org.apache.olingo.server.api.OData;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriParserParser;
|
||||
import org.apache.olingo.server.core.uri.parser.Parser;
|
||||
|
||||
public class ParserWithLogging extends Parser {
|
||||
TestErrorLogger errorCollector1;
|
||||
TestErrorLogger errorCollector2;
|
||||
|
||||
public ParserWithLogging(final Edm edm, final OData odata) {
|
||||
super(edm, odata);
|
||||
errorCollector1 = new TestErrorLogger("Stage 1", 1);
|
||||
errorCollector2 = new TestErrorLogger("Stage 2", 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addStage2ErrorStategy(final UriParserParser parser) {
|
||||
// Don't throw an at first syntax error, so the error listener will be called
|
||||
parser.setErrorHandler(new DefaultErrorStrategy());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addStage1ErrorListener(final UriParserParser parser) {
|
||||
// Log error to console
|
||||
parser.removeErrorListeners();
|
||||
parser.addErrorListener(errorCollector1);
|
||||
parser.addErrorListener(new DiagnosticErrorListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addStage2ErrorListener(final UriParserParser parser) {
|
||||
// Log error to console
|
||||
parser.removeErrorListeners();
|
||||
parser.addErrorListener(errorCollector2);
|
||||
parser.addErrorListener(new DiagnosticErrorListener());
|
||||
}
|
||||
}
|
|
@ -52,6 +52,7 @@ import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
|
|||
import org.apache.olingo.server.api.uri.queryoption.SelectItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.SelectOption;
|
||||
import org.apache.olingo.server.core.uri.UriResourceWithKeysImpl;
|
||||
import org.apache.olingo.server.core.uri.parser.Parser;
|
||||
import org.apache.olingo.server.core.uri.validator.UriValidationException;
|
||||
import org.apache.olingo.server.core.uri.validator.UriValidator;
|
||||
|
||||
|
@ -85,7 +86,7 @@ public class ResourceValidator implements TestValidator {
|
|||
// --- Execution ---
|
||||
|
||||
public ResourceValidator run(final String path) {
|
||||
ParserWithLogging testParser = new ParserWithLogging(edm, odata);
|
||||
Parser testParser = new Parser(edm, odata);
|
||||
|
||||
UriInfo uriInfoTmp = null;
|
||||
uriPathInfo = null;
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
* 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 org.apache.olingo.server.core.uri.testutil;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.antlr.v4.runtime.ANTLRErrorListener;
|
||||
import org.antlr.v4.runtime.Parser;
|
||||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.Recognizer;
|
||||
import org.antlr.v4.runtime.atn.ATNConfigSet;
|
||||
import org.antlr.v4.runtime.dfa.DFA;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriLexer;
|
||||
|
||||
class TestErrorLogger implements ANTLRErrorListener {
|
||||
|
||||
private String prefix;
|
||||
private int logLevel = 0;
|
||||
|
||||
public TestErrorLogger(final String prefix, final int logLevel) {
|
||||
this.prefix = prefix;
|
||||
this.logLevel = logLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syntaxError(final Recognizer<?, ?> recognizer, final Object offendingSymbol, final int line,
|
||||
final int charPositionInLine,
|
||||
final String msg, final RecognitionException e) {
|
||||
|
||||
if (logLevel > 0) {
|
||||
System.out.println("\n" + prefix + " -- SyntaxError");
|
||||
trace(recognizer, offendingSymbol, line, charPositionInLine, msg, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportAmbiguity(final Parser recognizer, final DFA dfa, final int startIndex, final int stopIndex,
|
||||
final boolean exact,
|
||||
final BitSet ambigAlts, final ATNConfigSet configs) {
|
||||
// Test
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportAttemptingFullContext(final Parser recognizer, final DFA dfa, final int startIndex,
|
||||
final int stopIndex,
|
||||
final BitSet conflictingAlts, final ATNConfigSet configs) {
|
||||
// Test
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportContextSensitivity(final Parser recognizer, final DFA dfa, final int startIndex,
|
||||
final int stopIndex, final int prediction,
|
||||
final ATNConfigSet configs) {
|
||||
// Test
|
||||
}
|
||||
|
||||
private void printStack(final Recognizer<?, ?> recognizer) {
|
||||
List<String> stack = ((Parser) recognizer).getRuleInvocationStack();
|
||||
Collections.reverse(stack);
|
||||
System.out.println(" rule stack: " + stack);
|
||||
}
|
||||
|
||||
public void trace(final Recognizer<?, ?> recognizer, final Object offendingSymbol,
|
||||
final int line, final int charPositionInLine, final String msg, final RecognitionException e) {
|
||||
|
||||
System.out.println("Error message: " + msg);
|
||||
|
||||
printStack(recognizer);
|
||||
|
||||
System.out.println(" line/char :" + line + " / " + charPositionInLine);
|
||||
System.out.println(" sym :" + offendingSymbol);
|
||||
if (e != null && e.getOffendingToken() != null) {
|
||||
|
||||
String lexerTokenName = "";
|
||||
try {
|
||||
lexerTokenName = UriLexer.VOCABULARY.getDisplayName(e.getOffendingToken().getType());
|
||||
} catch (ArrayIndexOutOfBoundsException es) {
|
||||
lexerTokenName = "token error";
|
||||
}
|
||||
|
||||
System.out.println(" tokenname:" + lexerTokenName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -29,35 +29,18 @@ import org.apache.olingo.server.core.uri.antlr.UriLexer;
|
|||
public class TokenValidator {
|
||||
|
||||
private String input = null;
|
||||
|
||||
private List<? extends Token> tokens = null;
|
||||
private Token curToken = null;
|
||||
private Exception curException = null;
|
||||
|
||||
private int startMode;
|
||||
private int logLevel = 0;
|
||||
|
||||
// --- Setup ---
|
||||
|
||||
public TokenValidator log(final int logLevel) {
|
||||
this.logLevel = logLevel;
|
||||
return this;
|
||||
}
|
||||
|
||||
// --- Execution ---
|
||||
|
||||
public TokenValidator run(final String uri) {
|
||||
input = uri;
|
||||
|
||||
tokens = parseInput(uri);
|
||||
if (logLevel > 0) {
|
||||
showTokens();
|
||||
}
|
||||
|
||||
first();
|
||||
exFirst();
|
||||
logLevel = 0;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -87,31 +70,6 @@ public class TokenValidator {
|
|||
return this;
|
||||
}
|
||||
|
||||
public TokenValidator exLast() {
|
||||
// curException = exceptions.get(exceptions.size() - 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
// navigate within the exception list
|
||||
public TokenValidator exFirst() {
|
||||
try {
|
||||
// curException = exceptions.get(0);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
curException = null;
|
||||
}
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
public TokenValidator exAt(final int index) {
|
||||
try {
|
||||
// curException = exceptions.get(index);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
curException = null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// --- Validation ---
|
||||
|
||||
public TokenValidator isText(final String expected) {
|
||||
|
@ -162,32 +120,8 @@ public class TokenValidator {
|
|||
|
||||
private List<? extends Token> parseInput(final String input) {
|
||||
ANTLRInputStream inputStream = new ANTLRInputStream(input);
|
||||
|
||||
UriLexer lexer = new UriLexerWithTrace(inputStream, logLevel, startMode);
|
||||
// lexer.addErrorListener(new ErrorCollector(this));
|
||||
UriLexer lexer = new UriLexer(inputStream);
|
||||
lexer.mode(startMode);
|
||||
return lexer.getAllTokens();
|
||||
}
|
||||
|
||||
public TokenValidator showTokens() {
|
||||
boolean first = true;
|
||||
System.out.println("input: " + input);
|
||||
String nL = "\n";
|
||||
String out = "[" + nL;
|
||||
for (Token token : tokens) {
|
||||
if (!first) {
|
||||
out += ",";
|
||||
first = false;
|
||||
}
|
||||
int index = token.getType();
|
||||
if (index != -1) {
|
||||
out += "\"" + token.getText() + "\"" + " " + UriLexer.VOCABULARY.getDisplayName(index) + nL;
|
||||
} else {
|
||||
out += "\"" + token.getText() + "\"" + " " + index + nL;
|
||||
}
|
||||
}
|
||||
out += ']';
|
||||
System.out.println("tokens: " + out);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
* 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 org.apache.olingo.server.core.uri.testutil;
|
||||
|
||||
import org.antlr.v4.runtime.ANTLRInputStream;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.apache.olingo.server.core.uri.antlr.UriLexer;
|
||||
|
||||
public class UriLexerWithTrace extends UriLexer {
|
||||
int logLevel = 0;
|
||||
|
||||
public UriLexerWithTrace(final ANTLRInputStream antlrInputStream, final int logLevel) {
|
||||
super(antlrInputStream);
|
||||
this.logLevel = logLevel;
|
||||
}
|
||||
|
||||
public UriLexerWithTrace(final ANTLRInputStream antlrInputStream, final int logLevel, final int mode) {
|
||||
super(antlrInputStream);
|
||||
super.mode(mode);
|
||||
this.logLevel = logLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emit(final Token token) {
|
||||
if (logLevel > 1) {
|
||||
String out = String.format("%1$-" + 20 + "s", token.getText());
|
||||
|
||||
int tokenType = token.getType();
|
||||
if (tokenType == -1) {
|
||||
out += "-1/EOF";
|
||||
} else {
|
||||
out += UriLexer.VOCABULARY.getDisplayName(tokenType);
|
||||
}
|
||||
System.out.println("Lexer.emit(...):" + out);
|
||||
}
|
||||
|
||||
super.emit(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushMode(final int m) {
|
||||
|
||||
String out = UriLexer.modeNames[_mode] + "-->";
|
||||
|
||||
super.pushMode(m);
|
||||
|
||||
out += UriLexer.modeNames[_mode];
|
||||
|
||||
if (logLevel > 1) {
|
||||
System.out.println(out + " ");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int popMode() {
|
||||
|
||||
String out = UriLexer.modeNames[_mode] + "-->";
|
||||
|
||||
int m = super.popMode();
|
||||
|
||||
out += UriLexer.modeNames[_mode];
|
||||
|
||||
if (logLevel > 1) {
|
||||
System.out.println(out + " ");
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue