[OLINGO-935]URI-parser support of the data aggregation extension
Signed-off-by: Christian Amend <christian.amend@sap.com>
This commit is contained in:
parent
824c174d75
commit
6553e95080
|
@ -20,6 +20,7 @@ package org.apache.olingo.server.api.uri;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.CountOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.CustomQueryOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
|
||||
|
@ -99,6 +100,11 @@ public interface UriInfoResource {
|
|||
*/
|
||||
TopOption getTopOption();
|
||||
|
||||
/**
|
||||
* @return information about the $apply option
|
||||
*/
|
||||
ApplyOption getApplyOption();
|
||||
|
||||
/**
|
||||
* The path segments behind the service root define which resources are
|
||||
* requested by that URI. This may be entities/functions/actions and more.
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.api.uri.queryoption;
|
||||
|
||||
/**
|
||||
* Represents a single transformation from the system query option $apply.
|
||||
*/
|
||||
public interface ApplyItem {
|
||||
|
||||
/** The kind of the transformation. */
|
||||
public enum Kind {
|
||||
AGGREGATE,
|
||||
BOTTOM_TOP,
|
||||
COMPUTE,
|
||||
CONCAT,
|
||||
CUSTOM_FUNCTION,
|
||||
EXPAND,
|
||||
FILTER,
|
||||
GROUP_BY,
|
||||
IDENTITY,
|
||||
SEARCH
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the kind of the transformation.
|
||||
* @return transformation kind
|
||||
*/
|
||||
Kind getKind();
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.api.uri.queryoption;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents the system query option $apply, defined in the data aggregation extension.
|
||||
*/
|
||||
public interface ApplyOption extends SystemQueryOption {
|
||||
|
||||
/**
|
||||
* @return a list of transformations
|
||||
*/
|
||||
List<ApplyItem> getApplyItems();
|
||||
}
|
|
@ -82,7 +82,12 @@ public enum SystemQueryOptionKind {
|
|||
/**
|
||||
* @see LevelsExpandOption
|
||||
*/
|
||||
LEVELS("$levels");
|
||||
LEVELS("$levels"),
|
||||
|
||||
/**
|
||||
* @see ApplyOption
|
||||
*/
|
||||
APPLY("$apply");
|
||||
|
||||
private final String syntax;
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.api.uri.queryoption.apply;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyItem;
|
||||
|
||||
/**
|
||||
* Represents the aggregate transformation.
|
||||
*/
|
||||
public interface Aggregate extends ApplyItem {
|
||||
|
||||
/**
|
||||
* Gets the aggregate expressions.
|
||||
* @return a non-empty list of aggregate expressions (and never <code>null</code>)
|
||||
*/
|
||||
List<AggregateExpression> getExpressions();
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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.api.uri.queryoption.apply;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.FullQualifiedName;
|
||||
import org.apache.olingo.server.api.uri.UriResource;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
|
||||
|
||||
/**
|
||||
* Represents an aggregate expression.
|
||||
* @see Aggregate
|
||||
*/
|
||||
public interface AggregateExpression {
|
||||
|
||||
/** Standard aggregation method. */
|
||||
public enum StandardMethod { SUM, MIN, MAX, AVERAGE, COUNT_DISTINCT }
|
||||
|
||||
/**
|
||||
* Gets the path prefix and the path segment.
|
||||
* @return a (potentially empty) list of path segments (and never <code>null</code>)
|
||||
*/
|
||||
List<UriResource> getPath();
|
||||
|
||||
/**
|
||||
* Gets the common expression to be aggregated.
|
||||
* @return an {@link Expression} that could be <code>null</code>
|
||||
*/
|
||||
Expression getExpression();
|
||||
|
||||
/**
|
||||
* Gets the standard aggregation method if used.
|
||||
* @return a {@link StandardMethod} or <code>null</code>
|
||||
* @see #getCustomMethod()
|
||||
*/
|
||||
StandardMethod getStandardMethod();
|
||||
|
||||
/**
|
||||
* Gets the name of the custom aggregation method if used.
|
||||
* @return a {@link FullQualifiedName} or <code>null</code>
|
||||
* @see #getStandardMethod()
|
||||
*/
|
||||
FullQualifiedName getCustomMethod();
|
||||
|
||||
/**
|
||||
* Gets the name of the aggregate if an alias name has been set.
|
||||
* @return an identifier String or <code>null</code>
|
||||
*/
|
||||
String getAlias();
|
||||
|
||||
/**
|
||||
* Gets the inline aggregation expression to be applied to the target of the path if used.
|
||||
* @return an aggregation expression or <code>null</code>
|
||||
* @see #getPath()
|
||||
*/
|
||||
AggregateExpression getInlineAggregateExpression();
|
||||
|
||||
/**
|
||||
* Gets the aggregate expressions for <code>from</code>.
|
||||
* @return a (potentially empty) list of aggregate expressions (but never <code>null</code>)
|
||||
*/
|
||||
List<AggregateExpression> getFrom();
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.api.uri.queryoption.apply;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
|
||||
|
||||
/**
|
||||
* Represents a transformation with one of the pre-defined methods
|
||||
* <code>bottomcount</code>, <code>bottompercent</code>, <code>bottomsum</code>,
|
||||
* <code>topcount</code>, <code>toppercent</code>, <code>topsum</code>.
|
||||
*/
|
||||
public interface BottomTop extends ApplyItem {
|
||||
|
||||
/** Pre-defined method for partial aggregration. */
|
||||
public enum Method { BOTTOM_COUNT, BOTTOM_PERCENT, BOTTOM_SUM, TOP_COUNT, TOP_PERCENT, TOP_SUM }
|
||||
|
||||
/**
|
||||
* Gets the partial-aggregation method.
|
||||
* @return a {@link Method} (but never <code>null</code>)
|
||||
*/
|
||||
Method getMethod();
|
||||
|
||||
/**
|
||||
* Gets the expression that determines the number of items to aggregate.
|
||||
* @return an {@link Expression} (but never <code>null</code>)
|
||||
*/
|
||||
Expression getNumber();
|
||||
|
||||
/**
|
||||
* Gets the expression that determines the values to aggregate.
|
||||
* @return an {@link Expression} (but never <code>null</code>)
|
||||
*/
|
||||
Expression getValue();
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.api.uri.queryoption.apply;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyItem;
|
||||
|
||||
/**
|
||||
* Represents the compute transformation.
|
||||
*/
|
||||
public interface Compute extends ApplyItem {
|
||||
|
||||
/**
|
||||
* Gets the compute expressions.
|
||||
* @return a non-empty list of compute expressions (and never <code>null</code>)
|
||||
*/
|
||||
List<ComputeExpression> getExpressions();
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.api.uri.queryoption.apply;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
|
||||
|
||||
/**
|
||||
* Represents a compute expression.
|
||||
* @see Compute
|
||||
*/
|
||||
public interface ComputeExpression {
|
||||
|
||||
/**
|
||||
* Gets the expression to compute.
|
||||
* @return an {@link Expression} (but never <code>null</code>)
|
||||
*/
|
||||
Expression getExpression();
|
||||
|
||||
/**
|
||||
* Gets the name of the computation result if an alias name has been set.
|
||||
* @return an identifier String (but never <code>null</code>)
|
||||
*/
|
||||
String getAlias();
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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.api.uri.queryoption.apply;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
|
||||
|
||||
/**
|
||||
* Represents the concat transformation.
|
||||
*/
|
||||
public interface Concat extends ApplyItem {
|
||||
|
||||
/**
|
||||
* Gets the concatenated apply options.
|
||||
* @return a non-empty list of apply options (and never <code>null</code>)
|
||||
*/
|
||||
List<ApplyOption> getApplyOptions();
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.api.uri.queryoption.apply;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.EdmFunction;
|
||||
import org.apache.olingo.server.api.uri.UriParameter;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyItem;
|
||||
|
||||
/**
|
||||
* Represents a transformation with a custom function.
|
||||
*/
|
||||
public interface CustomFunction extends ApplyItem {
|
||||
|
||||
/**
|
||||
* Gets the function to use.
|
||||
* @return an {@link EdmFunction} (but never <code>null</code>)
|
||||
*/
|
||||
EdmFunction getFunction();
|
||||
|
||||
/**
|
||||
* Gets the function parameters.
|
||||
* @return a (potentially empty) list of parameters (but never <code>null</code>)
|
||||
*/
|
||||
List<UriParameter> getParameters();
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.api.uri.queryoption.apply;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
|
||||
|
||||
/**
|
||||
* Represents the expand transformation.
|
||||
*/
|
||||
public interface Expand extends ApplyItem {
|
||||
|
||||
/**
|
||||
* Gets the expand option.
|
||||
* @return an {@link ExpandOption} (but never <code>null</code>)
|
||||
*/
|
||||
ExpandOption getExpandOption();
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.api.uri.queryoption.apply;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.FilterOption;
|
||||
|
||||
/**
|
||||
* Represents the filter transformation.
|
||||
*/
|
||||
public interface Filter extends ApplyItem {
|
||||
|
||||
/**
|
||||
* Gets the filter option.
|
||||
* @return a {@link FilterOption} (but never <code>null</code>)
|
||||
*/
|
||||
FilterOption getFilterOption();
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.api.uri.queryoption.apply;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
|
||||
|
||||
/**
|
||||
* Represents the grouping transformation.
|
||||
*/
|
||||
public interface GroupBy extends ApplyItem {
|
||||
|
||||
/**
|
||||
* Gets the items to group.
|
||||
* @return a non-empty list of {@link GroupByItem}s (but never <code>null</code>)
|
||||
*/
|
||||
List<GroupByItem> getGroupByItems();
|
||||
|
||||
/**
|
||||
* Gets the apply option to be applied to the grouped items.
|
||||
* @return an {@link ApplyOption} (but never <code>null</code>)
|
||||
*/
|
||||
ApplyOption getApplyOption();
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.api.uri.queryoption.apply;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.uri.UriResource;
|
||||
|
||||
/**
|
||||
* Represents a grouping property.
|
||||
* @see GroupBy
|
||||
*/
|
||||
public interface GroupByItem {
|
||||
|
||||
/**
|
||||
* Gets the path.
|
||||
* @return a (potentially empty) list of path segments (and never <code>null</code>)
|
||||
*/
|
||||
List<UriResource> getPath();
|
||||
|
||||
/**
|
||||
* Whether a nested rollup clause contains the special value '$all'.
|
||||
* @return <code>true</code> if '$all' has been given in rollup, <code>false</code> otherwise
|
||||
*/
|
||||
boolean isRollupAll();
|
||||
|
||||
/**
|
||||
* Gets the rollup.
|
||||
* @return a (potentially empty) list of grouping items (and never <code>null</code>)
|
||||
*/
|
||||
List<GroupByItem> getRollup();
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.api.uri.queryoption.apply;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyItem;
|
||||
|
||||
/**
|
||||
* Represents the identity transformation.
|
||||
*/
|
||||
public interface Identity extends ApplyItem {
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.api.uri.queryoption.apply;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.SearchOption;
|
||||
|
||||
/**
|
||||
* Represents the search transformation.
|
||||
*/
|
||||
public interface Search extends ApplyItem {
|
||||
|
||||
/**
|
||||
* Gets the search option.
|
||||
* @return a {@link SearchOption} (but never <code>null</code>)
|
||||
*/
|
||||
SearchOption getSearchOption();
|
||||
}
|
|
@ -34,6 +34,8 @@ import org.apache.olingo.server.api.uri.UriResourceEntitySet;
|
|||
import org.apache.olingo.server.api.uri.UriResourceFunction;
|
||||
import org.apache.olingo.server.api.uri.UriResourceNavigation;
|
||||
import org.apache.olingo.server.api.uri.UriResourcePartTyped;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.CountOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ExpandItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
|
||||
|
@ -46,6 +48,18 @@ import org.apache.olingo.server.api.uri.queryoption.SelectItem;
|
|||
import org.apache.olingo.server.api.uri.queryoption.SelectOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.SkipOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.TopOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Aggregate;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.AggregateExpression;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.BottomTop;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Compute;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.ComputeExpression;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Concat;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.CustomFunction;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Expand;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Filter;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.GroupBy;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.GroupByItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Search;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
|
||||
import org.apache.olingo.server.api.uri.queryoption.search.SearchExpression;
|
||||
|
||||
|
@ -94,7 +108,7 @@ public class DebugTabUri implements DebugTab {
|
|||
|
||||
appendCommonJsonObjects(gen, uriInfo.getCountOption(), uriInfo.getSkipOption(), uriInfo.getTopOption(),
|
||||
uriInfo.getFilterOption(), uriInfo.getOrderByOption(), uriInfo.getSelectOption(), uriInfo.getExpandOption(),
|
||||
uriInfo.getSearchOption());
|
||||
uriInfo.getSearchOption(), uriInfo.getApplyOption());
|
||||
|
||||
if (!uriInfo.getAliases().isEmpty()) {
|
||||
gen.writeFieldName("aliases");
|
||||
|
@ -109,12 +123,12 @@ public class DebugTabUri implements DebugTab {
|
|||
gen.writeEndObject();
|
||||
}
|
||||
|
||||
private void appendCommonJsonObjects(final JsonGenerator gen, final CountOption countOption,
|
||||
final SkipOption skipOption,
|
||||
final TopOption topOption, final FilterOption filterOption, final OrderByOption orderByOption,
|
||||
final SelectOption selectOption,
|
||||
final ExpandOption expandOption, final SearchOption searchOption)
|
||||
throws IOException {
|
||||
private void appendCommonJsonObjects(JsonGenerator gen,
|
||||
final CountOption countOption, final SkipOption skipOption, final TopOption topOption,
|
||||
final FilterOption filterOption, final OrderByOption orderByOption,
|
||||
final SelectOption selectOption, final ExpandOption expandOption, final SearchOption searchOption,
|
||||
final ApplyOption applyOption)
|
||||
throws IOException {
|
||||
if (countOption != null) {
|
||||
gen.writeBooleanField("isCount", countOption.getValue());
|
||||
}
|
||||
|
@ -155,6 +169,11 @@ public class DebugTabUri implements DebugTab {
|
|||
gen.writeFieldName("search");
|
||||
appendSearchJson(gen, searchOption.getSearchExpression());
|
||||
}
|
||||
|
||||
if (applyOption != null) {
|
||||
gen.writeFieldName("apply");
|
||||
appendApplyItemsJson(gen, applyOption.getApplyItems());
|
||||
}
|
||||
}
|
||||
|
||||
private void appendURIResourceParts(final JsonGenerator gen, final List<UriResource> uriResourceParts)
|
||||
|
@ -223,14 +242,7 @@ public class DebugTabUri implements DebugTab {
|
|||
gen.writeBooleanField("star", item.isStar());
|
||||
} else if (item.getResourcePath() != null && !item.getResourcePath().getUriResourceParts().isEmpty()) {
|
||||
gen.writeFieldName("expandPath");
|
||||
gen.writeStartArray();
|
||||
for (UriResource resource : item.getResourcePath().getUriResourceParts()) {
|
||||
gen.writeStartObject();
|
||||
gen.writeStringField("propertyKind", resource.getKind().toString());
|
||||
gen.writeStringField("propertyName", resource.toString());
|
||||
gen.writeEndObject();
|
||||
}
|
||||
gen.writeEndArray();
|
||||
appendURIResourceParts(gen, item.getResourcePath().getUriResourceParts());
|
||||
}
|
||||
|
||||
if (item.isRef()) {
|
||||
|
@ -240,7 +252,7 @@ public class DebugTabUri implements DebugTab {
|
|||
if (item.getLevelsOption() != null) {
|
||||
gen.writeFieldName("levels");
|
||||
if (item.getLevelsOption().isMax()) {
|
||||
gen.writeString("max");
|
||||
gen.writeString("max");
|
||||
} else {
|
||||
gen.writeNumber(item.getLevelsOption().getValue());
|
||||
}
|
||||
|
@ -248,7 +260,7 @@ public class DebugTabUri implements DebugTab {
|
|||
|
||||
appendCommonJsonObjects(gen, item.getCountOption(), item.getSkipOption(), item.getTopOption(),
|
||||
item.getFilterOption(), item.getOrderByOption(), item.getSelectOption(), item.getExpandOption(),
|
||||
item.getSearchOption());
|
||||
item.getSearchOption(), null); // TODO: item.getApplyOption()
|
||||
|
||||
gen.writeEndObject();
|
||||
}
|
||||
|
@ -315,6 +327,142 @@ public class DebugTabUri implements DebugTab {
|
|||
json.writeEndObject();
|
||||
}
|
||||
|
||||
private void appendApplyItemsJson(JsonGenerator json, final List<ApplyItem> applyItems) throws IOException {
|
||||
json.writeStartArray();
|
||||
for (final ApplyItem item : applyItems) {
|
||||
appendApplyItemJson(json, item);
|
||||
}
|
||||
json.writeEndArray();
|
||||
}
|
||||
|
||||
private void appendApplyItemJson(JsonGenerator json, final ApplyItem item) throws IOException {
|
||||
json.writeStartObject();
|
||||
|
||||
json.writeStringField("kind", item.getKind().name());
|
||||
switch (item.getKind()) {
|
||||
case AGGREGATE:
|
||||
appendAggregateJson(json, (Aggregate) item);
|
||||
break;
|
||||
case BOTTOM_TOP:
|
||||
json.writeStringField("method", ((BottomTop) item).getMethod().name());
|
||||
json.writeFieldName("number");
|
||||
appendExpressionJson(json, ((BottomTop) item).getNumber());
|
||||
json.writeFieldName("value");
|
||||
appendExpressionJson(json, ((BottomTop) item).getValue());
|
||||
break;
|
||||
case COMPUTE:
|
||||
json.writeFieldName("compute");
|
||||
json.writeStartArray();
|
||||
for (final ComputeExpression computeExpression : ((Compute) item).getExpressions()) {
|
||||
json.writeStartObject();
|
||||
json.writeFieldName("expression");
|
||||
appendExpressionJson(json, computeExpression.getExpression());
|
||||
json.writeStringField("as", computeExpression.getAlias());
|
||||
json.writeEndObject();
|
||||
}
|
||||
json.writeEndArray();
|
||||
break;
|
||||
case CONCAT:
|
||||
json.writeFieldName("concat");
|
||||
json.writeStartArray();
|
||||
for (final ApplyOption option : ((Concat) item).getApplyOptions()) {
|
||||
appendApplyItemsJson(json, option.getApplyItems());
|
||||
}
|
||||
json.writeEndArray();
|
||||
break;
|
||||
case CUSTOM_FUNCTION:
|
||||
json.writeStringField("name",
|
||||
((CustomFunction) item).getFunction().getFullQualifiedName().getFullQualifiedNameAsString());
|
||||
appendParameters(json, "parameters", ((CustomFunction) item).getParameters());
|
||||
break;
|
||||
case EXPAND:
|
||||
appendCommonJsonObjects(json, null, null, null, null, null, null, ((Expand) item).getExpandOption(), null, null);
|
||||
break;
|
||||
case FILTER:
|
||||
appendCommonJsonObjects(json, null, null, null, ((Filter) item).getFilterOption(), null, null, null, null, null);
|
||||
break;
|
||||
case GROUP_BY:
|
||||
json.writeFieldName("groupBy");
|
||||
appendGroupByItemsJson(json, ((GroupBy) item).getGroupByItems());
|
||||
appendCommonJsonObjects(json, null, null, null, null, null, null, null, null, ((GroupBy) item).getApplyOption());
|
||||
break;
|
||||
case IDENTITY:
|
||||
break;
|
||||
case SEARCH:
|
||||
appendCommonJsonObjects(json, null, null, null, null, null, null, null, ((Search) item).getSearchOption(), null);
|
||||
break;
|
||||
}
|
||||
|
||||
json.writeEndObject();
|
||||
}
|
||||
|
||||
private void appendGroupByItemsJson(JsonGenerator json, final List<GroupByItem> groupByItems) throws IOException {
|
||||
json.writeStartArray();
|
||||
for (final GroupByItem groupByItem : groupByItems) {
|
||||
json.writeStartObject();
|
||||
if (!groupByItem.getPath().isEmpty()) {
|
||||
json.writeFieldName("path");
|
||||
appendURIResourceParts(json, groupByItem.getPath());
|
||||
}
|
||||
json.writeBooleanField("isRollupAll", groupByItem.isRollupAll());
|
||||
if (!groupByItem.getRollup().isEmpty()) {
|
||||
json.writeFieldName("rollup");
|
||||
appendGroupByItemsJson(json, groupByItem.getRollup());
|
||||
}
|
||||
json.writeEndObject();
|
||||
}
|
||||
json.writeEndArray();
|
||||
}
|
||||
|
||||
private void appendAggregateJson(JsonGenerator json, final Aggregate aggregate) throws IOException {
|
||||
json.writeFieldName("aggregate");
|
||||
appendAggregateExpressionsJson(json, aggregate.getExpressions());
|
||||
}
|
||||
|
||||
private void appendAggregateExpressionsJson(JsonGenerator json, final List<AggregateExpression> aggregateExpressions)
|
||||
throws IOException {
|
||||
json.writeStartArray();
|
||||
for (final AggregateExpression aggregateExpression : aggregateExpressions) {
|
||||
appendAggregateExpressionJson(json, aggregateExpression);
|
||||
}
|
||||
json.writeEndArray();
|
||||
}
|
||||
|
||||
private void appendAggregateExpressionJson(JsonGenerator json, final AggregateExpression aggregateExpression)
|
||||
throws IOException {
|
||||
if (aggregateExpression == null) {
|
||||
json.writeNull();
|
||||
} else {
|
||||
json.writeStartObject();
|
||||
if (!aggregateExpression.getPath().isEmpty()) {
|
||||
json.writeFieldName("path");
|
||||
appendURIResourceParts(json, aggregateExpression.getPath());
|
||||
}
|
||||
if (aggregateExpression.getExpression() != null) {
|
||||
json.writeFieldName("expression");
|
||||
appendExpressionJson(json, aggregateExpression.getExpression());
|
||||
}
|
||||
if (aggregateExpression.getStandardMethod() != null) {
|
||||
json.writeStringField("standardMethod", aggregateExpression.getStandardMethod().name());
|
||||
}
|
||||
if (aggregateExpression.getCustomMethod() != null) {
|
||||
json.writeStringField("customMethod", aggregateExpression.getCustomMethod().getFullQualifiedNameAsString());
|
||||
}
|
||||
if (aggregateExpression.getAlias() != null) {
|
||||
json.writeStringField("as", aggregateExpression.getAlias());
|
||||
}
|
||||
if (aggregateExpression.getInlineAggregateExpression() != null) {
|
||||
json.writeFieldName("inlineAggregateExpression");
|
||||
appendAggregateExpressionJson(json, aggregateExpression.getInlineAggregateExpression());
|
||||
}
|
||||
if (!aggregateExpression.getFrom().isEmpty()) {
|
||||
json.writeFieldName("from");
|
||||
appendAggregateExpressionsJson(json, aggregateExpression.getFrom());
|
||||
}
|
||||
json.writeEndObject();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHtml(final Writer writer) throws IOException {
|
||||
// factory for JSON generators (the object mapper is necessary to write expression trees)
|
||||
|
@ -377,6 +525,15 @@ public class DebugTabUri implements DebugTab {
|
|||
writer.append("</ul>\n");
|
||||
}
|
||||
|
||||
if (uriInfo.getApplyOption() != null) {
|
||||
writer.append("<h2>Apply Option</h2>\n")
|
||||
.append("<ul>\n<li class=\"json\">");
|
||||
json = jsonFactory.createGenerator(writer).useDefaultPrettyPrinter();
|
||||
appendApplyItemsJson(json, uriInfo.getApplyOption().getApplyItems());
|
||||
json.close();
|
||||
writer.append("\n</li>\n</ul>\n");
|
||||
}
|
||||
|
||||
if (uriInfo.getCountOption() != null
|
||||
|| uriInfo.getSkipOption() != null
|
||||
|| uriInfo.getSkipTokenOption() != null
|
||||
|
|
|
@ -129,31 +129,28 @@ public class ExpressionJsonVisitor implements ExpressionVisitor<JsonNode> {
|
|||
public JsonNode visitMember(final Member member)
|
||||
throws ExpressionVisitException, ODataApplicationException {
|
||||
final List<UriResource> uriResourceParts = member.getResourcePath().getUriResourceParts();
|
||||
final UriResource lastSegment = uriResourceParts.get(uriResourceParts.size() - 1);
|
||||
ObjectNode result = nodeFactory.objectNode()
|
||||
.put(NODE_TYPE_NAME, MEMBER_NAME)
|
||||
.put(TYPE_NAME, getType(uriResourceParts));
|
||||
.put(TYPE_NAME, getType(lastSegment));
|
||||
ArrayNode segments = result.putArray(RESOURCE_SEGMENTS_NAME);
|
||||
if (uriResourceParts != null) {
|
||||
for (final UriResource segment : uriResourceParts) {
|
||||
if (segment instanceof UriResourceLambdaAll) {
|
||||
final UriResourceLambdaAll all = (UriResourceLambdaAll) segment;
|
||||
segments.add(visitLambdaExpression(ALL_NAME, all.getLambdaVariable(), all.getExpression()));
|
||||
} else if (segment instanceof UriResourceLambdaAny) {
|
||||
final UriResourceLambdaAny any = (UriResourceLambdaAny) segment;
|
||||
segments.add(visitLambdaExpression(ANY_NAME, any.getLambdaVariable(), any.getExpression()));
|
||||
} else if (segment instanceof UriResourcePartTyped) {
|
||||
final String typeName = ((UriResourcePartTyped) segment).getType()
|
||||
.getFullQualifiedName().getFullQualifiedNameAsString();
|
||||
segments.add(nodeFactory.objectNode()
|
||||
.put(NODE_TYPE_NAME, segment.getKind().toString())
|
||||
.put(NAME_NAME, segment.toString())
|
||||
.put(TYPE_NAME, typeName));
|
||||
} else {
|
||||
segments.add(nodeFactory.objectNode()
|
||||
.put(NODE_TYPE_NAME, segment.getKind().toString())
|
||||
.put(NAME_NAME, segment.toString())
|
||||
.putNull(TYPE_NAME));
|
||||
}
|
||||
for (final UriResource segment : uriResourceParts) {
|
||||
if (segment instanceof UriResourceLambdaAll) {
|
||||
final UriResourceLambdaAll all = (UriResourceLambdaAll) segment;
|
||||
segments.add(visitLambdaExpression(ALL_NAME, all.getLambdaVariable(), all.getExpression()));
|
||||
} else if (segment instanceof UriResourceLambdaAny) {
|
||||
final UriResourceLambdaAny any = (UriResourceLambdaAny) segment;
|
||||
segments.add(visitLambdaExpression(ANY_NAME, any.getLambdaVariable(), any.getExpression()));
|
||||
} else if (segment instanceof UriResourcePartTyped) {
|
||||
segments.add(nodeFactory.objectNode()
|
||||
.put(NODE_TYPE_NAME, segment.getKind().toString())
|
||||
.put(NAME_NAME, segment.toString())
|
||||
.put(TYPE_NAME, getType(segment)));
|
||||
} else {
|
||||
segments.add(nodeFactory.objectNode()
|
||||
.put(NODE_TYPE_NAME, segment.getKind().toString())
|
||||
.put(NAME_NAME, segment.toString())
|
||||
.putNull(TYPE_NAME));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -287,15 +284,8 @@ public class ExpressionJsonVisitor implements ExpressionVisitor<JsonNode> {
|
|||
return type == null ? null : type.getFullQualifiedName().getFullQualifiedNameAsString();
|
||||
}
|
||||
|
||||
private String getType(final List<UriResource> uriResourceParts) {
|
||||
if (uriResourceParts == null || uriResourceParts.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final UriResource lastSegment = uriResourceParts.get(uriResourceParts.size() - 1);
|
||||
final EdmType type = lastSegment instanceof UriResourcePartTyped ?
|
||||
((UriResourcePartTyped) lastSegment).getType() :
|
||||
null;
|
||||
return type == null ? UNKNOWN_NAME : type.getFullQualifiedName().getFullQualifiedNameAsString();
|
||||
private String getType(final UriResource segment) {
|
||||
final EdmType type = segment instanceof UriResourcePartTyped ? ((UriResourcePartTyped) segment).getType() : null;
|
||||
return type == null ? UNKNOWN_NAME : type.getFullQualifiedName().getFullQualifiedNameAsString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.apache.olingo.server.api.uri.UriInfoResource;
|
|||
import org.apache.olingo.server.api.uri.UriInfoService;
|
||||
import org.apache.olingo.server.api.uri.UriResource;
|
||||
import org.apache.olingo.server.api.uri.queryoption.AliasQueryOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.CountOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.CustomQueryOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
|
||||
|
@ -144,11 +145,6 @@ public class UriInfoImpl implements UriInfo {
|
|||
return this;
|
||||
}
|
||||
|
||||
public UriInfoImpl removeResourcePart(final int index) {
|
||||
pathParts.remove(index);
|
||||
return this;
|
||||
}
|
||||
|
||||
public UriResource getLastResourcePart() {
|
||||
return lastResourcePart;
|
||||
}
|
||||
|
@ -195,6 +191,7 @@ public class UriInfoImpl implements UriInfo {
|
|||
case SKIPTOKEN:
|
||||
case TOP:
|
||||
case LEVELS:
|
||||
case APPLY:
|
||||
systemQueryOptions.put(systemQueryOptionKind, systemOption);
|
||||
break;
|
||||
default:
|
||||
|
@ -258,6 +255,11 @@ public class UriInfoImpl implements UriInfo {
|
|||
return (TopOption) systemQueryOptions.get(SystemQueryOptionKind.TOP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplyOption getApplyOption() {
|
||||
return (ApplyOption) systemQueryOptions.get(SystemQueryOptionKind.APPLY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SystemQueryOption> getSystemQueryOptions() {
|
||||
return Collections.unmodifiableList(new ArrayList<SystemQueryOption>(systemQueryOptions.values()));
|
||||
|
|
|
@ -0,0 +1,570 @@
|
|||
/*
|
||||
* 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.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.Edm;
|
||||
import org.apache.olingo.commons.api.edm.EdmElement;
|
||||
import org.apache.olingo.commons.api.edm.EdmFunction;
|
||||
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
|
||||
import org.apache.olingo.commons.api.edm.EdmParameter;
|
||||
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
|
||||
import org.apache.olingo.commons.api.edm.EdmProperty;
|
||||
import org.apache.olingo.commons.api.edm.EdmReturnType;
|
||||
import org.apache.olingo.commons.api.edm.EdmStructuredType;
|
||||
import org.apache.olingo.commons.api.edm.EdmType;
|
||||
import org.apache.olingo.commons.api.edm.FullQualifiedName;
|
||||
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
|
||||
import org.apache.olingo.server.api.OData;
|
||||
import org.apache.olingo.server.api.uri.UriInfo;
|
||||
import org.apache.olingo.server.api.uri.UriParameter;
|
||||
import org.apache.olingo.server.api.uri.UriResource;
|
||||
import org.apache.olingo.server.api.uri.UriResourceKind;
|
||||
import org.apache.olingo.server.api.uri.UriResourcePartTyped;
|
||||
import org.apache.olingo.server.api.uri.queryoption.AliasQueryOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.FilterOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.SearchOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Aggregate;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.AggregateExpression;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.AggregateExpression.StandardMethod;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.BottomTop;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Compute;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Concat;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.CustomFunction;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.GroupBy;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.GroupByItem;
|
||||
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.UriResourceComplexPropertyImpl;
|
||||
import org.apache.olingo.server.core.uri.UriResourceCountImpl;
|
||||
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.ApplyOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.ExpandItemImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.apply.AggregateExpressionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.apply.AggregateImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.apply.BottomTopImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.apply.ComputeExpressionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.apply.ComputeImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.apply.ConcatImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.apply.CustomFunctionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.apply.DynamicProperty;
|
||||
import org.apache.olingo.server.core.uri.queryoption.apply.DynamicStructuredType;
|
||||
import org.apache.olingo.server.core.uri.queryoption.apply.ExpandImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.apply.FilterImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.apply.GroupByImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.apply.GroupByItemImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.apply.IdentityImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.apply.SearchImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.expression.MemberImpl;
|
||||
import org.apache.olingo.server.core.uri.validator.UriValidationException;
|
||||
|
||||
public class ApplyParser {
|
||||
|
||||
private static final Map<TokenKind, StandardMethod> TOKEN_KIND_TO_STANDARD_METHOD;
|
||||
static {
|
||||
Map<TokenKind, StandardMethod> temp = new EnumMap<TokenKind, StandardMethod>(TokenKind.class);
|
||||
temp.put(TokenKind.SUM, StandardMethod.SUM);
|
||||
temp.put(TokenKind.MIN, StandardMethod.MIN);
|
||||
temp.put(TokenKind.MAX, StandardMethod.MAX);
|
||||
temp.put(TokenKind.AVERAGE, StandardMethod.AVERAGE);
|
||||
temp.put(TokenKind.COUNTDISTINCT, StandardMethod.COUNT_DISTINCT);
|
||||
TOKEN_KIND_TO_STANDARD_METHOD = Collections.unmodifiableMap(temp);
|
||||
}
|
||||
|
||||
private static final Map<TokenKind, BottomTop.Method> TOKEN_KIND_TO_BOTTOM_TOP_METHOD;
|
||||
static {
|
||||
Map<TokenKind, BottomTop.Method> temp = new EnumMap<TokenKind, BottomTop.Method>(TokenKind.class);
|
||||
temp.put(TokenKind.BottomCountTrafo, BottomTop.Method.BOTTOM_COUNT);
|
||||
temp.put(TokenKind.BottomPercentTrafo, BottomTop.Method.BOTTOM_PERCENT);
|
||||
temp.put(TokenKind.BottomSumTrafo, BottomTop.Method.BOTTOM_SUM);
|
||||
temp.put(TokenKind.TopCountTrafo, BottomTop.Method.TOP_COUNT);
|
||||
temp.put(TokenKind.TopPercentTrafo, BottomTop.Method.TOP_PERCENT);
|
||||
temp.put(TokenKind.TopSumTrafo, BottomTop.Method.TOP_SUM);
|
||||
TOKEN_KIND_TO_BOTTOM_TOP_METHOD = Collections.unmodifiableMap(temp);
|
||||
}
|
||||
|
||||
private final Edm edm;
|
||||
private final OData odata;
|
||||
|
||||
private UriTokenizer tokenizer;
|
||||
private Collection<String> crossjoinEntitySetNames;
|
||||
private Map<String, AliasQueryOption> aliases;
|
||||
|
||||
public ApplyParser(final Edm edm, final OData odata) {
|
||||
this.edm = edm;
|
||||
this.odata = odata;
|
||||
}
|
||||
|
||||
public ApplyOption parse(UriTokenizer tokenizer, final EdmStructuredType referencedType,
|
||||
final Collection<String> crossjoinEntitySetNames, final Map<String, AliasQueryOption> aliases)
|
||||
throws UriParserException, UriValidationException {
|
||||
this.tokenizer = tokenizer;
|
||||
this.crossjoinEntitySetNames = crossjoinEntitySetNames;
|
||||
this.aliases = aliases;
|
||||
|
||||
// TODO: Check when to create a new dynamic type and how it can be returned.
|
||||
DynamicStructuredType type = new DynamicStructuredType(referencedType);
|
||||
return parseApply(type);
|
||||
}
|
||||
|
||||
private ApplyOption parseApply(EdmStructuredType referencedType)
|
||||
throws UriParserException, UriValidationException {
|
||||
ApplyOptionImpl option = new ApplyOptionImpl();
|
||||
do {
|
||||
option.add(parseTrafo(referencedType));
|
||||
} while (tokenizer.next(TokenKind.SLASH));
|
||||
return option;
|
||||
}
|
||||
|
||||
private ApplyItem parseTrafo(EdmStructuredType referencedType) throws UriParserException, UriValidationException {
|
||||
if (tokenizer.next(TokenKind.AggregateTrafo)) {
|
||||
return parseAggregateTrafo(referencedType);
|
||||
|
||||
} else if (tokenizer.next(TokenKind.IDENTITY)) {
|
||||
return new IdentityImpl();
|
||||
|
||||
} else if (tokenizer.next(TokenKind.ComputeTrafo)) {
|
||||
return parseComputeTrafo(referencedType);
|
||||
|
||||
} else if (tokenizer.next(TokenKind.ConcatMethod)) {
|
||||
return parseConcatTrafo(referencedType);
|
||||
|
||||
} else if (tokenizer.next(TokenKind.ExpandTrafo)) {
|
||||
return new ExpandImpl().setExpandOption(parseExpandTrafo(referencedType));
|
||||
|
||||
} else if (tokenizer.next(TokenKind.FilterTrafo)) {
|
||||
final FilterOption filterOption = new FilterParser(edm, odata)
|
||||
.parse(tokenizer, referencedType, crossjoinEntitySetNames, aliases);
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
return new FilterImpl().setFilterOption(filterOption);
|
||||
|
||||
} else if (tokenizer.next(TokenKind.GroupByTrafo)) {
|
||||
return parseGroupByTrafo(referencedType);
|
||||
|
||||
} else if (tokenizer.next(TokenKind.SearchTrafo)) {
|
||||
final SearchOption searchOption = new SearchParser().parse(tokenizer);
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
return new SearchImpl().setSearchOption(searchOption);
|
||||
|
||||
} else if (tokenizer.next(TokenKind.QualifiedName)) {
|
||||
return parseCustomFunction(new FullQualifiedName(tokenizer.getText()), referencedType);
|
||||
|
||||
} else {
|
||||
final TokenKind kind = ParserHelper.next(tokenizer,
|
||||
TokenKind.BottomCountTrafo, TokenKind.BottomPercentTrafo, TokenKind.BottomSumTrafo,
|
||||
TokenKind.TopCountTrafo, TokenKind.TopPercentTrafo, TokenKind.TopSumTrafo);
|
||||
if (kind == null) {
|
||||
throw new UriParserSyntaxException("Invalid apply expression syntax.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
} else {
|
||||
return parseBottomTop(kind, referencedType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Aggregate parseAggregateTrafo(EdmStructuredType referencedType)
|
||||
throws UriParserException, UriValidationException {
|
||||
AggregateImpl aggregate = new AggregateImpl();
|
||||
do {
|
||||
aggregate.addExpression(parseAggregateExpr(referencedType));
|
||||
} while (tokenizer.next(TokenKind.COMMA));
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
return aggregate;
|
||||
}
|
||||
|
||||
private AggregateExpression parseAggregateExpr(EdmStructuredType referencedType)
|
||||
throws UriParserException, UriValidationException {
|
||||
AggregateExpressionImpl aggregateExpression = new AggregateExpressionImpl();
|
||||
tokenizer.saveState();
|
||||
|
||||
// First try is checking for a (potentially empty) path prefix and the things that could follow it.
|
||||
UriInfoImpl uriInfo = new UriInfoImpl();
|
||||
final String identifierLeft = parsePathPrefix(uriInfo, referencedType);
|
||||
if (identifierLeft != null) {
|
||||
final String customAggregate = tokenizer.getText();
|
||||
// A custom aggregate (an OData identifier) is defined in the CustomAggregate
|
||||
// EDM annotation (in namespace Org.OData.Aggregation.V1) of the structured type or of the entity container.
|
||||
// Currently we don't look into annotations, so all custom aggregates are allowed and have no type.
|
||||
uriInfo.addResourcePart(new UriResourcePrimitivePropertyImpl(createDynamicProperty(customAggregate, null)));
|
||||
aggregateExpression.setPath(uriInfo);
|
||||
final String alias = parseAsAlias(referencedType, false);
|
||||
aggregateExpression.setAlias(alias);
|
||||
if (alias != null) {
|
||||
((DynamicStructuredType) referencedType).addProperty(createDynamicProperty(alias, null));
|
||||
}
|
||||
parseAggregateFrom(aggregateExpression, referencedType);
|
||||
} else if (tokenizer.next(TokenKind.OPEN)) {
|
||||
final UriResource lastResourcePart = uriInfo.getLastResourcePart();
|
||||
if (lastResourcePart == null) {
|
||||
throw new UriParserSyntaxException("Invalid 'aggregateExpr' syntax.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
aggregateExpression.setPath(uriInfo);
|
||||
DynamicStructuredType inlineType = new DynamicStructuredType((EdmStructuredType)
|
||||
ParserHelper.getTypeInformation((UriResourcePartTyped) lastResourcePart));
|
||||
aggregateExpression.setInlineAggregateExpression(parseAggregateExpr(inlineType));
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
} else if (tokenizer.next(TokenKind.COUNT)) {
|
||||
uriInfo.addResourcePart(new UriResourceCountImpl());
|
||||
aggregateExpression.setPath(uriInfo);
|
||||
final String alias = parseAsAlias(referencedType, true);
|
||||
aggregateExpression.setAlias(alias);
|
||||
((DynamicStructuredType) referencedType).addProperty(
|
||||
createDynamicProperty(alias,
|
||||
// The OData standard mandates Edm.Decimal (with no decimals), although counts are always integer.
|
||||
odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal)));
|
||||
} else {
|
||||
// No legitimate continuation of a path prefix has been found.
|
||||
|
||||
// Second try is checking for a common expression.
|
||||
tokenizer.returnToSavedState();
|
||||
final Expression expression = new ExpressionParser(edm, odata)
|
||||
.parse(tokenizer, referencedType, crossjoinEntitySetNames, aliases);
|
||||
aggregateExpression.setExpression(expression);
|
||||
parseAggregateWith(aggregateExpression);
|
||||
if (aggregateExpression.getStandardMethod() == null && aggregateExpression.getCustomMethod() == null) {
|
||||
throw new UriParserSyntaxException("Invalid 'aggregateExpr' syntax.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
final String alias = parseAsAlias(referencedType, true);
|
||||
aggregateExpression.setAlias(alias);
|
||||
((DynamicStructuredType) referencedType).addProperty(
|
||||
createDynamicProperty(alias,
|
||||
// Determine the type for standard methods; there is no way to do this for custom methods.
|
||||
getTypeForAggregateMethod(aggregateExpression.getStandardMethod(),
|
||||
ExpressionParser.getType(expression))));
|
||||
parseAggregateFrom(aggregateExpression, referencedType);
|
||||
}
|
||||
|
||||
return aggregateExpression;
|
||||
}
|
||||
|
||||
private void parseAggregateWith(AggregateExpressionImpl aggregateExpression) throws UriParserException {
|
||||
if (tokenizer.next(TokenKind.WithOperator)) {
|
||||
final TokenKind kind = ParserHelper.next(tokenizer,
|
||||
TokenKind.SUM, TokenKind.MIN, TokenKind.MAX, TokenKind.AVERAGE, TokenKind.COUNTDISTINCT,
|
||||
TokenKind.QualifiedName);
|
||||
if (kind == null) {
|
||||
throw new UriParserSyntaxException("Invalid 'with' syntax.",
|
||||
UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
} else if (kind == TokenKind.QualifiedName) {
|
||||
// A custom aggregation method is announced in the CustomAggregationMethods
|
||||
// EDM annotation (in namespace Org.OData.Aggregation.V1) of the structured type or of the entity container.
|
||||
// Currently we don't look into annotations, so all custom aggregation methods are allowed and have no type.
|
||||
aggregateExpression.setCustomMethod(new FullQualifiedName(tokenizer.getText()));
|
||||
} else {
|
||||
aggregateExpression.setStandardMethod(TOKEN_KIND_TO_STANDARD_METHOD.get(kind));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EdmType getTypeForAggregateMethod(final StandardMethod method, final EdmType type) {
|
||||
if (method == StandardMethod.SUM || method == StandardMethod.AVERAGE || method == StandardMethod.COUNT_DISTINCT) {
|
||||
return odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Decimal);
|
||||
} else if (method == StandardMethod.MIN || method == StandardMethod.MAX) {
|
||||
return type;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String parseAsAlias(final EdmStructuredType referencedType, final boolean isRequired)
|
||||
throws UriParserException {
|
||||
if (tokenizer.next(TokenKind.AsOperator)) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.ODataIdentifier);
|
||||
final String name = tokenizer.getText();
|
||||
if (referencedType.getProperty(name) != null) {
|
||||
throw new UriParserSemanticException("Alias '" + name + "' is already a property.",
|
||||
UriParserSemanticException.MessageKeys.IS_PROPERTY, name);
|
||||
}
|
||||
return name;
|
||||
} else if (isRequired) {
|
||||
throw new UriParserSyntaxException("Expected asAlias not found.", UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void parseAggregateFrom(AggregateExpressionImpl aggregateExpression,
|
||||
final EdmStructuredType referencedType) throws UriParserException {
|
||||
while (tokenizer.next(TokenKind.FromOperator)) {
|
||||
AggregateExpressionImpl from = new AggregateExpressionImpl();
|
||||
from.setExpression(new MemberImpl(parseGroupingProperty(referencedType), referencedType));
|
||||
parseAggregateWith(from);
|
||||
aggregateExpression.addFrom(from);
|
||||
}
|
||||
}
|
||||
|
||||
private EdmProperty createDynamicProperty(final String name, final EdmType type) {
|
||||
return name == null ? null : new DynamicProperty(name, type);
|
||||
}
|
||||
|
||||
private Compute parseComputeTrafo(final EdmStructuredType referencedType)
|
||||
throws UriParserException, UriValidationException {
|
||||
ComputeImpl compute = new ComputeImpl();
|
||||
// TODO: Check when to create a new dynamic type and how it can be returned.
|
||||
DynamicStructuredType type = new DynamicStructuredType(referencedType);
|
||||
do {
|
||||
final Expression expression = new ExpressionParser(edm, odata)
|
||||
.parse(tokenizer, type, crossjoinEntitySetNames, aliases);
|
||||
final EdmType expressionType = ExpressionParser.getType(expression);
|
||||
if (expressionType.getKind() != EdmTypeKind.PRIMITIVE) {
|
||||
throw new UriParserSemanticException("Compute expressions must return primitive values.",
|
||||
UriParserSemanticException.MessageKeys.ONLY_FOR_PRIMITIVE_TYPES, "compute");
|
||||
}
|
||||
final String alias = parseAsAlias(type, true);
|
||||
type.addProperty(createDynamicProperty(alias, expressionType));
|
||||
compute.addExpression(new ComputeExpressionImpl()
|
||||
.setExpression(expression)
|
||||
.setAlias(alias));
|
||||
} while (tokenizer.next(TokenKind.COMMA));
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
return compute;
|
||||
}
|
||||
|
||||
private Concat parseConcatTrafo(final EdmStructuredType referencedType)
|
||||
throws UriParserException, UriValidationException {
|
||||
ConcatImpl concat = new ConcatImpl();
|
||||
// TODO: Check when to create a new dynamic type and how it can be returned.
|
||||
concat.addApplyOption(parseApply(referencedType));
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
|
||||
do {
|
||||
concat.addApplyOption(parseApply(referencedType));
|
||||
} while (tokenizer.next(TokenKind.COMMA));
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
return concat;
|
||||
}
|
||||
|
||||
private ExpandOption parseExpandTrafo(final EdmStructuredType referencedType)
|
||||
throws UriParserException, UriValidationException {
|
||||
ExpandItemImpl item = new ExpandItemImpl();
|
||||
item.setResourcePath(ExpandParser.parseExpandPath(tokenizer, edm, referencedType, item));
|
||||
final EdmType type = ParserHelper.getTypeInformation((UriResourcePartTyped)
|
||||
((UriInfoImpl) item.getResourcePath()).getLastResourcePart());
|
||||
if (tokenizer.next(TokenKind.COMMA)) {
|
||||
if (tokenizer.next(TokenKind.FilterTrafo)) {
|
||||
item.setSystemQueryOption(
|
||||
new FilterParser(edm, odata).parse(tokenizer,type, crossjoinEntitySetNames, aliases));
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
} else {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.ExpandTrafo);
|
||||
item.setSystemQueryOption(parseExpandTrafo((EdmStructuredType) type));
|
||||
}
|
||||
}
|
||||
while (tokenizer.next(TokenKind.COMMA)) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.ExpandTrafo);
|
||||
final ExpandOption nestedExpand = parseExpandTrafo((EdmStructuredType) type);
|
||||
if (item.getExpandOption() == null) {
|
||||
item.setSystemQueryOption(nestedExpand);
|
||||
} else {
|
||||
// Add to the existing items.
|
||||
((ExpandOptionImpl) item.getExpandOption())
|
||||
.addExpandItem(nestedExpand.getExpandItems().get(0));
|
||||
}
|
||||
}
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
ExpandOptionImpl expand = new ExpandOptionImpl();
|
||||
expand.addExpandItem(item);
|
||||
return expand;
|
||||
}
|
||||
|
||||
private GroupBy parseGroupByTrafo(final EdmStructuredType referencedType)
|
||||
throws UriParserException, UriValidationException {
|
||||
GroupByImpl groupBy = new GroupByImpl();
|
||||
parseGroupByList(groupBy, referencedType);
|
||||
if (tokenizer.next(TokenKind.COMMA)) {
|
||||
groupBy.setApplyOption(parseApply(referencedType));
|
||||
}
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
return groupBy;
|
||||
}
|
||||
|
||||
private void parseGroupByList(GroupByImpl groupBy, final EdmStructuredType referencedType)
|
||||
throws UriParserException {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.OPEN);
|
||||
do {
|
||||
groupBy.addGroupByItem(parseGroupByElement(referencedType));
|
||||
} while (tokenizer.next(TokenKind.COMMA));
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
}
|
||||
|
||||
private GroupByItem parseGroupByElement(final EdmStructuredType referencedType)
|
||||
throws UriParserException {
|
||||
if (tokenizer.next(TokenKind.RollUpSpec)) {
|
||||
return parseRollUpSpec(referencedType);
|
||||
} else {
|
||||
return new GroupByItemImpl().setPath(parseGroupingProperty(referencedType));
|
||||
}
|
||||
}
|
||||
|
||||
private GroupByItem parseRollUpSpec(final EdmStructuredType referencedType)
|
||||
throws UriParserException {
|
||||
GroupByItemImpl item = new GroupByItemImpl();
|
||||
if (tokenizer.next(TokenKind.ROLLUP_ALL)) {
|
||||
item.setIsRollupAll();
|
||||
} else {
|
||||
item.addRollupItem(new GroupByItemImpl().setPath(
|
||||
parseGroupingProperty(referencedType)));
|
||||
}
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
|
||||
do {
|
||||
item.addRollupItem(new GroupByItemImpl().setPath(
|
||||
parseGroupingProperty(referencedType)));
|
||||
} while (tokenizer.next(TokenKind.COMMA));
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
return item;
|
||||
}
|
||||
|
||||
private UriInfo parseGroupingProperty(final EdmStructuredType referencedType) throws UriParserException {
|
||||
UriInfoImpl uriInfo = new UriInfoImpl();
|
||||
final String identifierLeft = parsePathPrefix(uriInfo, referencedType);
|
||||
if (identifierLeft != null) {
|
||||
throw new UriParserSemanticException("Unknown identifier in grouping property path.",
|
||||
UriParserSemanticException.MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE,
|
||||
identifierLeft,
|
||||
uriInfo.getLastResourcePart() != null && uriInfo.getLastResourcePart() instanceof UriResourcePartTyped ?
|
||||
((UriResourcePartTyped) uriInfo.getLastResourcePart())
|
||||
.getType().getFullQualifiedName().getFullQualifiedNameAsString() :
|
||||
"");
|
||||
}
|
||||
if (uriInfo.getLastResourcePart() != null
|
||||
&& uriInfo.getLastResourcePart().getKind() == UriResourceKind.navigationProperty) {
|
||||
if (tokenizer.next(TokenKind.SLASH)) {
|
||||
UriResourceNavigationPropertyImpl lastPart = (UriResourceNavigationPropertyImpl) uriInfo.getLastResourcePart();
|
||||
final EdmStructuredType typeCast = ParserHelper.parseTypeCast(tokenizer, edm,
|
||||
(EdmStructuredType) lastPart.getType());
|
||||
lastPart.setCollectionTypeFilter(typeCast);
|
||||
}
|
||||
}
|
||||
return uriInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the path prefix and a following OData identifier as one path, deviating from the ABNF.
|
||||
* @param uriInfo object to be filled with path segments
|
||||
* @return a parsed but not used OData identifier */
|
||||
private String parsePathPrefix(UriInfoImpl uriInfo, final EdmStructuredType referencedType)
|
||||
throws UriParserException {
|
||||
final EdmStructuredType typeCast = ParserHelper.parseTypeCast(tokenizer, edm, referencedType);
|
||||
if (typeCast != null) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.SLASH);
|
||||
}
|
||||
EdmStructuredType type = typeCast == null ? referencedType : typeCast;
|
||||
while (tokenizer.next(TokenKind.ODataIdentifier)) {
|
||||
final String name = tokenizer.getText();
|
||||
final EdmElement property = type.getProperty(name);
|
||||
final UriResource segment = parsePathSegment(property);
|
||||
if (segment == null) {
|
||||
if (property == null) {
|
||||
return name;
|
||||
} else {
|
||||
uriInfo.addResourcePart(
|
||||
property instanceof EdmNavigationProperty ?
|
||||
new UriResourceNavigationPropertyImpl((EdmNavigationProperty) property) :
|
||||
property.getType().getKind() == EdmTypeKind.COMPLEX ?
|
||||
new UriResourceComplexPropertyImpl((EdmProperty) property) :
|
||||
new UriResourcePrimitivePropertyImpl((EdmProperty) property));
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
uriInfo.addResourcePart(segment);
|
||||
}
|
||||
type = (EdmStructuredType) ParserHelper.getTypeInformation((UriResourcePartTyped) segment);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private UriResource parsePathSegment(final EdmElement property) throws UriParserException {
|
||||
if (property == null
|
||||
|| !(property.getType().getKind() == EdmTypeKind.COMPLEX
|
||||
|| property instanceof EdmNavigationProperty)) {
|
||||
// Could be a customAggregate or $count.
|
||||
return null;
|
||||
}
|
||||
if (tokenizer.next(TokenKind.SLASH)) {
|
||||
final EdmStructuredType typeCast = ParserHelper.parseTypeCast(tokenizer, edm,
|
||||
(EdmStructuredType) property.getType());
|
||||
if (typeCast != null) {
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.SLASH);
|
||||
}
|
||||
return property.getType().getKind() == EdmTypeKind.COMPLEX ?
|
||||
new UriResourceComplexPropertyImpl((EdmProperty) property).setTypeFilter(typeCast) :
|
||||
new UriResourceNavigationPropertyImpl((EdmNavigationProperty) property).setCollectionTypeFilter(typeCast);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private CustomFunction parseCustomFunction(final FullQualifiedName functionName,
|
||||
final EdmStructuredType referencedType) throws UriParserException, UriValidationException {
|
||||
final List<UriParameter> parameters =
|
||||
ParserHelper.parseFunctionParameters(tokenizer, edm, referencedType, true, aliases);
|
||||
final List<String> parameterNames = ParserHelper.getParameterNames(parameters);
|
||||
final EdmFunction function = edm.getBoundFunction(functionName,
|
||||
referencedType.getFullQualifiedName(), true, parameterNames);
|
||||
if (function == null) {
|
||||
throw new UriParserSemanticException("No function '" + functionName + "' found.",
|
||||
UriParserSemanticException.MessageKeys.FUNCTION_NOT_FOUND,
|
||||
functionName.getFullQualifiedNameAsString());
|
||||
}
|
||||
ParserHelper.validateFunctionParameters(function, parameters, edm, referencedType, aliases);
|
||||
|
||||
// The binding parameter and the return type must be of type complex or entity collection.
|
||||
final EdmParameter bindingParameter = function.getParameter(function.getParameterNames().get(0));
|
||||
final EdmReturnType returnType = function.getReturnType();
|
||||
if (bindingParameter.getType().getKind() != EdmTypeKind.ENTITY
|
||||
&& bindingParameter.getType().getKind() != EdmTypeKind.COMPLEX
|
||||
|| !bindingParameter.isCollection()
|
||||
|| returnType.getType().getKind() != EdmTypeKind.ENTITY
|
||||
&& returnType.getType().getKind() != EdmTypeKind.COMPLEX
|
||||
|| !returnType.isCollection()) {
|
||||
throw new UriParserSemanticException("Only entity- or complex-collection functions are allowed.",
|
||||
UriParserSemanticException.MessageKeys.FUNCTION_MUST_USE_COLLECTIONS,
|
||||
functionName.getFullQualifiedNameAsString());
|
||||
}
|
||||
|
||||
return new CustomFunctionImpl().setFunction(function).setParameters(parameters);
|
||||
}
|
||||
|
||||
private BottomTop parseBottomTop(final TokenKind kind, final EdmStructuredType referencedType)
|
||||
throws UriParserException, UriValidationException {
|
||||
BottomTopImpl bottomTop = new BottomTopImpl();
|
||||
bottomTop.setMethod(TOKEN_KIND_TO_BOTTOM_TOP_METHOD.get(kind));
|
||||
final ExpressionParser expressionParser = new ExpressionParser(edm, odata);
|
||||
final Expression number = expressionParser.parse(tokenizer, referencedType, crossjoinEntitySetNames, aliases);
|
||||
expressionParser.checkIntegerType(number);
|
||||
bottomTop.setNumber(number);
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.COMMA);
|
||||
final Expression value = expressionParser.parse(tokenizer, referencedType, crossjoinEntitySetNames, aliases);
|
||||
expressionParser.checkNumericType(value);
|
||||
bottomTop.setValue(value);
|
||||
ParserHelper.requireNext(tokenizer, TokenKind.CLOSE);
|
||||
return bottomTop;
|
||||
}
|
||||
}
|
|
@ -38,6 +38,8 @@ import org.apache.olingo.server.api.uri.UriResourcePartTyped;
|
|||
import org.apache.olingo.server.api.uri.UriResourceRef;
|
||||
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.ApplyItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ExpandItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.FilterOption;
|
||||
|
@ -53,6 +55,7 @@ import org.apache.olingo.server.core.uri.UriResourceStartingTypeFilterImpl;
|
|||
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.ApplyOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
|
||||
import org.apache.olingo.server.core.uri.queryoption.FilterOptionImpl;
|
||||
|
@ -206,6 +209,8 @@ public class Parser {
|
|||
}
|
||||
|
||||
// Post-process system query options that need context information from the resource path.
|
||||
parseApplyOption(contextUriInfo.getApplyOption(), contextType,
|
||||
contextUriInfo.getEntitySetNames(), contextUriInfo.getAliasMap());
|
||||
parseFilterOption(contextUriInfo.getFilterOption(), contextType,
|
||||
contextUriInfo.getEntitySetNames(), contextUriInfo.getAliasMap());
|
||||
parseOrderByOption(contextUriInfo.getOrderByOption(), contextType,
|
||||
|
@ -226,7 +231,7 @@ public class Parser {
|
|||
throw new UriParserSyntaxException("Unknown system query option!",
|
||||
UriParserSyntaxException.MessageKeys.UNKNOWN_SYSTEM_QUERY_OPTION, optionName);
|
||||
}
|
||||
final SystemQueryOptionImpl systemOption;
|
||||
SystemQueryOptionImpl systemOption;
|
||||
switch (kind) {
|
||||
case SEARCH:
|
||||
SearchOption searchOption = new SearchParser().parse(optionValue);
|
||||
|
@ -293,6 +298,9 @@ public class Parser {
|
|||
case LEVELS:
|
||||
throw new UriParserSyntaxException("System query option '$levels' is allowed only inside '$expand'!",
|
||||
UriParserSyntaxException.MessageKeys.SYSTEM_QUERY_OPTION_LEVELS_NOT_ALLOWED_HERE);
|
||||
case APPLY:
|
||||
systemOption = new ApplyOptionImpl();
|
||||
break;
|
||||
default:
|
||||
throw new UriParserSyntaxException("System query option '" + kind + "' is not known!",
|
||||
UriParserSyntaxException.MessageKeys.UNKNOWN_SYSTEM_QUERY_OPTION, optionName);
|
||||
|
@ -344,8 +352,8 @@ public class Parser {
|
|||
}
|
||||
|
||||
private void parseExpandOption(ExpandOption expandOption, final EdmType contextType, final boolean isAll,
|
||||
final List<String> entitySetNames, final Map<String, AliasQueryOption> aliases) throws UriParserException,
|
||||
UriValidationException {
|
||||
final List<String> entitySetNames, final Map<String, AliasQueryOption> aliases)
|
||||
throws UriParserException, UriValidationException {
|
||||
if (expandOption != null) {
|
||||
if (!(contextType instanceof EdmStructuredType || isAll
|
||||
|| (entitySetNames != null && !entitySetNames.isEmpty()))) {
|
||||
|
@ -377,6 +385,23 @@ public class Parser {
|
|||
}
|
||||
}
|
||||
|
||||
private void parseApplyOption(ApplyOption applyOption, final EdmType contextType,
|
||||
final List<String> entitySetNames, final Map<String, AliasQueryOption> aliases)
|
||||
throws UriParserException, UriValidationException {
|
||||
if (applyOption != null) {
|
||||
final String optionValue = applyOption.getText();
|
||||
UriTokenizer applyTokenizer = new UriTokenizer(optionValue);
|
||||
final ApplyOption option = new ApplyParser(edm, odata).parse(applyTokenizer,
|
||||
contextType instanceof EdmStructuredType ? (EdmStructuredType) contextType : null,
|
||||
entitySetNames,
|
||||
aliases);
|
||||
checkOptionEOF(applyTokenizer, applyOption.getName(), optionValue);
|
||||
for (final ApplyItem item : option.getApplyItems()) {
|
||||
((ApplyOptionImpl) applyOption).add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureLastSegment(final String segment, final int pos, final int size)
|
||||
throws UriParserSyntaxException {
|
||||
if (pos < size) {
|
||||
|
|
|
@ -67,9 +67,15 @@ public class UriParserSemanticException extends UriParserException {
|
|||
COMPLEX_PARAMETER_IN_RESOURCE_PATH,
|
||||
/** parameters: left type, right type */
|
||||
TYPES_NOT_COMPATIBLE,
|
||||
/** parameter: addressed resource name*/
|
||||
NOT_A_MEDIA_RESOURCE;
|
||||
|
||||
/** parameter: addressed resource name */
|
||||
NOT_A_MEDIA_RESOURCE,
|
||||
/** parameters: property name */
|
||||
IS_PROPERTY,
|
||||
/** parameter: expression */
|
||||
ONLY_FOR_PRIMITIVE_TYPES,
|
||||
/** parameter: function name */
|
||||
FUNCTION_MUST_USE_COLLECTIONS;
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return name();
|
||||
|
|
|
@ -23,7 +23,8 @@ package org.apache.olingo.server.core.uri.parser;
|
|||
* <p>As far as feasible, it tries to work on character basis, assuming this to be faster than string operations.
|
||||
* Since only the index is "moved", backing out while parsing a token is easy and used throughout.
|
||||
* There is intentionally no method to push back tokens (although it would be easy to add such a method)
|
||||
* because this tokenizer should behave like a classical token-consuming tokenizer.</p>
|
||||
* because this tokenizer should behave like a classical token-consuming tokenizer.
|
||||
* There is, however, the possibility to save the current state and return to it later.</p>
|
||||
* <p>Whitespace is not an extra token but consumed with the tokens that require whitespace.
|
||||
* Optional whitespace is not supported.</p>
|
||||
*/
|
||||
|
@ -40,6 +41,7 @@ public class UriTokenizer {
|
|||
ROOT,
|
||||
IT,
|
||||
|
||||
APPLY, // for the aggregation extension
|
||||
EXPAND,
|
||||
FILTER,
|
||||
LEVELS,
|
||||
|
@ -66,6 +68,13 @@ public class UriTokenizer {
|
|||
NULL,
|
||||
MAX,
|
||||
|
||||
AVERAGE, // for the aggregation extension
|
||||
COUNTDISTINCT, // for the aggregation extension
|
||||
IDENTITY, // for the aggregation extension
|
||||
MIN, // for the aggregation extension
|
||||
SUM, // for the aggregation extension
|
||||
ROLLUP_ALL, // for the aggregation extension
|
||||
|
||||
// variable-value tokens (convention: mixed case)
|
||||
ODataIdentifier,
|
||||
QualifiedName,
|
||||
|
@ -125,6 +134,10 @@ public class UriTokenizer {
|
|||
MinusOperator,
|
||||
NotOperator,
|
||||
|
||||
AsOperator, // for the aggregation extension
|
||||
FromOperator, // for the aggregation extension
|
||||
WithOperator, // for the aggregation extension
|
||||
|
||||
CastMethod,
|
||||
CeilingMethod,
|
||||
ConcatMethod,
|
||||
|
@ -158,6 +171,23 @@ public class UriTokenizer {
|
|||
TrimMethod,
|
||||
YearMethod,
|
||||
|
||||
IsDefinedMethod, // for the aggregation extension
|
||||
|
||||
AggregateTrafo, // for the aggregation extension
|
||||
BottomCountTrafo, // for the aggregation extension
|
||||
BottomPercentTrafo, // for the aggregation extension
|
||||
BottomSumTrafo, // for the aggregation extension
|
||||
ComputeTrafo, // for the aggregation extension
|
||||
ExpandTrafo, // for the aggregation extension
|
||||
FilterTrafo, // for the aggregation extension
|
||||
GroupByTrafo, // for the aggregation extension
|
||||
SearchTrafo, // for the aggregation extension
|
||||
TopCountTrafo, // for the aggregation extension
|
||||
TopPercentTrafo, // for the aggregation extension
|
||||
TopSumTrafo, // for the aggregation extension
|
||||
|
||||
RollUpSpec, // for the aggregation extension
|
||||
|
||||
AscSuffix,
|
||||
DescSuffix
|
||||
}
|
||||
|
@ -167,10 +197,31 @@ public class UriTokenizer {
|
|||
private int startIndex = 0;
|
||||
private int index = 0;
|
||||
|
||||
private int savedStartIndex;
|
||||
private int savedIndex;
|
||||
|
||||
public UriTokenizer(final String parseString) {
|
||||
this.parseString = parseString == null ? "" : parseString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the current state.
|
||||
* @see #returnToSavedState()
|
||||
*/
|
||||
public void saveState() {
|
||||
savedStartIndex = startIndex;
|
||||
savedIndex = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return to the previously saved state.
|
||||
* @see #saveState()
|
||||
*/
|
||||
public void returnToSavedState() {
|
||||
startIndex = savedStartIndex;
|
||||
index = savedIndex;
|
||||
}
|
||||
|
||||
/** Returns the string value corresponding to the last successful {@link #next(TokenKind)} call. */
|
||||
public String getText() {
|
||||
return parseString.substring(startIndex, index);
|
||||
|
@ -218,6 +269,9 @@ public class UriTokenizer {
|
|||
found = nextConstant("$it");
|
||||
break;
|
||||
|
||||
case APPLY:
|
||||
found = nextConstant("$apply");
|
||||
break;
|
||||
case EXPAND:
|
||||
found = nextConstant("$expand");
|
||||
break;
|
||||
|
@ -288,6 +342,26 @@ public class UriTokenizer {
|
|||
found = nextConstant("max");
|
||||
break;
|
||||
|
||||
case AVERAGE:
|
||||
found = nextConstant("average");
|
||||
break;
|
||||
case COUNTDISTINCT:
|
||||
found = nextConstant("countdistinct");
|
||||
break;
|
||||
case IDENTITY:
|
||||
found = nextConstant("identity");
|
||||
break;
|
||||
case MIN:
|
||||
found = nextConstant("min");
|
||||
break;
|
||||
case SUM:
|
||||
found = nextConstant("sum");
|
||||
break;
|
||||
|
||||
case ROLLUP_ALL:
|
||||
found = nextConstant("$all");
|
||||
break;
|
||||
|
||||
// Identifiers
|
||||
case ODataIdentifier:
|
||||
found = nextODataIdentifier();
|
||||
|
@ -456,6 +530,17 @@ public class UriTokenizer {
|
|||
found = nextUnaryOperator("not");
|
||||
break;
|
||||
|
||||
// Operators for the aggregation extension
|
||||
case AsOperator:
|
||||
found = nextBinaryOperator("as");
|
||||
break;
|
||||
case FromOperator:
|
||||
found = nextBinaryOperator("from");
|
||||
break;
|
||||
case WithOperator:
|
||||
found = nextBinaryOperator("with");
|
||||
break;
|
||||
|
||||
// Methods
|
||||
case CastMethod:
|
||||
found = nextMethod("cast");
|
||||
|
@ -554,6 +639,54 @@ public class UriTokenizer {
|
|||
found = nextMethod("year");
|
||||
break;
|
||||
|
||||
// Method for the aggregation extension
|
||||
case IsDefinedMethod:
|
||||
found = nextMethod("isdefined");
|
||||
break;
|
||||
|
||||
// Transformations for the aggregation extension
|
||||
case AggregateTrafo:
|
||||
found = nextMethod("aggregate");
|
||||
break;
|
||||
case BottomCountTrafo:
|
||||
found = nextMethod("bottomcount");
|
||||
break;
|
||||
case BottomPercentTrafo:
|
||||
found = nextMethod("bottompercent");
|
||||
break;
|
||||
case BottomSumTrafo:
|
||||
found = nextMethod("bottomsum");
|
||||
break;
|
||||
case ComputeTrafo:
|
||||
found = nextMethod("compute");
|
||||
break;
|
||||
case ExpandTrafo:
|
||||
found = nextMethod("expand");
|
||||
break;
|
||||
case FilterTrafo:
|
||||
found = nextMethod("filter");
|
||||
break;
|
||||
case GroupByTrafo:
|
||||
found = nextMethod("groupby");
|
||||
break;
|
||||
case SearchTrafo:
|
||||
found = nextMethod("search");
|
||||
break;
|
||||
case TopCountTrafo:
|
||||
found = nextMethod("topcount");
|
||||
break;
|
||||
case TopPercentTrafo:
|
||||
found = nextMethod("toppercent");
|
||||
break;
|
||||
case TopSumTrafo:
|
||||
found = nextMethod("topsum");
|
||||
break;
|
||||
|
||||
// Roll-up specification for the aggregation extension
|
||||
case RollUpSpec:
|
||||
found = nextMethod("rollup");
|
||||
break;
|
||||
|
||||
// Suffixes
|
||||
case AscSuffix:
|
||||
found = nextSuffix("asc");
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.queryoption;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind;
|
||||
|
||||
public class ApplyOptionImpl extends SystemQueryOptionImpl implements ApplyOption {
|
||||
|
||||
private List<ApplyItem> transformations = new ArrayList<ApplyItem>();
|
||||
|
||||
public ApplyOptionImpl() {
|
||||
setKind(SystemQueryOptionKind.APPLY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ApplyItem> getApplyItems() {
|
||||
return Collections.unmodifiableList(transformations);
|
||||
}
|
||||
|
||||
public ApplyOptionImpl add(final ApplyItem transformation) {
|
||||
transformations.add(transformation);
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* 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.queryoption.apply;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.FullQualifiedName;
|
||||
import org.apache.olingo.server.api.uri.UriInfo;
|
||||
import org.apache.olingo.server.api.uri.UriResource;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.AggregateExpression;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
|
||||
|
||||
/**
|
||||
* Represents an aggregate expression.
|
||||
*/
|
||||
public class AggregateExpressionImpl implements AggregateExpression {
|
||||
|
||||
private UriInfo path;
|
||||
private Expression expression;
|
||||
private StandardMethod standardMethod;
|
||||
private FullQualifiedName customMethod;
|
||||
private String alias;
|
||||
private AggregateExpression inlineAggregateExpression;
|
||||
private List<AggregateExpression> from = new ArrayList<AggregateExpression>();
|
||||
|
||||
@Override
|
||||
public List<UriResource> getPath() {
|
||||
return path == null ? Collections.<UriResource> emptyList() : path.getUriResourceParts();
|
||||
}
|
||||
|
||||
public AggregateExpressionImpl setPath(final UriInfo uriInfo) {
|
||||
path = uriInfo;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression getExpression() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
public AggregateExpressionImpl setExpression(final Expression expression) {
|
||||
this.expression = expression;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StandardMethod getStandardMethod() {
|
||||
return standardMethod;
|
||||
}
|
||||
|
||||
public AggregateExpressionImpl setStandardMethod(final StandardMethod standardMethod) {
|
||||
this.standardMethod = standardMethod;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FullQualifiedName getCustomMethod() {
|
||||
return customMethod;
|
||||
}
|
||||
|
||||
public AggregateExpressionImpl setCustomMethod(final FullQualifiedName customMethod) {
|
||||
this.customMethod = customMethod;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AggregateExpression getInlineAggregateExpression() {
|
||||
return inlineAggregateExpression;
|
||||
}
|
||||
|
||||
public AggregateExpressionImpl setInlineAggregateExpression(final AggregateExpression aggregateExpression) {
|
||||
inlineAggregateExpression = aggregateExpression;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AggregateExpression> getFrom() {
|
||||
return Collections.unmodifiableList(from);
|
||||
}
|
||||
|
||||
public AggregateExpressionImpl addFrom(final AggregateExpression from) {
|
||||
this.from.add(from);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public AggregateExpressionImpl setAlias(final String alias) {
|
||||
this.alias = alias;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.queryoption.apply;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Aggregate;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.AggregateExpression;
|
||||
|
||||
/**
|
||||
* Represents the aggregate transformation.
|
||||
*/
|
||||
public class AggregateImpl implements Aggregate {
|
||||
|
||||
private List<AggregateExpression> expressions = new ArrayList<AggregateExpression>();
|
||||
|
||||
@Override
|
||||
public Kind getKind() {
|
||||
return Kind.AGGREGATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AggregateExpression> getExpressions() {
|
||||
return expressions;
|
||||
}
|
||||
|
||||
public AggregateImpl addExpression(final AggregateExpression expression) {
|
||||
expressions.add(expression);
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.queryoption.apply;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.BottomTop;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
|
||||
|
||||
/**
|
||||
* Represents a transformation with one of the pre-defined methods
|
||||
* <code>bottomcount</code>, <code>bottompercent</code>, <code>bottomsum</code>,
|
||||
* <code>topcount</code>, <code>toppercent</code>, <code>topsum</code>.
|
||||
*/
|
||||
public class BottomTopImpl implements BottomTop {
|
||||
|
||||
private Method method;
|
||||
private Expression number;
|
||||
private Expression value;
|
||||
|
||||
@Override
|
||||
public Kind getKind() {
|
||||
return Kind.BOTTOM_TOP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public BottomTopImpl setMethod(final Method method) {
|
||||
this.method = method;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public BottomTopImpl setNumber(final Expression number) {
|
||||
this.number = number;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public BottomTopImpl setValue(final Expression value) {
|
||||
this.value = value;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.queryoption.apply;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.ComputeExpression;
|
||||
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
|
||||
|
||||
/**
|
||||
* Represents an aggregate expression.
|
||||
*/
|
||||
public class ComputeExpressionImpl implements ComputeExpression {
|
||||
|
||||
private Expression expression;
|
||||
private String alias;
|
||||
|
||||
@Override
|
||||
public Expression getExpression() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
public ComputeExpressionImpl setExpression(final Expression expression) {
|
||||
this.expression = expression;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public ComputeExpressionImpl setAlias(final String alias) {
|
||||
this.alias = alias;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.queryoption.apply;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Compute;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.ComputeExpression;
|
||||
|
||||
/**
|
||||
* Represents the compute transformation.
|
||||
*/
|
||||
public class ComputeImpl implements Compute {
|
||||
|
||||
private List<ComputeExpression> expressions = new ArrayList<ComputeExpression>();
|
||||
|
||||
@Override
|
||||
public Kind getKind() {
|
||||
return Kind.COMPUTE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ComputeExpression> getExpressions() {
|
||||
return expressions;
|
||||
}
|
||||
|
||||
public ComputeImpl addExpression(final ComputeExpressionImpl expression) {
|
||||
expressions.add(expression);
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.queryoption.apply;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Concat;
|
||||
|
||||
/**
|
||||
* Represents the concat transformation.
|
||||
*/
|
||||
public class ConcatImpl implements Concat {
|
||||
|
||||
private List<ApplyOption> options = new ArrayList<ApplyOption>();
|
||||
|
||||
@Override
|
||||
public Kind getKind() {
|
||||
return Kind.CONCAT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ApplyOption> getApplyOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public ConcatImpl addApplyOption(final ApplyOption option) {
|
||||
options.add(option);
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.queryoption.apply;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.EdmFunction;
|
||||
import org.apache.olingo.server.api.uri.UriParameter;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.CustomFunction;
|
||||
|
||||
/**
|
||||
* Represents a transformation with a custom function.
|
||||
*/
|
||||
public class CustomFunctionImpl implements CustomFunction {
|
||||
|
||||
private EdmFunction function = null;
|
||||
private List<UriParameter> parameters;
|
||||
|
||||
@Override
|
||||
public Kind getKind() {
|
||||
return Kind.CUSTOM_FUNCTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdmFunction getFunction() {
|
||||
return function;
|
||||
}
|
||||
|
||||
public CustomFunctionImpl setFunction(final EdmFunction function) {
|
||||
this.function = function;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UriParameter> getParameters() {
|
||||
return parameters == null ?
|
||||
Collections.<UriParameter> emptyList() :
|
||||
Collections.unmodifiableList(parameters);
|
||||
}
|
||||
|
||||
public CustomFunctionImpl setParameters(final List<UriParameter> parameters) {
|
||||
this.parameters = parameters;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* 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.queryoption.apply;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.EdmAnnotation;
|
||||
import org.apache.olingo.commons.api.edm.EdmMapping;
|
||||
import org.apache.olingo.commons.api.edm.EdmProperty;
|
||||
import org.apache.olingo.commons.api.edm.EdmTerm;
|
||||
import org.apache.olingo.commons.api.edm.EdmType;
|
||||
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
|
||||
import org.apache.olingo.commons.api.edm.geo.SRID;
|
||||
|
||||
/** A dynamic EDM property containing an aggregation. */
|
||||
public class DynamicProperty implements EdmProperty {
|
||||
|
||||
private final String name;
|
||||
private final EdmType propertyType;
|
||||
|
||||
/** Creates a dynamic property with a mandatory name and an optional type. */
|
||||
public DynamicProperty(final String name, final EdmType type) {
|
||||
this.name = name;
|
||||
propertyType = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdmType getType() {
|
||||
return propertyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollection() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdmMapping getMapping() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMimeType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNullable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getMaxLength() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getPrecision() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getScale() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SRID getSrid() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnicode() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrimitive() {
|
||||
return propertyType != null && propertyType.getKind() == EdmTypeKind.PRIMITIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdmAnnotation getAnnotation(final EdmTerm term, final String qualifier) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EdmAnnotation> getAnnotations() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* 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.queryoption.apply;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.EdmAnnotation;
|
||||
import org.apache.olingo.commons.api.edm.EdmElement;
|
||||
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.EdmTerm;
|
||||
import org.apache.olingo.commons.api.edm.EdmType;
|
||||
import org.apache.olingo.commons.api.edm.FullQualifiedName;
|
||||
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
|
||||
|
||||
/** A dynamic structured type used to incorporate dynamic properties containing aggregations. */
|
||||
public class DynamicStructuredType implements EdmStructuredType, Cloneable {
|
||||
|
||||
private final EdmStructuredType startType;
|
||||
private Map<String, EdmProperty> properties;
|
||||
|
||||
public DynamicStructuredType(final EdmStructuredType startType) {
|
||||
this.startType = startType;
|
||||
}
|
||||
|
||||
public DynamicStructuredType addProperty(final EdmProperty property) {
|
||||
if (properties == null) {
|
||||
properties = new LinkedHashMap<String, EdmProperty>();
|
||||
}
|
||||
properties.put(property.getName(), property);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdmElement getProperty(final String name) {
|
||||
final EdmElement property = startType.getProperty(name);
|
||||
return property == null ?
|
||||
properties == null ? null : properties.get(name) :
|
||||
property;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPropertyNames() {
|
||||
if (properties == null || properties.isEmpty()) {
|
||||
return startType.getPropertyNames();
|
||||
} else {
|
||||
List<String> names = new ArrayList<String>(startType.getPropertyNames());
|
||||
names.addAll(properties.keySet());
|
||||
return Collections.unmodifiableList(names);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdmProperty getStructuralProperty(final String name) {
|
||||
final EdmProperty property = startType.getStructuralProperty(name);
|
||||
return property == null ?
|
||||
properties == null ? null : properties.get(name) :
|
||||
property;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdmNavigationProperty getNavigationProperty(final String name) {
|
||||
return startType.getNavigationProperty(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getNavigationPropertyNames() {
|
||||
return startType.getNavigationPropertyNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return startType.getNamespace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return startType.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FullQualifiedName getFullQualifiedName() {
|
||||
return startType.getFullQualifiedName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdmTypeKind getKind() {
|
||||
return startType.getKind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdmAnnotation getAnnotation(final EdmTerm term, final String qualifier) {
|
||||
return startType.getAnnotation(term, qualifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EdmAnnotation> getAnnotations() {
|
||||
return startType.getAnnotations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdmStructuredType getBaseType() {
|
||||
return startType.getBaseType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean compatibleTo(final EdmType targetType) {
|
||||
return startType.compatibleTo(targetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpenType() {
|
||||
return startType.isOpenType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAbstract() {
|
||||
return startType.isAbstract();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.queryoption.apply;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Expand;
|
||||
|
||||
/**
|
||||
* Represents the expand transformation.
|
||||
*/
|
||||
public class ExpandImpl implements Expand {
|
||||
|
||||
private ExpandOption expandOption = null;
|
||||
|
||||
@Override
|
||||
public Kind getKind() {
|
||||
return Kind.EXPAND;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpandOption getExpandOption() {
|
||||
return expandOption;
|
||||
}
|
||||
|
||||
public ExpandImpl setExpandOption(final ExpandOption expandOption) {
|
||||
this.expandOption = expandOption;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.queryoption.apply;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.FilterOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Filter;
|
||||
|
||||
/**
|
||||
* Represents the filter transformation.
|
||||
*/
|
||||
public class FilterImpl implements Filter {
|
||||
|
||||
private FilterOption filterOption = null;
|
||||
|
||||
@Override
|
||||
public Kind getKind() {
|
||||
return Kind.FILTER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilterOption getFilterOption() {
|
||||
return filterOption;
|
||||
}
|
||||
|
||||
public FilterImpl setFilterOption(final FilterOption filterOption) {
|
||||
this.filterOption = filterOption;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.queryoption.apply;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.GroupBy;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.GroupByItem;
|
||||
|
||||
/**
|
||||
* Represents the grouping transformation.
|
||||
*/
|
||||
public class GroupByImpl implements GroupBy {
|
||||
|
||||
private ApplyOption applyOption;
|
||||
private List<GroupByItem> groupByItems = new ArrayList<GroupByItem>();
|
||||
|
||||
@Override
|
||||
public Kind getKind() {
|
||||
return Kind.GROUP_BY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplyOption getApplyOption() {
|
||||
return applyOption;
|
||||
}
|
||||
|
||||
public GroupByImpl setApplyOption(final ApplyOption applyOption) {
|
||||
this.applyOption = applyOption;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupByItem> getGroupByItems() {
|
||||
return groupByItems;
|
||||
}
|
||||
|
||||
public GroupByImpl addGroupByItem(final GroupByItem groupByItem) {
|
||||
groupByItems.add(groupByItem);
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.queryoption.apply;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.uri.UriInfo;
|
||||
import org.apache.olingo.server.api.uri.UriResource;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.GroupByItem;
|
||||
|
||||
/**
|
||||
* Represents a grouping property.
|
||||
*/
|
||||
public class GroupByItemImpl implements GroupByItem {
|
||||
|
||||
private UriInfo path;
|
||||
private boolean isRollupAll;
|
||||
private List<GroupByItem> rollup = new ArrayList<GroupByItem>();
|
||||
|
||||
@Override
|
||||
public List<UriResource> getPath() {
|
||||
return path == null ? Collections.<UriResource> emptyList() : path.getUriResourceParts();
|
||||
}
|
||||
|
||||
public GroupByItemImpl setPath(final UriInfo uriInfo) {
|
||||
path = uriInfo;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupByItem> getRollup() {
|
||||
return rollup;
|
||||
}
|
||||
|
||||
public GroupByItemImpl addRollupItem(final GroupByItem groupByItem) {
|
||||
rollup.add(groupByItem);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRollupAll() {
|
||||
return isRollupAll;
|
||||
}
|
||||
|
||||
public GroupByItemImpl setIsRollupAll() {
|
||||
this.isRollupAll = true;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.queryoption.apply;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Identity;
|
||||
|
||||
/**
|
||||
* Represents the identity transformation.
|
||||
*/
|
||||
public class IdentityImpl implements Identity {
|
||||
|
||||
@Override
|
||||
public Kind getKind() {
|
||||
return Kind.IDENTITY;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.queryoption.apply;
|
||||
|
||||
import org.apache.olingo.server.api.uri.queryoption.SearchOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Search;
|
||||
|
||||
/**
|
||||
* Represents the search transformation.
|
||||
*/
|
||||
public class SearchImpl implements Search {
|
||||
|
||||
private SearchOption searchOption = null;
|
||||
|
||||
@Override
|
||||
public Kind getKind() {
|
||||
return Kind.SEARCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchOption getSearchOption() {
|
||||
return searchOption;
|
||||
}
|
||||
|
||||
public SearchImpl setSearchOption(final SearchOption searchOption) {
|
||||
this.searchOption = searchOption;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -45,27 +45,27 @@ public class UriValidator {
|
|||
//CHECKSTYLE:OFF (Maven checkstyle)
|
||||
private static final boolean[][] decisionMatrix =
|
||||
{
|
||||
/* 0-FILTER 1-FORMAT 2-EXPAND 3-ID 4-COUNT 5-ORDERBY 6-SEARCH 7-SELECT 8-SKIP 9-SKIPTOKEN 10-TOP */
|
||||
/* all 0 */ { true , true , true , false, true , true , true , true , true , true , true },
|
||||
/* batch 1 */ { false, false, false, false, false, false, false, false, false, false, false },
|
||||
/* crossjoin 2 */ { true , true , true , false, true , true , true , true , true , true , true },
|
||||
/* entityId 3 */ { false, true , true , true , false, false, false, true , false, false, false },
|
||||
/* metadata 4 */ { false, true , false, false, false, false, false, false, false, false, false },
|
||||
/* service 5 */ { false, true , false, false, false, false, false, false, false, false, false },
|
||||
/* entitySet 6 */ { true , true , true , false, true , true , true , true , true , true , true },
|
||||
/* entitySetCount 7 */ { true , false, false, false, false, false, true , false, false, false, false },
|
||||
/* entity 8 */ { false, true , true , false, false, false, false, true , false, false, false },
|
||||
/* mediaStream 9 */ { false, false, false, false, false, false, false, false, false, false, false },
|
||||
/* references 10 */ { true , true , false, false, true , true , true , false, true , true , true },
|
||||
/* reference 11 */ { false, true , false, false, false, false, false, false, false, false, false },
|
||||
/* propertyComplex 12 */ { false, true , true , false, false, false, false, true , false, false, false },
|
||||
/* propertyComplexCollection 13 */ { true , true , true , false, true , true , false, true , true , true , true },
|
||||
/* propertyComplexCollectionCount 14 */ { true , false, false, false, false, false, false, false, false, false, false },
|
||||
/* propertyPrimitive 15 */ { false, true , false, false, false, false, false, false, false, false, false },
|
||||
/* propertyPrimitiveCollection 16 */ { true , true , false, false, true , true , false, false, true , true , true },
|
||||
/* propertyPrimitiveCollectionCount 17 */ { true , false, false, false, false, false, false, false, false, false, false },
|
||||
/* propertyPrimitiveValue 18 */ { false, true , false, false, false, false, false, false, false, false, false },
|
||||
/* none 19 */ { false, true , false, false, false, false, false, false, false, false, false }
|
||||
/* 0-FILTER 1-FORMAT 2-EXPAND 3-ID 4-COUNT 5-ORDERBY 6-SEARCH 7-SELECT 8-SKIP 9-SKIPTOKEN 10-TOP 11-APPLY */
|
||||
/* all 0 */ { true , true , true , false, true , true , true , true , true , true , true , true },
|
||||
/* batch 1 */ { false, false, false, false, false, false, false, false, false, false, false, false },
|
||||
/* crossjoin 2 */ { true , true , true , false, true , true , true , true , true , true , true , true },
|
||||
/* entityId 3 */ { false, true , true , true , false, false, false, true , false, false, false, false },
|
||||
/* metadata 4 */ { false, true , false, false, false, false, false, false, false, false, false, false },
|
||||
/* service 5 */ { false, true , false, false, false, false, false, false, false, false, false, false },
|
||||
/* entitySet 6 */ { true , true , true , false, true , true , true , true , true , true , true , true },
|
||||
/* entitySetCount 7 */ { true , false, false, false, false, false, true , false, false, false, false, false },
|
||||
/* entity 8 */ { false, true , true , false, false, false, false, true , false, false, false, false },
|
||||
/* mediaStream 9 */ { false, false, false, false, false, false, false, false, false, false, false, false },
|
||||
/* references 10 */ { true , true , false, false, true , true , true , false, true , true , true , false },
|
||||
/* reference 11 */ { false, true , false, false, false, false, false, false, false, false, false, false },
|
||||
/* propertyComplex 12 */ { false, true , true , false, false, false, false, true , false, false, false, false },
|
||||
/* propertyComplexCollection 13 */ { true , true , true , false, true , true , false, true , true , true , true , true },
|
||||
/* propertyComplexCollectionCount 14 */ { true , false, false, false, false, false, false, false, false, false, false, false },
|
||||
/* propertyPrimitive 15 */ { false, true , false, false, false, false, false, false, false, false, false, false },
|
||||
/* propertyPrimitiveCollection 16 */ { true , true , false, false, true , true , false, false, true , true , true , false },
|
||||
/* propertyPrimitiveCollectionCount 17 */ { true , false, false, false, false, false, false, false, false, false, false, false },
|
||||
/* propertyPrimitiveValue 18 */ { false, true , false, false, false, false, false, false, false, false, false, false },
|
||||
/* none 19 */ { false, true , false, false, false, false, false, false, false, false, false, false }
|
||||
};
|
||||
//CHECKSTYLE:ON
|
||||
//@formatter:on
|
||||
|
@ -118,6 +118,7 @@ public class UriValidator {
|
|||
temp.put(SystemQueryOptionKind.SKIP, 8);
|
||||
temp.put(SystemQueryOptionKind.SKIPTOKEN, 9);
|
||||
temp.put(SystemQueryOptionKind.TOP, 10);
|
||||
temp.put(SystemQueryOptionKind.APPLY, 11);
|
||||
OPTION_INDEX = Collections.unmodifiableMap(temp);
|
||||
}
|
||||
|
||||
|
@ -255,7 +256,6 @@ public class UriValidator {
|
|||
|
||||
private UriType getUriTypeForFunction(final UriResource lastPathSegment) throws UriValidationException {
|
||||
final UriResourceFunction uriFunction = (UriResourceFunction) lastPathSegment;
|
||||
|
||||
final boolean isCollection = uriFunction.isCollection();
|
||||
final EdmTypeKind typeKind = uriFunction.getFunction().getReturnType().getType().getKind();
|
||||
UriType uriType;
|
||||
|
|
|
@ -73,6 +73,9 @@ UriParserSemanticException.NAMESPACE_NOT_ALLOWED_AT_FIRST_ELEMENT=Namespace is n
|
|||
UriParserSemanticException.COMPLEX_PARAMETER_IN_RESOURCE_PATH=Complex parameters must not appear in resource path segments; found: '%1$s'.
|
||||
UriParserSemanticException.TYPES_NOT_COMPATIBLE=The types '%1$s' and '%2$s' are not compatible.
|
||||
UriParserSemanticException.NOT_A_MEDIA_RESOURCE=The resource '%1$s' is not a media resource. $value can only be applied on media resources.
|
||||
UriParserSemanticException.IS_PROPERTY=The identifier '%1$s' is already used as a property.
|
||||
UriParserSemanticException.ONLY_FOR_PRIMITIVE_TYPES='%1$s' is only allowed for primitive-type expressions.
|
||||
UriParserSemanticException.FUNCTION_MUST_USE_COLLECTIONS=Only bound functions with collections of structural types as binding parameter and as return type are allowed; '%1$s' is not such a function.
|
||||
|
||||
UriValidationException.UNSUPPORTED_QUERY_OPTION=The query option '%1$s' is not supported.
|
||||
UriValidationException.UNSUPPORTED_URI_KIND=The URI kind '%1$s' is not supported.
|
||||
|
|
|
@ -96,6 +96,18 @@ public class UriTokenizerTest {
|
|||
assertTrue(tokenizer.next(TokenKind.EOF));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveState() {
|
||||
UriTokenizer tokenizer = new UriTokenizer("a*");
|
||||
assertTrue(tokenizer.next(TokenKind.ODataIdentifier));
|
||||
tokenizer.saveState();
|
||||
assertTrue(tokenizer.next(TokenKind.STAR));
|
||||
assertTrue(tokenizer.next(TokenKind.EOF));
|
||||
tokenizer.returnToSavedState();
|
||||
assertTrue(tokenizer.next(TokenKind.STAR));
|
||||
assertTrue(tokenizer.next(TokenKind.EOF));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void systemQueryOptions() {
|
||||
UriTokenizer tokenizer = new UriTokenizer("$expand=*;$filter=true;$levels=max;$orderby=false");
|
||||
|
@ -642,6 +654,64 @@ public class UriTokenizerTest {
|
|||
wrongToken(TokenKind.GeometryCollection, "geometry'SRID=0;Collection(Point(1 2),Point(3 4))'", 'x');
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aggregation() {
|
||||
UriTokenizer tokenizer = new UriTokenizer("$apply=aggregate(a with sum as s from x with average)");
|
||||
assertTrue(tokenizer.next(TokenKind.APPLY));
|
||||
assertTrue(tokenizer.next(TokenKind.EQ));
|
||||
assertTrue(tokenizer.next(TokenKind.AggregateTrafo));
|
||||
assertTrue(tokenizer.next(TokenKind.ODataIdentifier));
|
||||
assertTrue(tokenizer.next(TokenKind.WithOperator));
|
||||
assertTrue(tokenizer.next(TokenKind.SUM));
|
||||
assertTrue(tokenizer.next(TokenKind.AsOperator));
|
||||
assertTrue(tokenizer.next(TokenKind.ODataIdentifier));
|
||||
assertTrue(tokenizer.next(TokenKind.FromOperator));
|
||||
assertTrue(tokenizer.next(TokenKind.ODataIdentifier));
|
||||
assertTrue(tokenizer.next(TokenKind.WithOperator));
|
||||
assertTrue(tokenizer.next(TokenKind.AVERAGE));
|
||||
assertTrue(tokenizer.next(TokenKind.CLOSE));
|
||||
assertTrue(tokenizer.next(TokenKind.EOF));
|
||||
|
||||
tokenizer = new UriTokenizer("a with min as m");
|
||||
assertTrue(tokenizer.next(TokenKind.ODataIdentifier));
|
||||
assertTrue(tokenizer.next(TokenKind.WithOperator));
|
||||
assertTrue(tokenizer.next(TokenKind.MIN));
|
||||
|
||||
tokenizer = new UriTokenizer("a with countdistinct as c");
|
||||
assertTrue(tokenizer.next(TokenKind.ODataIdentifier));
|
||||
assertTrue(tokenizer.next(TokenKind.WithOperator));
|
||||
assertTrue(tokenizer.next(TokenKind.COUNTDISTINCT));
|
||||
|
||||
assertTrue(new UriTokenizer("identity").next(TokenKind.IDENTITY));
|
||||
assertTrue(new UriTokenizer("bottomcount(1,x)").next(TokenKind.BottomCountTrafo));
|
||||
assertTrue(new UriTokenizer("bottompercent(1,x)").next(TokenKind.BottomPercentTrafo));
|
||||
assertTrue(new UriTokenizer("bottomsum(1,x)").next(TokenKind.BottomSumTrafo));
|
||||
assertTrue(new UriTokenizer("topcount(1,x)").next(TokenKind.TopCountTrafo));
|
||||
assertTrue(new UriTokenizer("toppercent(1,x)").next(TokenKind.TopPercentTrafo));
|
||||
assertTrue(new UriTokenizer("topsum(1,x)").next(TokenKind.TopSumTrafo));
|
||||
assertTrue(new UriTokenizer("compute(a mul b as m)").next(TokenKind.ComputeTrafo));
|
||||
|
||||
assertTrue(new UriTokenizer("search(a)").next(TokenKind.SearchTrafo));
|
||||
assertTrue(new UriTokenizer("expand(a)").next(TokenKind.ExpandTrafo));
|
||||
assertTrue(new UriTokenizer("filter(true)").next(TokenKind.FilterTrafo));
|
||||
|
||||
tokenizer = new UriTokenizer("groupby((rollup($all,x,y)))");
|
||||
assertTrue(tokenizer.next(TokenKind.GroupByTrafo));
|
||||
assertTrue(tokenizer.next(TokenKind.OPEN));
|
||||
assertTrue(tokenizer.next(TokenKind.RollUpSpec));
|
||||
assertTrue(tokenizer.next(TokenKind.ROLLUP_ALL));
|
||||
assertTrue(tokenizer.next(TokenKind.COMMA));
|
||||
assertTrue(tokenizer.next(TokenKind.ODataIdentifier));
|
||||
assertTrue(tokenizer.next(TokenKind.COMMA));
|
||||
assertTrue(tokenizer.next(TokenKind.ODataIdentifier));
|
||||
assertTrue(tokenizer.next(TokenKind.CLOSE));
|
||||
assertTrue(tokenizer.next(TokenKind.CLOSE));
|
||||
assertTrue(tokenizer.next(TokenKind.CLOSE));
|
||||
assertTrue(tokenizer.next(TokenKind.EOF));
|
||||
|
||||
assertTrue(new UriTokenizer("isdefined(x)").next(TokenKind.IsDefinedMethod));
|
||||
}
|
||||
|
||||
private void wrongToken(final TokenKind kind, final String value, final char disturbCharacter) {
|
||||
assertFalse(new UriTokenizer(disturbCharacter + value).next(kind));
|
||||
|
||||
|
|
|
@ -233,7 +233,7 @@ public abstract class TechnicalProcessor implements Processor {
|
|||
}
|
||||
|
||||
protected void validateOptions(final UriInfoResource uriInfo) throws ODataApplicationException {
|
||||
if (uriInfo.getIdOption() != null) {
|
||||
if (uriInfo.getIdOption() != null || uriInfo.getApplyOption() != null) {
|
||||
throw new ODataApplicationException("Not all of the specified options are supported.",
|
||||
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,676 @@
|
|||
/*
|
||||
* 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.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.Edm;
|
||||
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
|
||||
import org.apache.olingo.commons.api.edm.FullQualifiedName;
|
||||
import org.apache.olingo.commons.api.edmx.EdmxReference;
|
||||
import org.apache.olingo.server.api.OData;
|
||||
import org.apache.olingo.server.api.uri.UriInfo;
|
||||
import org.apache.olingo.server.api.uri.UriInfoKind;
|
||||
import org.apache.olingo.server.api.uri.UriResource;
|
||||
import org.apache.olingo.server.api.uri.UriResourceKind;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Aggregate;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.AggregateExpression;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.AggregateExpression.StandardMethod;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.BottomTop;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.BottomTop.Method;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Compute;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.ComputeExpression;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Concat;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.CustomFunction;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Expand;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Filter;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.GroupBy;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.GroupByItem;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Identity;
|
||||
import org.apache.olingo.server.api.uri.queryoption.apply.Search;
|
||||
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.UriInfoImpl;
|
||||
import org.apache.olingo.server.core.uri.testutil.ExpandValidator;
|
||||
import org.apache.olingo.server.core.uri.testutil.FilterValidator;
|
||||
import org.apache.olingo.server.core.uri.testutil.ResourceValidator;
|
||||
import org.apache.olingo.server.core.uri.testutil.TestUriValidator;
|
||||
import org.apache.olingo.server.core.uri.testutil.TestValidator;
|
||||
import org.apache.olingo.server.core.uri.validator.UriValidationException;
|
||||
import org.apache.olingo.server.tecsvc.provider.ComplexTypeProvider;
|
||||
import org.apache.olingo.server.tecsvc.provider.EdmTechProvider;
|
||||
import org.apache.olingo.server.tecsvc.provider.EntityTypeProvider;
|
||||
import org.apache.olingo.server.tecsvc.provider.FunctionProvider;
|
||||
import org.apache.olingo.server.tecsvc.provider.PropertyProvider;
|
||||
import org.junit.Test;
|
||||
|
||||
/** Tests of the $apply parser inspired by the ABNF test cases. */
|
||||
public class ApplyParserTest {
|
||||
|
||||
private static final OData odata = OData.newInstance();
|
||||
private static final Edm edm = odata.createServiceMetadata(
|
||||
new EdmTechProvider(), Collections.<EdmxReference> emptyList()).getEdm();
|
||||
|
||||
@Test
|
||||
public void aggregate() throws Exception {
|
||||
parse("ESTwoKeyNav", "aggregate(PropertyInt16 with sum as s)")
|
||||
.is(Aggregate.class)
|
||||
.goAggregate(0).isStandardMethod(StandardMethod.SUM).isAlias("s")
|
||||
.goExpression().goPath().first().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
parse("ESTwoKeyNav", "aggregate(PropertyInt16 with min as m)")
|
||||
.goAggregate(0).isStandardMethod(StandardMethod.MIN).isAlias("m");
|
||||
parse("ESTwoKeyNav", "aggregate(PropertyInt16 with max as m)")
|
||||
.goAggregate(0).isStandardMethod(StandardMethod.MAX).isAlias("m");
|
||||
parse("ESTwoKeyNav", "aggregate(PropertyInt16 with average as a)")
|
||||
.goAggregate(0).isStandardMethod(StandardMethod.AVERAGE).isAlias("a");
|
||||
parse("ESTwoKeyNav", "aggregate(PropertyInt16 with countdistinct as c)")
|
||||
.goAggregate(0).isStandardMethod(StandardMethod.COUNT_DISTINCT).isAlias("c");
|
||||
parse("ESTwoKeyNav", "aggregate(PropertyInt16 with custom.aggregate as c)")
|
||||
.is(Aggregate.class)
|
||||
.goAggregate(0).isCustomMethod(new FullQualifiedName("custom", "aggregate")).isAlias("c");
|
||||
|
||||
parseEx("ESTwoKeyNav", "aggregate()")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
parseEx("ESTwoKeyNav", "aggregate(PropertyInt16)")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
parseEx("ESTwoKeyNav", "aggregate(PropertyInt16 with sum)")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
parseEx("ESTwoKeyNav", "aggregate(PropertyInt16 as s)")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
parseEx("ESTwoKeyNav", "aggregate(PropertyInt16 with SUM as s)")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
parseEx("ESTwoKeyNav", "aggregate(PropertyString with countdistinct as PropertyInt16)")
|
||||
.isExSemantic(UriParserSemanticException.MessageKeys.IS_PROPERTY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aggregateExpression() throws Exception {
|
||||
parse("ESTwoKeyNav", "aggregate(PropertyInt16 mul PropertyComp/PropertyInt16 with sum as s)")
|
||||
.is(Aggregate.class)
|
||||
.goAggregate(0).isStandardMethod(StandardMethod.SUM)
|
||||
.goExpression().isBinary(BinaryOperatorKind.MUL)
|
||||
.left().goPath().first().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false)
|
||||
.goUpFilterValidator().root()
|
||||
.right().goPath().first().isComplexProperty("PropertyComp", ComplexTypeProvider.nameCTPrimComp, false)
|
||||
.n().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
|
||||
parse("ESTwoKeyNav",
|
||||
"aggregate(NavPropertyETKeyNavMany(PropertyInt16 mul NavPropertyETTwoKeyNavOne/PropertyInt16 with sum as s))")
|
||||
.goAggregate(0)
|
||||
.goInlineAggregateExpression().isStandardMethod(StandardMethod.SUM)
|
||||
.goUpAggregate()
|
||||
.goPath().first().isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true);
|
||||
|
||||
parseEx("ESTwoKeyNav", "aggregate((PropertyInt16 mul 2 with sum as s))")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aggregateCount() throws Exception {
|
||||
parse("ESTwoKeyNav", "aggregate($count as count)")
|
||||
.is(Aggregate.class)
|
||||
.goAggregate(0).goPath().first().isCount();
|
||||
|
||||
parseEx("ESTwoKeyNav", "aggregate($count)").isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
parseEx("ESTwoKeyNav", "aggregate($count with sum as count)")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aggregateFrom() throws Exception {
|
||||
parse("ESTwoKeyNav", "aggregate(PropertyInt16 with sum as s from CollPropertyComp with average)")
|
||||
.goAggregate(0).isStandardMethod(StandardMethod.SUM)
|
||||
.goFrom(0).isStandardMethod(StandardMethod.AVERAGE)
|
||||
.goExpression().goPath().first()
|
||||
.isComplexProperty("CollPropertyComp", ComplexTypeProvider.nameCTPrimComp, true);
|
||||
parse("ESTwoKeyNav",
|
||||
"aggregate(PropertyInt16 with sum as s from CollPropertyComp with average from CollPropertyString with max)")
|
||||
.goAggregate(0).isStandardMethod(StandardMethod.SUM)
|
||||
.goFrom(0).isStandardMethod(StandardMethod.AVERAGE)
|
||||
.goUpAggregate().goFrom(1).isStandardMethod(StandardMethod.MAX);
|
||||
parse("ESTwoKeyNav", "aggregate(customAggregate as a from CollPropertyComp with average)")
|
||||
.goAggregate(0).goFrom(0).isStandardMethod(StandardMethod.AVERAGE);
|
||||
|
||||
parseEx("ESTwoKeyNav", "aggregate(PropertyInt16 as a from CollPropertyComp with average)")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
parseEx("ESTwoKeyNav", "aggregate(PropertyInt16 with sum from CollPropertyComp with average)")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void identity() throws Exception {
|
||||
parse("ESTwoKeyNav", "identity").is(Identity.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compute() throws Exception {
|
||||
parse("ESTwoKeyNav", "compute(PropertyInt16 mul NavPropertyETKeyNavOne/PropertyInt16 as p)")
|
||||
.is(Compute.class)
|
||||
.goCompute(0).isAlias("p").goExpression().isBinary(BinaryOperatorKind.MUL)
|
||||
.left().isMember().goPath().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
parse("ESTwoKeyNav", "compute(PropertyInt16 mul 2 as p,day(now()) as d)")
|
||||
.goCompute(0).isAlias("p")
|
||||
.goUp().goCompute(1).isAlias("d")
|
||||
.goExpression().isMethod(MethodKind.DAY, 1).goParameter(0).isMethod(MethodKind.NOW, 0);
|
||||
|
||||
parseEx("ESTwoKeyNav", "compute(PropertyInt16)")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
parseEx("ESTwoKeyNav", "compute(PropertyComp as c)")
|
||||
.isExSemantic(UriParserSemanticException.MessageKeys.ONLY_FOR_PRIMITIVE_TYPES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void concat() throws Exception {
|
||||
parse("ESTwoKeyNav", "concat(topcount(2,PropertyInt16),bottomcount(2,PropertyInt16))")
|
||||
.is(Concat.class)
|
||||
.goConcat(0).goBottomTop().isMethod(Method.TOP_COUNT)
|
||||
.goUp().goUp()
|
||||
.goConcat(1).goBottomTop().isMethod(Method.BOTTOM_COUNT).goNumber().isLiteral("2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expand() throws Exception {
|
||||
parse("ESTwoKeyNav", "expand(NavPropertyETKeyNavMany,filter(PropertyInt16 gt 2))")
|
||||
.is(Expand.class).goExpand()
|
||||
.goPath().first().isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
|
||||
.goUpExpandValidator().isFilterSerialized("<<PropertyInt16> gt <2>>");
|
||||
parse("ESTwoKeyNav",
|
||||
"expand(NavPropertyETKeyNavMany,expand(NavPropertyETTwoKeyNavMany,filter(PropertyInt16 gt 2)))")
|
||||
.is(Expand.class).goExpand().goExpand().isFilterSerialized("<<PropertyInt16> gt <2>>");
|
||||
parse("ESTwoKeyNav",
|
||||
"expand(NavPropertyETKeyNavMany,expand(NavPropertyETTwoKeyNavMany,filter(PropertyInt16 gt 2)),"
|
||||
+ "expand(NavPropertyETTwoKeyNavOne,expand(NavPropertyETKeyNavMany)))")
|
||||
.is(Expand.class).goExpand().goExpand().next().goExpand()
|
||||
.goPath().first().isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void search() throws Exception {
|
||||
parse("ESTwoKeyNav", "search(String)").isSearch("'String'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filter() throws Exception {
|
||||
parse("ESTwoKeyNav", "filter(PropertyInt16 gt 3)")
|
||||
.is(Filter.class)
|
||||
.goFilter().isBinary(BinaryOperatorKind.GT)
|
||||
.left().isMember().goPath().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bottomTop() throws Exception {
|
||||
parse("ESTwoKeyNav", "topcount(2,PropertyInt16)")
|
||||
.goBottomTop().isMethod(Method.TOP_COUNT)
|
||||
.goNumber().isLiteralType(odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.SByte))
|
||||
.isLiteral("2");
|
||||
parse("ESTwoKeyNav", "topsum(2,PropertyInt16)")
|
||||
.goBottomTop().isMethod(Method.TOP_SUM)
|
||||
.goValue().isMember().goPath().first().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
parse("ESTwoKeyNav", "toppercent(2,PropertyInt16)").goBottomTop().isMethod(Method.TOP_PERCENT);
|
||||
|
||||
parse("ESTwoKeyNav", "bottomcount(2,PropertyInt16)").goBottomTop().isMethod(Method.BOTTOM_COUNT);
|
||||
parse("ESTwoKeyNav", "bottomsum(2,PropertyInt16)").goBottomTop().isMethod(Method.BOTTOM_SUM);
|
||||
parse("ESTwoKeyNav", "bottompercent(2,PropertyInt16)").goBottomTop().isMethod(Method.BOTTOM_PERCENT);
|
||||
|
||||
parseEx("ESTwoKeyNav", "bottompercent(1.2,PropertyInt16)")
|
||||
.isExSemantic(UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE);
|
||||
parseEx("ESTwoKeyNav", "bottompercent(2,PropertyString)")
|
||||
.isExSemantic(UriParserSemanticException.MessageKeys.TYPES_NOT_COMPATIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customFunction() throws Exception {
|
||||
parse("ESBaseTwoKeyNav", "Namespace1_Alias.BFCESBaseTwoKeyNavRTESBaseTwoKey()")
|
||||
.isCustomFunction(FunctionProvider.nameBFCESBaseTwoKeyNavRTESBaseTwoKey);
|
||||
parse("ESKeyNav(1)/CollPropertyComp", "Namespace1_Alias.BFCCollCTPrimCompRTESAllPrim()")
|
||||
.isCustomFunction(FunctionProvider.nameBFCCollCTPrimCompRTESAllPrim);
|
||||
|
||||
parseEx("ESBaseTwoKeyNav", "BFCESBaseTwoKeyNavRTESBaseTwoKey()")
|
||||
.isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
|
||||
parseEx("ESBaseTwoKeyNav", "Namespace1_Alias.BFCETBaseTwoKeyNavRTETTwoKeyNav()")
|
||||
.isExSemantic(UriParserSemanticException.MessageKeys.FUNCTION_NOT_FOUND);
|
||||
parseEx("ESBaseTwoKeyNav", "Namespace1_Alias.BFCCollStringRTESTwoKeyNav()")
|
||||
.isExSemantic(UriParserSemanticException.MessageKeys.FUNCTION_NOT_FOUND);
|
||||
parseEx("ESTwoKeyNav", "Namespace1_Alias.BFCESTwoKeyNavRTTwoKeyNav()")
|
||||
.isExSemantic(UriParserSemanticException.MessageKeys.FUNCTION_MUST_USE_COLLECTIONS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupBy() throws Exception {
|
||||
parse("ESTwoKeyNav", "groupby((PropertyString))")
|
||||
.is(GroupBy.class)
|
||||
.goGroupBy(0).goPath().first().isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
|
||||
parse("ESTwoKeyNav", "groupby((NavPropertyETKeyNavOne/PropertyInt16))")
|
||||
.is(GroupBy.class)
|
||||
.goGroupBy(0).goPath().first().isNavProperty("NavPropertyETKeyNavOne", EntityTypeProvider.nameETKeyNav, false)
|
||||
.n().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
parse("ESTwoKeyNav", "groupby((NavPropertyETKeyNavOne/PropertyInt16,PropertyString))")
|
||||
.is(GroupBy.class)
|
||||
.goGroupBy(1).goPath().first().isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
|
||||
parse("ESTwoKeyNav", "groupby((NavPropertyETKeyNavOne/PropertyInt16,NavPropertyETKeyNavOne/PropertyString))");
|
||||
parse("ESTwoKeyNav",
|
||||
"groupby((NavPropertyETKeyNavOne/PropertyInt16,NavPropertyETKeyNavOne/PropertyString,PropertyString))")
|
||||
.goGroupBy(2).goPath().first().isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupByAggregate() throws Exception {
|
||||
parse("ESTwoKeyNav", "groupby((PropertyInt16),aggregate(PropertyInt16 with sum as s))")
|
||||
.goGroupByOption().goAggregate(0).isStandardMethod(StandardMethod.SUM)
|
||||
.goExpression().goPath().first().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
parse("ESTwoKeyNav",
|
||||
"groupby((NavPropertyETKeyNavOne/PropertyInt16),aggregate(PropertyInt16 with average as a))")
|
||||
.goGroupByOption().goAggregate(0).isStandardMethod(StandardMethod.AVERAGE)
|
||||
.goUp().goUp().goGroupBy(0).goPath()
|
||||
.first().isNavProperty("NavPropertyETKeyNavOne", EntityTypeProvider.nameETKeyNav, false)
|
||||
.n().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
|
||||
parse("ESTwoKeyNav", "groupby((NavPropertyETKeyNavOne/PropertyInt16),"
|
||||
+ "aggregate(PropertyInt16 with sum as s,PropertyInt16 with average as a))")
|
||||
.goGroupByOption().goAggregate(1).isStandardMethod(StandardMethod.AVERAGE);
|
||||
parse("ESTwoKeyNav", "groupby((PropertyInt16),aggregate(NavPropertyETKeyNavMany/$count as c))")
|
||||
.goGroupByOption().goAggregate(0).goPath().at(1).isCount();
|
||||
parse("ESTwoKeyNav", "groupby((PropertyString),aggregate(NavPropertyETKeyNavMany(PropertyInt16 with sum as s)))")
|
||||
.goGroupByOption().goAggregate(0).goInlineAggregateExpression().isStandardMethod(StandardMethod.SUM)
|
||||
.goUpAggregate().goPath().first()
|
||||
.isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true);
|
||||
parse("ESTwoKeyNav",
|
||||
"groupby((NavPropertyETKeyNavOne/PropertyInt16,NavPropertyETKeyNavOne/PropertyString),"
|
||||
+ "aggregate(PropertyInt16 with sum as s))")
|
||||
.goGroupBy(1).goPath().at(1).isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
|
||||
parse("ESTwoKeyNav",
|
||||
"groupby((NavPropertyETKeyNavOne/PropertyInt16,NavPropertyETKeyNavOne/PropertyString),"
|
||||
+ "aggregate(PropertyInt16 with sum as s from NavPropertyETKeyNavOne/PropertyInt16 with average))")
|
||||
.goGroupByOption().goAggregate(0).goFrom(0).isStandardMethod(StandardMethod.AVERAGE);
|
||||
parse("ESTwoKeyNav", "groupby((NavPropertyETKeyNavOne),aggregate(CollPropertyComp(PropertyInt16 with sum as s)))");
|
||||
parse("ESTwoKeyNav",
|
||||
"groupby((NavPropertyETTwoKeyNavOne/PropertyInt16),"
|
||||
+ "topcount(2,PropertyInt16)/aggregate(PropertyInt16 with sum as s))")
|
||||
.goGroupByOption()
|
||||
.at(0).goBottomTop().isMethod(Method.TOP_COUNT)
|
||||
.goUp().at(1).goAggregate(0).isStandardMethod(StandardMethod.SUM);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupByRollUp() throws Exception {
|
||||
parse("ESTwoKeyNav",
|
||||
"groupby((rollup(NavPropertyETKeyNavOne/PropertyInt16,NavPropertyETKeyNavOne/PropertyString),"
|
||||
+ "rollup(NavPropertyETKeyNavOne/NavPropertyETTwoKeyNavOne/PropertyInt16,"
|
||||
+ "NavPropertyETTwoKeyNavOne/PropertyString),NavPropertyETTwoKeyNavOne/PropertyInt16),"
|
||||
+ "aggregate(PropertyInt16 with sum as s))")
|
||||
.goGroupBy(1).goRollup(1).goPath()
|
||||
.first().isNavProperty("NavPropertyETTwoKeyNavOne", EntityTypeProvider.nameETTwoKeyNav, false)
|
||||
.n().isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
|
||||
|
||||
parse("ESTwoKeyNav",
|
||||
"groupby((rollup($all,NavPropertyETKeyNavOne/PropertyInt16,NavPropertyETKeyNavOne/PropertyString),"
|
||||
+ "NavPropertyETTwoKeyNavOne/PropertyString),"
|
||||
+ "aggregate(PropertyInt16 with sum as s from NavPropertyETTwoKeyNavOne/PropertyInt16 with average "
|
||||
+ "from NavPropertyETTwoKeyNavOne/PropertyString with average))")
|
||||
.goGroupBy(0).isRollupAll().goUp().goGroupByOption().goAggregate(0).goFrom(1)
|
||||
.isStandardMethod(StandardMethod.AVERAGE).goExpression().goPath().at(1)
|
||||
.isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupBySpecial() throws Exception {
|
||||
parse("ESTwoKeyNav", "groupby((NavPropertyETTwoKeyNavOne/PropertyInt16),aggregate(customAggregate))")
|
||||
.is(GroupBy.class)
|
||||
.goGroupByOption().goAggregate(0)
|
||||
.goPath().first().isUriPathInfoKind(UriResourceKind.primitiveProperty);
|
||||
|
||||
parse("ESTwoKeyNav",
|
||||
"groupby((PropertyString),aggregate(NavPropertyETKeyNavMany/$count as c,"
|
||||
+ "NavPropertyETKeyNavMany(PropertyInt16 with sum as s)))")
|
||||
.is(GroupBy.class)
|
||||
.goGroupByOption().goAggregate(0).isAlias("c").goPath().at(1).isCount();
|
||||
parse("ESTwoKeyNav",
|
||||
"groupby((PropertyString),aggregate(NavPropertyETKeyNavMany($count as c),"
|
||||
+ "NavPropertyETKeyNavMany(PropertyInt16 with sum as s)))")
|
||||
.is(GroupBy.class)
|
||||
.goGroupByOption().goAggregate(0).goInlineAggregateExpression()
|
||||
.isAlias("c").goPath().first().isCount();
|
||||
parse("ESTwoKeyNav",
|
||||
"groupby((NavPropertyETKeyNavOne/PropertyString),"
|
||||
+ "aggregate(PropertyInt16 with sum as s,customAggregate))")
|
||||
.is(GroupBy.class)
|
||||
.goGroupByOption().goAggregate(1).isStandardMethod(null).isAlias(null)
|
||||
.goPath().first().isUriPathInfoKind(UriResourceKind.primitiveProperty);
|
||||
parse("ESTwoKeyNav",
|
||||
"groupby((PropertyString),aggregate(NavPropertyETKeyNavMany(PropertyInt16 with sum as s),"
|
||||
+ "NavPropertyETKeyNavMany/customAggregate))")
|
||||
.is(GroupBy.class)
|
||||
.goGroupByOption().goAggregate(1)
|
||||
.goPath().at(1).isUriPathInfoKind(UriResourceKind.primitiveProperty);
|
||||
parse("ESTwoKeyNav",
|
||||
"groupby((PropertyString),aggregate(NavPropertyETKeyNavMany(PropertyInt16 with sum as s),"
|
||||
+ "NavPropertyETKeyNavMany(PropertyInt16 with average as a)))")
|
||||
.is(GroupBy.class)
|
||||
.goGroupByOption().goAggregate(0).goInlineAggregateExpression()
|
||||
.isStandardMethod(StandardMethod.SUM).isAlias("s")
|
||||
.goUpAggregate().goUp().goAggregate(1).goInlineAggregateExpression()
|
||||
.isStandardMethod(StandardMethod.AVERAGE).isAlias("a");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sequence() throws Exception {
|
||||
parse("ESTwoKeyNav", "identity/identity/identity")
|
||||
.at(0).is(Identity.class).at(1).is(Identity.class).at(2).is(Identity.class);
|
||||
|
||||
parse("ESTwoKeyNav", "filter(PropertyInt16 le 1)/aggregate(PropertyInt16 with sum as s)")
|
||||
.at(0).is(Filter.class)
|
||||
.at(1).is(Aggregate.class).goAggregate(0).isStandardMethod(StandardMethod.SUM).isAlias("s");
|
||||
parse("ESTwoKeyNav",
|
||||
"groupby((NavPropertyETKeyNavOne),aggregate(PropertyInt16 with sum as s))/"
|
||||
+ "aggregate(s with average as a)")
|
||||
.at(1).goAggregate(0).isStandardMethod(StandardMethod.AVERAGE).isAlias("a");
|
||||
parse("ESTwoKeyNav",
|
||||
"filter(PropertyInt16 ge 1)/"
|
||||
+ "groupby((NavPropertyETKeyNavOne/PropertyString),aggregate(PropertyInt16 with sum as s))")
|
||||
.at(0).is(Filter.class)
|
||||
.at(1).is(GroupBy.class);
|
||||
parse("ESTwoKeyNav",
|
||||
"groupby((NavPropertyETKeyNavOne/PropertyString),aggregate(PropertyInt16 with sum as s))/"
|
||||
+ "filter(s ge 10)/concat(identity,groupby((NavPropertyETKeyNavOne/PropertyString),"
|
||||
+ "aggregate(s with sum as t)))")
|
||||
.at(0).is(GroupBy.class)
|
||||
.at(1).is(Filter.class)
|
||||
.at(2).is(Concat.class).goConcat(0).is(Identity.class);
|
||||
parse("ESTwoKeyNav",
|
||||
"groupby((NavPropertyETKeyNavOne/PropertyString),aggregate(PropertyInt16 with sum as s))/"
|
||||
+ "filter(s ge 10)/groupby((rollup(NavPropertyETKeyNavOne/PropertyString,"
|
||||
+ "NavPropertyETKeyNavOne/PropertyCompAllPrim/PropertyDuration)),aggregate(s with sum as t))")
|
||||
.at(0).is(GroupBy.class)
|
||||
.at(1).is(Filter.class)
|
||||
.at(2).is(GroupBy.class).goGroupBy(0).goRollup(1).goPath().at(2)
|
||||
.isPrimitiveProperty("PropertyDuration", PropertyProvider.nameDuration, false);
|
||||
parse("ESTwoKeyNav",
|
||||
"groupby((NavPropertyETKeyNavOne/PropertyString),aggregate(PropertyInt16 with sum as s))/"
|
||||
+ "concat(filter(s ge 10),groupby((NavPropertyETKeyNavOne/PropertyString),"
|
||||
+ "aggregate(s with sum as t)))")
|
||||
.at(0).is(GroupBy.class)
|
||||
.at(1).is(Concat.class).goConcat(1).is(GroupBy.class);
|
||||
|
||||
parse("ESTwoKeyNav",
|
||||
"filter(PropertyInt16 eq 1)/expand(NavPropertyETKeyNavMany,filter(not PropertyCompAllPrim/PropertyBoolean))/"
|
||||
+ "groupby((NavPropertyETKeyNavOne/PropertyInt16),"
|
||||
+ "aggregate(NavPropertyETKeyNavMany(PropertyInt16 with sum as s)))")
|
||||
.at(0).is(Filter.class)
|
||||
.at(1).is(Expand.class)
|
||||
.at(2).is(GroupBy.class);
|
||||
}
|
||||
|
||||
private ApplyValidator parse(final String path, final String apply)
|
||||
throws UriParserException, UriValidationException {
|
||||
final UriInfo uriInfo = new Parser(edm, odata).parseUri(path, "$apply=" + apply, null);
|
||||
return new ApplyValidator(uriInfo.getApplyOption());
|
||||
}
|
||||
|
||||
private TestUriValidator parseEx(final String path, final String apply) {
|
||||
return new TestUriValidator().setEdm(edm).runEx(path, "$apply=" + apply);
|
||||
}
|
||||
|
||||
private final class ApplyValidator implements TestValidator {
|
||||
|
||||
private final ApplyOption applyOption;
|
||||
private final ApplyValidator previous;
|
||||
private ApplyItem applyItem;
|
||||
|
||||
protected ApplyValidator(final ApplyOption applyOption) {
|
||||
this(applyOption, null);
|
||||
}
|
||||
|
||||
private ApplyValidator(final ApplyOption applyOption, final ApplyValidator previous) {
|
||||
this.applyOption = applyOption;
|
||||
this.previous = previous;
|
||||
at(0);
|
||||
}
|
||||
|
||||
public ApplyValidator at(final int index) {
|
||||
assertTrue(index < applyOption.getApplyItems().size());
|
||||
applyItem = applyOption.getApplyItems().get(index);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApplyValidator is(final Class<? extends ApplyItem> cls) {
|
||||
assertNotNull(applyItem);
|
||||
assertTrue(cls.isAssignableFrom(applyItem.getClass()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public AggregateValidator goAggregate(final int index) {
|
||||
is(Aggregate.class);
|
||||
assertTrue(index < ((Aggregate) applyItem).getExpressions().size());
|
||||
return new AggregateValidator(((Aggregate) applyItem).getExpressions().get(index), this);
|
||||
}
|
||||
|
||||
public ExpandValidator goExpand() {
|
||||
is(Expand.class);
|
||||
return new ExpandValidator().setUpValidator(this).setExpand(((Expand) applyItem).getExpandOption());
|
||||
}
|
||||
|
||||
public FilterValidator goFilter() {
|
||||
is(Filter.class);
|
||||
return new FilterValidator().setFilter(((Filter) applyItem).getFilterOption());
|
||||
}
|
||||
|
||||
public BottomTopValidator goBottomTop() {
|
||||
is(BottomTop.class);
|
||||
return new BottomTopValidator((BottomTop) applyItem, this);
|
||||
}
|
||||
|
||||
public ApplyValidator isCustomFunction(final FullQualifiedName function) {
|
||||
is(CustomFunction.class);
|
||||
assertEquals(function, ((CustomFunction) applyItem).getFunction().getFullQualifiedName());
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApplyValidator isSearch(final String serializedSearch) {
|
||||
is(Search.class);
|
||||
assertEquals(serializedSearch, ((Search) applyItem).getSearchOption().getSearchExpression().toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApplyValidator goConcat(final int index) {
|
||||
is(Concat.class);
|
||||
assertTrue(index < ((Concat) applyItem).getApplyOptions().size());
|
||||
return new ApplyValidator(((Concat) applyItem).getApplyOptions().get(index), this);
|
||||
}
|
||||
|
||||
public ComputeValidator goCompute(final int index) {
|
||||
is(Compute.class);
|
||||
assertTrue(index < ((Compute) applyItem).getExpressions().size());
|
||||
return new ComputeValidator(((Compute) applyItem).getExpressions().get(index), this);
|
||||
}
|
||||
|
||||
public GroupByValidator goGroupBy(final int index) {
|
||||
is(GroupBy.class);
|
||||
assertTrue(index < ((GroupBy) applyItem).getGroupByItems().size());
|
||||
return new GroupByValidator(((GroupBy) applyItem).getGroupByItems().get(index), this);
|
||||
}
|
||||
|
||||
public ApplyValidator goGroupByOption() {
|
||||
is(GroupBy.class);
|
||||
assertNotNull(((GroupBy) applyItem).getApplyOption());
|
||||
return new ApplyValidator(((GroupBy) applyItem).getApplyOption(), this);
|
||||
}
|
||||
|
||||
public ApplyValidator goUp() {
|
||||
return previous;
|
||||
}
|
||||
}
|
||||
|
||||
private final class AggregateValidator implements TestValidator {
|
||||
|
||||
private final AggregateExpression aggregateExpression;
|
||||
private final TestValidator previous;
|
||||
|
||||
protected AggregateValidator(final AggregateExpression aggregateExpression, final TestValidator previous) {
|
||||
this.aggregateExpression = aggregateExpression;
|
||||
this.previous = previous;
|
||||
}
|
||||
|
||||
public AggregateValidator isStandardMethod(final AggregateExpression.StandardMethod method) {
|
||||
assertNotNull(aggregateExpression);
|
||||
assertEquals(method, aggregateExpression.getStandardMethod());
|
||||
return this;
|
||||
}
|
||||
|
||||
public AggregateValidator isCustomMethod(final FullQualifiedName method) {
|
||||
assertNotNull(aggregateExpression);
|
||||
assertEquals(method, aggregateExpression.getCustomMethod());
|
||||
return this;
|
||||
}
|
||||
|
||||
public AggregateValidator isAlias(final String alias) {
|
||||
assertNotNull(aggregateExpression);
|
||||
assertEquals(alias, aggregateExpression.getAlias());
|
||||
return this;
|
||||
}
|
||||
|
||||
public FilterValidator goExpression() {
|
||||
assertNotNull(aggregateExpression);
|
||||
assertNotNull(aggregateExpression.getExpression());
|
||||
return new FilterValidator().setValidator(this).setEdm(edm)
|
||||
.setExpression(aggregateExpression.getExpression());
|
||||
}
|
||||
|
||||
public ResourceValidator goPath() {
|
||||
assertNotNull(aggregateExpression);
|
||||
assertFalse(aggregateExpression.getPath().isEmpty());
|
||||
UriInfoImpl resource = new UriInfoImpl().setKind(UriInfoKind.resource);
|
||||
for (final UriResource segment : aggregateExpression.getPath()) {
|
||||
resource.addResourcePart(segment);
|
||||
}
|
||||
return new ResourceValidator().setUpValidator(this).setEdm(edm).setUriInfoPath(resource);
|
||||
}
|
||||
|
||||
public AggregateValidator goInlineAggregateExpression() {
|
||||
return new AggregateValidator(aggregateExpression.getInlineAggregateExpression(), this);
|
||||
}
|
||||
|
||||
public AggregateValidator goFrom(final int index) {
|
||||
assertTrue(index < aggregateExpression.getFrom().size());
|
||||
return new AggregateValidator(aggregateExpression.getFrom().get(index), this);
|
||||
}
|
||||
|
||||
public AggregateValidator goUpAggregate() {
|
||||
return (AggregateValidator) previous;
|
||||
}
|
||||
|
||||
public ApplyValidator goUp() {
|
||||
return (ApplyValidator) previous;
|
||||
}
|
||||
}
|
||||
|
||||
private final class BottomTopValidator implements TestValidator {
|
||||
|
||||
private final BottomTop item;
|
||||
private final ApplyValidator previous;
|
||||
|
||||
private BottomTopValidator(final BottomTop item, final ApplyValidator previous) {
|
||||
this.item = item;
|
||||
this.previous = previous;
|
||||
}
|
||||
|
||||
public BottomTopValidator isMethod(final BottomTop.Method method) {
|
||||
assertEquals(method, item.getMethod());
|
||||
return this;
|
||||
}
|
||||
|
||||
public FilterValidator goNumber() {
|
||||
assertNotNull(item.getNumber());
|
||||
return new FilterValidator().setValidator(this).setEdm(edm).setExpression(item.getNumber());
|
||||
}
|
||||
|
||||
public FilterValidator goValue() {
|
||||
assertNotNull(item.getValue());
|
||||
return new FilterValidator().setValidator(this).setEdm(edm).setExpression(item.getValue());
|
||||
}
|
||||
|
||||
public ApplyValidator goUp() {
|
||||
return previous;
|
||||
}
|
||||
}
|
||||
|
||||
private final class ComputeValidator implements TestValidator {
|
||||
|
||||
private final ComputeExpression item;
|
||||
private final ApplyValidator previous;
|
||||
|
||||
private ComputeValidator(final ComputeExpression item, final ApplyValidator previous) {
|
||||
this.item = item;
|
||||
this.previous = previous;
|
||||
}
|
||||
|
||||
public ComputeValidator isAlias(final String alias) {
|
||||
assertEquals(alias, item.getAlias());
|
||||
return this;
|
||||
}
|
||||
|
||||
public FilterValidator goExpression() {
|
||||
assertNotNull(item.getExpression());
|
||||
return new FilterValidator().setValidator(this).setEdm(edm).setExpression(item.getExpression());
|
||||
}
|
||||
|
||||
public ApplyValidator goUp() {
|
||||
return previous;
|
||||
}
|
||||
}
|
||||
|
||||
private final class GroupByValidator implements TestValidator {
|
||||
|
||||
private final GroupByItem item;
|
||||
private final TestValidator previous;
|
||||
|
||||
private GroupByValidator(final GroupByItem item, final TestValidator previous) {
|
||||
this.item = item;
|
||||
this.previous = previous;
|
||||
}
|
||||
|
||||
public ResourceValidator goPath() {
|
||||
assertFalse(item.getPath().isEmpty());
|
||||
UriInfoImpl resource = new UriInfoImpl().setKind(UriInfoKind.resource);
|
||||
for (final UriResource segment : item.getPath()) {
|
||||
resource.addResourcePart(segment);
|
||||
}
|
||||
return new ResourceValidator().setUpValidator(this).setEdm(edm).setUriInfoPath(resource);
|
||||
}
|
||||
|
||||
public GroupByValidator isRollupAll() {
|
||||
assertTrue(item.isRollupAll());
|
||||
return this;
|
||||
}
|
||||
|
||||
public GroupByValidator goRollup(final int index) {
|
||||
assertTrue(index < item.getRollup().size());
|
||||
return new GroupByValidator(item.getRollup().get(index), this);
|
||||
}
|
||||
|
||||
public ApplyValidator goUp() {
|
||||
return (ApplyValidator) previous;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -88,16 +88,17 @@ public class UriValidatorTest {
|
|||
private static final String QO_SKIP = "$skip=3";
|
||||
private static final String QO_SKIPTOKEN = "$skiptoken=123";
|
||||
private static final String QO_TOP = "$top=1";
|
||||
private static final String QO_APPLY = "$apply=identity";
|
||||
|
||||
private final String[][] urisWithValidSystemQueryOptions = {
|
||||
{ URI_ALL, QO_FILTER }, { URI_ALL, QO_FORMAT }, { URI_ALL, QO_EXPAND }, { URI_ALL, QO_COUNT },
|
||||
{ URI_ALL, QO_ORDERBY }, { URI_ALL, QO_SEARCH }, { URI_ALL, QO_SELECT }, { URI_ALL, QO_SKIP },
|
||||
{ URI_ALL, QO_SKIPTOKEN }, { URI_ALL, QO_TOP },
|
||||
{ URI_ALL, QO_SKIPTOKEN }, { URI_ALL, QO_TOP }, { URI_ALL, QO_APPLY },
|
||||
|
||||
{ URI_CROSSJOIN, QO_FILTER }, { URI_CROSSJOIN, QO_FORMAT },
|
||||
{ URI_CROSSJOIN, QO_EXPAND }, { URI_CROSSJOIN, QO_COUNT }, { URI_CROSSJOIN, QO_ORDERBY },
|
||||
{ URI_CROSSJOIN, QO_SEARCH }, { URI_CROSSJOIN, QO_SELECT }, { URI_CROSSJOIN, QO_SKIP },
|
||||
{ URI_CROSSJOIN, QO_SKIPTOKEN }, { URI_CROSSJOIN, QO_TOP },
|
||||
{ URI_CROSSJOIN, QO_SKIPTOKEN }, { URI_CROSSJOIN, QO_TOP }, { URI_CROSSJOIN, QO_APPLY },
|
||||
|
||||
{ URI_ENTITY_ID, QO_ID, QO_FORMAT }, { URI_ENTITY_ID, QO_ID }, { URI_ENTITY_ID, QO_ID, QO_EXPAND },
|
||||
{ URI_ENTITY_ID, QO_ID, QO_SELECT },
|
||||
|
@ -109,7 +110,7 @@ public class UriValidatorTest {
|
|||
{ URI_ENTITY_SET, QO_FILTER }, { URI_ENTITY_SET, QO_FORMAT }, { URI_ENTITY_SET, QO_EXPAND },
|
||||
{ URI_ENTITY_SET, QO_COUNT }, { URI_ENTITY_SET, QO_ORDERBY }, { URI_ENTITY_SET, QO_SEARCH },
|
||||
{ URI_ENTITY_SET, QO_SELECT }, { URI_ENTITY_SET, QO_SKIP }, { URI_ENTITY_SET, QO_SKIPTOKEN },
|
||||
{ URI_ENTITY_SET, QO_TOP },
|
||||
{ URI_ENTITY_SET, QO_TOP }, { URI_ENTITY_SET, QO_APPLY },
|
||||
|
||||
{ URI_ENTITY_SET_COUNT, QO_FILTER }, { URI_ENTITY_SET_COUNT, QO_SEARCH },
|
||||
|
||||
|
@ -127,7 +128,7 @@ public class UriValidatorTest {
|
|||
{ URI_PROPERTY_COMPLEX_COLLECTION, QO_EXPAND }, { URI_PROPERTY_COMPLEX_COLLECTION, QO_COUNT },
|
||||
{ URI_PROPERTY_COMPLEX_COLLECTION, QO_ORDERBY }, { URI_PROPERTY_COMPLEX_COLLECTION, QO_SELECT },
|
||||
{ URI_PROPERTY_COMPLEX_COLLECTION, QO_SKIP }, { URI_PROPERTY_COMPLEX_COLLECTION, QO_SKIPTOKEN },
|
||||
{ URI_PROPERTY_COMPLEX_COLLECTION, QO_TOP },
|
||||
{ URI_PROPERTY_COMPLEX_COLLECTION, QO_TOP }, { URI_PROPERTY_COMPLEX_COLLECTION, QO_APPLY },
|
||||
|
||||
{ URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_FILTER },
|
||||
|
||||
|
@ -149,12 +150,12 @@ public class UriValidatorTest {
|
|||
{ URI_NAV_ENTITY_SET, QO_FILTER }, { URI_NAV_ENTITY_SET, QO_FORMAT }, { URI_NAV_ENTITY_SET, QO_EXPAND },
|
||||
{ URI_NAV_ENTITY_SET, QO_COUNT }, { URI_NAV_ENTITY_SET, QO_ORDERBY },
|
||||
{ URI_NAV_ENTITY_SET, QO_SEARCH }, { URI_NAV_ENTITY_SET, QO_SELECT }, { URI_NAV_ENTITY_SET, QO_SKIP },
|
||||
{ URI_NAV_ENTITY_SET, QO_SKIPTOKEN }, { URI_NAV_ENTITY_SET, QO_TOP },
|
||||
{ URI_NAV_ENTITY_SET, QO_SKIPTOKEN }, { URI_NAV_ENTITY_SET, QO_TOP }, { URI_NAV_ENTITY_SET, QO_APPLY },
|
||||
|
||||
{ URI_FI_ENTITY_SET, QO_FILTER }, { URI_FI_ENTITY_SET, QO_FORMAT }, { URI_FI_ENTITY_SET, QO_EXPAND },
|
||||
{ URI_FI_ENTITY_SET, QO_COUNT }, { URI_FI_ENTITY_SET, QO_ORDERBY }, { URI_FI_ENTITY_SET, QO_SEARCH },
|
||||
{ URI_FI_ENTITY_SET, QO_SELECT }, { URI_FI_ENTITY_SET, QO_SKIP }, { URI_FI_ENTITY_SET, QO_SKIPTOKEN },
|
||||
{ URI_FI_ENTITY_SET, QO_TOP },
|
||||
{ URI_FI_ENTITY_SET, QO_TOP }, { URI_FI_ENTITY_SET, QO_APPLY },
|
||||
|
||||
{ URI_FI_ENTITY, QO_FORMAT }, { URI_FI_ENTITY, QO_EXPAND }, { URI_FI_ENTITY, QO_SELECT },
|
||||
{ URI_FI_ENTITY_SET_KEY, QO_FORMAT }, { URI_FI_ENTITY_SET_KEY, QO_EXPAND }, { URI_FI_ENTITY_SET_KEY, QO_SELECT },
|
||||
|
@ -170,22 +171,23 @@ public class UriValidatorTest {
|
|||
|
||||
{ URI_BATCH, QO_FILTER }, { URI_BATCH, QO_FORMAT }, { URI_BATCH, QO_ID }, { URI_BATCH, QO_EXPAND },
|
||||
{ URI_BATCH, QO_COUNT }, { URI_BATCH, QO_ORDERBY }, { URI_BATCH, QO_SEARCH }, { URI_BATCH, QO_SELECT },
|
||||
{ URI_BATCH, QO_SKIP }, { URI_BATCH, QO_SKIPTOKEN }, { URI_BATCH, QO_TOP },
|
||||
{ URI_BATCH, QO_SKIP }, { URI_BATCH, QO_SKIPTOKEN }, { URI_BATCH, QO_TOP }, { URI_BATCH, QO_APPLY },
|
||||
|
||||
{ URI_CROSSJOIN, QO_ID },
|
||||
|
||||
{ URI_ENTITY_ID, QO_ID, QO_FILTER },
|
||||
{ URI_ENTITY_ID, QO_ID, QO_COUNT }, { URI_ENTITY_ID, QO_ORDERBY }, { URI_ENTITY_ID, QO_SEARCH },
|
||||
{ URI_ENTITY_ID, QO_ID, QO_COUNT }, { URI_ENTITY_ID, QO_ID, QO_ORDERBY }, { URI_ENTITY_ID, QO_ID, QO_SEARCH },
|
||||
{ URI_ENTITY_ID, QO_ID, QO_SKIP }, { URI_ENTITY_ID, QO_ID, QO_SKIPTOKEN }, { URI_ENTITY_ID, QO_ID, QO_TOP },
|
||||
{ URI_ENTITY_ID, QO_ID, QO_APPLY },
|
||||
|
||||
{ URI_METADATA, QO_FILTER }, { URI_METADATA, QO_ID }, { URI_METADATA, QO_EXPAND },
|
||||
{ URI_METADATA, QO_COUNT }, { URI_METADATA, QO_ORDERBY }, { URI_METADATA, QO_SEARCH },
|
||||
{ URI_METADATA, QO_SELECT }, { URI_METADATA, QO_SKIP }, { URI_METADATA, QO_SKIPTOKEN },
|
||||
{ URI_METADATA, QO_TOP },
|
||||
{ URI_METADATA, QO_TOP }, { URI_METADATA, QO_APPLY },
|
||||
|
||||
{ URI_SERVICE, QO_FILTER }, { URI_SERVICE, QO_ID }, { URI_SERVICE, QO_EXPAND }, { URI_SERVICE, QO_COUNT },
|
||||
{ URI_SERVICE, QO_ORDERBY }, { URI_SERVICE, QO_SEARCH }, { URI_SERVICE, QO_SELECT },
|
||||
{ URI_SERVICE, QO_SKIP }, { URI_SERVICE, QO_SKIPTOKEN }, { URI_SERVICE, QO_TOP },
|
||||
{ URI_SERVICE, QO_SKIP }, { URI_SERVICE, QO_SKIPTOKEN }, { URI_SERVICE, QO_TOP }, { URI_SERVICE, QO_APPLY },
|
||||
|
||||
{ URI_ENTITY_SET, QO_ID },
|
||||
|
||||
|
@ -193,26 +195,29 @@ public class UriValidatorTest {
|
|||
{ URI_ENTITY_SET_COUNT, QO_EXPAND }, { URI_ENTITY_SET_COUNT, QO_COUNT },
|
||||
{ URI_ENTITY_SET_COUNT, QO_ORDERBY },
|
||||
{ URI_ENTITY_SET_COUNT, QO_SELECT }, { URI_ENTITY_SET_COUNT, QO_SKIP }, { URI_ENTITY_SET_COUNT, QO_SKIPTOKEN },
|
||||
{ URI_ENTITY_SET_COUNT, QO_TOP },
|
||||
{ URI_ENTITY_SET_COUNT, QO_TOP }, { URI_ENTITY_SET_COUNT, QO_APPLY },
|
||||
|
||||
{ URI_ENTITY, QO_FILTER }, { URI_ENTITY, QO_ID }, { URI_ENTITY, QO_COUNT }, { URI_ENTITY, QO_ORDERBY },
|
||||
{ URI_ENTITY, QO_SEARCH }, { URI_ENTITY, QO_SKIP }, { URI_ENTITY, QO_SKIPTOKEN }, { URI_ENTITY, QO_TOP },
|
||||
{ URI_ENTITY, QO_APPLY },
|
||||
|
||||
{ URI_MEDIA_STREAM, QO_FILTER }, { URI_MEDIA_STREAM, QO_FORMAT }, { URI_MEDIA_STREAM, QO_ID },
|
||||
{ URI_MEDIA_STREAM, QO_EXPAND }, { URI_MEDIA_STREAM, QO_COUNT }, { URI_MEDIA_STREAM, QO_ORDERBY },
|
||||
{ URI_MEDIA_STREAM, QO_SEARCH }, { URI_MEDIA_STREAM, QO_SELECT }, { URI_MEDIA_STREAM, QO_SKIP },
|
||||
{ URI_MEDIA_STREAM, QO_SKIPTOKEN }, { URI_MEDIA_STREAM, QO_TOP },
|
||||
{ URI_MEDIA_STREAM, QO_SKIPTOKEN }, { URI_MEDIA_STREAM, QO_TOP }, { URI_MEDIA_STREAM, QO_APPLY },
|
||||
|
||||
{ URI_REFERENCES, QO_ID }, { URI_REFERENCES, QO_EXPAND }, { URI_REFERENCES, QO_SELECT },
|
||||
{ URI_REFERENCES, QO_APPLY },
|
||||
|
||||
{ URI_REFERENCE, QO_FILTER }, { URI_REFERENCE, QO_ID }, { URI_REFERENCE, QO_EXPAND },
|
||||
{ URI_REFERENCE, QO_COUNT }, { URI_REFERENCE, QO_ORDERBY }, { URI_REFERENCE, QO_SEARCH },
|
||||
{ URI_REFERENCE, QO_SELECT }, { URI_REFERENCE, QO_SKIP }, { URI_REFERENCE, QO_SKIPTOKEN },
|
||||
{ URI_REFERENCE, QO_TOP },
|
||||
{ URI_REFERENCE, QO_TOP }, { URI_REFERENCE, QO_APPLY },
|
||||
|
||||
{ URI_PROPERTY_COMPLEX, QO_FILTER }, { URI_PROPERTY_COMPLEX, QO_ID }, { URI_PROPERTY_COMPLEX, QO_COUNT },
|
||||
{ URI_PROPERTY_COMPLEX, QO_ORDERBY }, { URI_PROPERTY_COMPLEX, QO_SEARCH },
|
||||
{ URI_PROPERTY_COMPLEX, QO_SKIP }, { URI_PROPERTY_COMPLEX, QO_SKIPTOKEN }, { URI_PROPERTY_COMPLEX, QO_TOP },
|
||||
{ URI_PROPERTY_COMPLEX, QO_APPLY },
|
||||
|
||||
{ URI_PROPERTY_COMPLEX_COLLECTION, QO_ID }, { URI_PROPERTY_COMPLEX_COLLECTION, QO_SEARCH },
|
||||
|
||||
|
@ -221,16 +226,17 @@ public class UriValidatorTest {
|
|||
{ URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_COUNT }, { URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_ORDERBY },
|
||||
{ URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_SEARCH }, { URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_SELECT },
|
||||
{ URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_SKIP }, { URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_SKIPTOKEN },
|
||||
{ URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_TOP },
|
||||
{ URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_TOP }, { URI_PROPERTY_COMPLEX_COLLECTION_COUNT, QO_APPLY },
|
||||
|
||||
{ URI_PROPERTY_PRIMITIVE, QO_FILTER }, { URI_PROPERTY_PRIMITIVE, QO_ID }, { URI_PROPERTY_PRIMITIVE, QO_EXPAND },
|
||||
{ URI_PROPERTY_PRIMITIVE, QO_COUNT }, { URI_PROPERTY_PRIMITIVE, QO_ORDERBY },
|
||||
{ URI_PROPERTY_PRIMITIVE, QO_SEARCH }, { URI_PROPERTY_PRIMITIVE, QO_SELECT },
|
||||
{ URI_PROPERTY_PRIMITIVE, QO_SKIP }, { URI_PROPERTY_PRIMITIVE, QO_SKIPTOKEN },
|
||||
{ URI_PROPERTY_PRIMITIVE, QO_TOP },
|
||||
{ URI_PROPERTY_PRIMITIVE, QO_TOP }, { URI_PROPERTY_PRIMITIVE, QO_APPLY },
|
||||
|
||||
{ URI_PROPERTY_PRIMITIVE_COLLECTION, QO_ID }, { URI_PROPERTY_PRIMITIVE_COLLECTION, QO_EXPAND },
|
||||
{ URI_PROPERTY_PRIMITIVE_COLLECTION, QO_SEARCH }, { URI_PROPERTY_PRIMITIVE_COLLECTION, QO_SELECT },
|
||||
{ URI_PROPERTY_PRIMITIVE_COLLECTION, QO_APPLY },
|
||||
|
||||
{ URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_FORMAT },
|
||||
{ URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_ID }, { URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_EXPAND },
|
||||
|
@ -238,20 +244,22 @@ public class UriValidatorTest {
|
|||
{ URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_ORDERBY }, { URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_SEARCH },
|
||||
{ URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_SELECT }, { URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_SKIP },
|
||||
{ URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_SKIPTOKEN }, { URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_TOP },
|
||||
{ URI_PROPERTY_PRIMITIVE_COLLECTION_COUNT, QO_APPLY },
|
||||
|
||||
{ URI_PROPERTY_PRIMITIVE_VALUE, QO_FILTER }, { URI_PROPERTY_PRIMITIVE_VALUE, QO_ID },
|
||||
{ URI_PROPERTY_PRIMITIVE_VALUE, QO_EXPAND }, { URI_PROPERTY_PRIMITIVE_VALUE, QO_COUNT },
|
||||
{ URI_PROPERTY_PRIMITIVE_VALUE, QO_ORDERBY }, { URI_PROPERTY_PRIMITIVE_VALUE, QO_SEARCH },
|
||||
{ URI_PROPERTY_PRIMITIVE_VALUE, QO_SELECT }, { URI_PROPERTY_PRIMITIVE_VALUE, QO_SKIP },
|
||||
{ URI_PROPERTY_PRIMITIVE_VALUE, QO_SKIPTOKEN }, { URI_PROPERTY_PRIMITIVE_VALUE, QO_TOP },
|
||||
{ URI_PROPERTY_PRIMITIVE_VALUE, QO_APPLY },
|
||||
|
||||
{ URI_SINGLETON, QO_FILTER }, { URI_SINGLETON, QO_ID }, { URI_SINGLETON, QO_COUNT },
|
||||
{ URI_SINGLETON, QO_ORDERBY }, { URI_SINGLETON, QO_SEARCH }, { URI_SINGLETON, QO_SKIP },
|
||||
{ URI_SINGLETON, QO_SKIPTOKEN }, { URI_SINGLETON, QO_TOP },
|
||||
{ URI_SINGLETON, QO_SKIPTOKEN }, { URI_SINGLETON, QO_TOP }, { URI_SINGLETON, QO_APPLY },
|
||||
|
||||
{ URI_NAV_ENTITY, QO_FILTER }, { URI_NAV_ENTITY, QO_ID }, { URI_NAV_ENTITY, QO_COUNT },
|
||||
{ URI_NAV_ENTITY, QO_ORDERBY }, { URI_NAV_ENTITY, QO_SEARCH }, { URI_NAV_ENTITY, QO_SKIP },
|
||||
{ URI_NAV_ENTITY, QO_SKIPTOKEN }, { URI_SINGLETON, QO_TOP },
|
||||
{ URI_NAV_ENTITY, QO_SKIPTOKEN }, { URI_NAV_ENTITY, QO_TOP }, { URI_NAV_ENTITY, QO_APPLY },
|
||||
|
||||
{ URI_NAV_ENTITY_SET, QO_ID },
|
||||
|
||||
|
@ -262,7 +270,8 @@ public class UriValidatorTest {
|
|||
{ URI_FI_ENTITY, QO_SKIPTOKEN }, { URI_FI_ENTITY, QO_TOP },
|
||||
{ URI_FI_ENTITY_SET_KEY, QO_FILTER }, { URI_FI_ENTITY_SET_KEY, QO_ID }, { URI_FI_ENTITY_SET_KEY, QO_COUNT },
|
||||
{ URI_FI_ENTITY_SET_KEY, QO_ORDERBY }, { URI_FI_ENTITY_SET_KEY, QO_SEARCH },
|
||||
{ URI_FI_ENTITY_SET_KEY, QO_SKIP }, { URI_FI_ENTITY_SET_KEY, QO_SKIPTOKEN }, { URI_FI_ENTITY_SET_KEY, QO_TOP }
|
||||
{ URI_FI_ENTITY_SET_KEY, QO_SKIP }, { URI_FI_ENTITY_SET_KEY, QO_SKIPTOKEN }, { URI_FI_ENTITY_SET_KEY, QO_TOP },
|
||||
{ URI_FI_ENTITY_SET_KEY, QO_APPLY }
|
||||
};
|
||||
|
||||
private final String[][] actionWithValidSystemQueryOptions = {
|
||||
|
@ -282,14 +291,14 @@ public class UriValidatorTest {
|
|||
{ URI_ACTION_COLL_CT, QO_EXPAND }, { URI_ACTION_COLL_CT, QO_COUNT },
|
||||
{ URI_ACTION_COLL_CT, QO_ORDERBY }, { URI_ACTION_COLL_CT, QO_SELECT },
|
||||
{ URI_ACTION_COLL_CT, QO_SKIP }, { URI_ACTION_COLL_CT, QO_SKIPTOKEN },
|
||||
{ URI_ACTION_COLL_CT, QO_TOP },
|
||||
{ URI_ACTION_COLL_CT, QO_TOP }, { URI_ACTION_COLL_CT, QO_APPLY },
|
||||
// EntityReturnType
|
||||
{ URI_ACTION_ENTITY, QO_FORMAT }, { URI_ACTION_ENTITY, QO_EXPAND }, { URI_ACTION_ENTITY, QO_SELECT },
|
||||
// EntityCollectionReturnType
|
||||
{ URI_ACTION_ES, QO_FORMAT }, { URI_ACTION_ES, QO_FILTER },
|
||||
{ URI_ACTION_ES, QO_COUNT }, { URI_ACTION_ES, QO_ORDERBY }, { URI_ACTION_ES, QO_SEARCH },
|
||||
{ URI_ACTION_ES, QO_SELECT }, { URI_ACTION_ES, QO_SKIP }, { URI_ACTION_ES, QO_SKIPTOKEN },
|
||||
{ URI_ACTION_ES, QO_TOP }
|
||||
{ URI_ACTION_ES, QO_TOP }, { URI_ACTION_ES, QO_APPLY }
|
||||
};
|
||||
|
||||
private final String[][] actionsWithNotValidSystemQueryOptions = {
|
||||
|
@ -298,26 +307,28 @@ public class UriValidatorTest {
|
|||
{ URI_ACTION_NO_RETURN, QO_EXPAND }, { URI_ACTION_NO_RETURN, QO_COUNT },
|
||||
{ URI_ACTION_NO_RETURN, QO_ORDERBY }, { URI_ACTION_NO_RETURN, QO_SEARCH },
|
||||
{ URI_ACTION_NO_RETURN, QO_SELECT }, { URI_ACTION_NO_RETURN, QO_SKIP },
|
||||
{ URI_ACTION_NO_RETURN, QO_SKIPTOKEN }, { URI_ACTION_NO_RETURN, QO_TOP },
|
||||
{ URI_ACTION_NO_RETURN, QO_SKIPTOKEN }, { URI_ACTION_NO_RETURN, QO_TOP }, { URI_ACTION_NO_RETURN, QO_APPLY },
|
||||
// PrimReturnType
|
||||
{ URI_ACTION_PRIM, QO_FILTER }, { URI_ACTION_PRIM, QO_ID },
|
||||
{ URI_ACTION_PRIM, QO_EXPAND }, { URI_ACTION_PRIM, QO_COUNT },
|
||||
{ URI_ACTION_PRIM, QO_ORDERBY }, { URI_ACTION_PRIM, QO_SEARCH },
|
||||
{ URI_ACTION_PRIM, QO_SELECT }, { URI_ACTION_PRIM, QO_SKIP },
|
||||
{ URI_ACTION_PRIM, QO_SKIPTOKEN }, { URI_ACTION_PRIM, QO_TOP },
|
||||
{ URI_ACTION_PRIM, QO_SKIPTOKEN }, { URI_ACTION_PRIM, QO_TOP }, { URI_ACTION_PRIM, QO_APPLY },
|
||||
// PrimCollectionReturnType
|
||||
{ URI_ACTION_COLL_PRIM, QO_ID }, { URI_ACTION_COLL_PRIM, QO_EXPAND },
|
||||
{ URI_ACTION_COLL_PRIM, QO_SEARCH }, { URI_ACTION_COLL_PRIM, QO_SELECT },
|
||||
{ URI_ACTION_COLL_PRIM, QO_SEARCH }, { URI_ACTION_COLL_PRIM, QO_SELECT }, { URI_ACTION_COLL_PRIM, QO_APPLY },
|
||||
// ComplexReturnType
|
||||
{ URI_ACTION_CT, QO_FILTER }, { URI_ACTION_CT, QO_ID }, { URI_ACTION_CT, QO_COUNT },
|
||||
{ URI_ACTION_CT, QO_ORDERBY }, { URI_ACTION_CT, QO_SEARCH },
|
||||
{ URI_ACTION_CT, QO_SKIP }, { URI_ACTION_CT, QO_SKIPTOKEN }, { URI_ACTION_CT, QO_TOP },
|
||||
{ URI_ACTION_CT, QO_APPLY },
|
||||
// ComplexCollectionReturnType
|
||||
{ URI_ACTION_COLL_CT, QO_ID }, { URI_ACTION_COLL_CT, QO_SEARCH },
|
||||
// EntityReturnType
|
||||
{ URI_ACTION_ENTITY, QO_FILTER }, { URI_ACTION_ENTITY, QO_ID }, { URI_ACTION_ENTITY, QO_COUNT },
|
||||
{ URI_ACTION_ENTITY, QO_ORDERBY }, { URI_ACTION_ENTITY, QO_SEARCH },
|
||||
{ URI_ACTION_ENTITY, QO_SKIP }, { URI_ACTION_ENTITY, QO_SKIPTOKEN }, { URI_ACTION_ENTITY, QO_TOP },
|
||||
{ URI_ACTION_ENTITY, QO_APPLY },
|
||||
// EntityCollectionReturnType
|
||||
{ URI_ACTION_ES, QO_ID }
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue