Query.java
/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2016 ForgeRock AS.
*/
package org.forgerock.api.models;
import static org.forgerock.api.util.ValidationUtil.*;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.wrensecurity.guava.common.base.Strings;
import org.forgerock.api.ApiValidationException;
import org.forgerock.api.enums.CountPolicy;
import org.forgerock.api.enums.PagingMode;
import org.forgerock.api.enums.QueryType;
/**
* Class that represents the Create Operation type in API descriptor.
*/
@JsonDeserialize(builder = Query.Builder.class)
public final class Query extends Operation implements Comparable<Query> {
private final QueryType type;
private final PagingMode[] pagingModes;
private final CountPolicy[] countPolicies;
private final String queryId;
private final String[] queryableFields;
private final String[] supportedSortKeys;
private Query(Builder builder) {
super(builder);
this.type = builder.type;
this.pagingModes = builder.pagingModes;
this.countPolicies = builder.countPolicies;
this.queryId = builder.queryId;
this.queryableFields = builder.queryableFields;
this.supportedSortKeys = builder.supportedSortKeys;
if (type == null) {
throw new ApiValidationException("type is required");
}
if (type == QueryType.ID && isEmpty(queryId)) {
throw new ApiValidationException("queryId required for type = ID");
}
}
/**
* Getter of the query type.
*
* @return Query type num
*/
public QueryType getType() {
return type;
}
/**
* Getter of the paging modes.
*
* @return Paging mode enums
*/
public PagingMode[] getPagingModes() {
return pagingModes;
}
/**
* Getter of the supported paging policies.
* If the array is empty, this means that the query does not support any form of count policy, and no value for
* count policy should be specified.
*
* @return Supported paging policy enums
*/
public CountPolicy[] getCountPolicies() {
return countPolicies;
}
/**
* Getter of the query id.
*
* @return Query id
*/
public String getQueryId() {
return queryId;
}
/**
* Getter of the queryable fields.
*
* @return Queryable fields
*/
public String[] getQueryableFields() {
return queryableFields;
}
/**
* Getter of the supported sort keys.
*
* @return Supported sort keys
*/
public String[] getSupportedSortKeys() {
return supportedSortKeys;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
Query query = (Query) o;
return type == query.type
&& Arrays.equals(pagingModes, query.pagingModes)
&& Arrays.equals(countPolicies, query.countPolicies)
&& Objects.equals(queryId, query.queryId)
&& Arrays.equals(queryableFields, query.queryableFields)
&& Arrays.equals(supportedSortKeys, query.supportedSortKeys);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), type, pagingModes, countPolicies, queryId, queryableFields,
supportedSortKeys);
}
/**
* Creates a new builder for Query.
*
* @return New builder instance
*/
public static Builder query() {
return new Builder();
}
/**
* Allocates the Query operation type to the given Resource Builder.
*
* @param resourceBuilder - Resource Builder to add the operation
*/
@Override
protected void allocateToResource(Resource.Builder resourceBuilder) {
resourceBuilder.query(this);
}
/**
* Builds a Query object from the data stored in the annotation.
*
* @param query The annotation that stores the data.
* @param annotated The method that the annotation was found on.
* @param descriptor The root descriptor to add definitions to.
* @param relativeType The type relative to which schema resources should be resolved.
* @return Query instance
*/
public static Query fromAnnotation(org.forgerock.api.annotations.Query query, Method annotated,
ApiDescription descriptor, Class<?> relativeType) {
String queryId = query.id();
if (query.type() == QueryType.ID && Strings.isNullOrEmpty(queryId)) {
if (annotated == null) {
throw new IllegalArgumentException("Query is missing ID: " + query);
}
queryId = annotated.getName();
}
return query()
.detailsFromAnnotation(query.operationDescription(), descriptor, relativeType)
.type(query.type())
.pagingModes(query.pagingModes())
.countPolicies(query.countPolicies())
.queryId(queryId)
.queryableFields(query.queryableFields())
.supportedSortKeys(query.sortKeys())
.build();
}
/**
* Compares two queries.
*
* @param query Query to compare to
* @return the value {@code 0} if the argument string is equal to
* this string; a value less than {@code 0} if this string
* is lexicographically less than the string argument; and a
* value greater than {@code 0} if this string is
* lexicographically greater than the string argument.
*/
@Override
public int compareTo(final Query query) {
// Sort Order: EXPRESSION, FILTER, ID
// @Checkstyle:off
switch (query.getType()) {
case EXPRESSION:
if (this.getType() == QueryType.EXPRESSION) {
return 0;
}
return 1;
case FILTER:
if (this.getType() == QueryType.FILTER) {
return 0;
}
if (this.getType() == QueryType.EXPRESSION) {
return -1;
}
return 1;
case ID:
if (this.getType() == QueryType.ID) {
return this.queryId.compareTo(query.getQueryId());
}
return -1;
default:
throw new IllegalStateException("Unsupported QueryType: " + query.getType());
}
// @Checkstyle:on
}
/**
* Builder to help construct the Read.
*/
public static final class Builder extends Operation.Builder<Builder> {
private QueryType type;
private PagingMode[] pagingModes;
private CountPolicy[] countPolicies;
private String queryId;
private String[] queryableFields;
private String[] supportedSortKeys;
/**
* Returns the builder instance.
*
* @return Builder
*/
@Override
protected Builder self() {
return this;
}
/**
* Set the query type.
*
* @param type query type enum
* @return Builder
*/
@JsonProperty("type")
public Builder type(QueryType type) {
this.type = type;
return this;
}
/**
* Set the paging mode.
*
* @param pagingMode Query paging mode enum
* @return Builder
*/
@JsonProperty("pagingModes")
public Builder pagingModes(PagingMode... pagingMode) {
this.pagingModes = pagingMode;
return this;
}
/**
* Set the supported page count policies.
* If the array is empty, this means that the query does not support any form of count policy, and no value
* for count policy should be specified.
*
* @param countPolicy Array of supported paging mode policies
* @return Builder
*/
@JsonProperty("countPolicies")
public Builder countPolicies(CountPolicy... countPolicy) {
this.countPolicies = countPolicy;
return this;
}
/**
* Set the query id. Required if “type” is ID.
*
* @param queryId Query id
* @return Builder
*/
@JsonProperty("queryId")
public Builder queryId(String queryId) {
this.queryId = queryId;
return this;
}
/**
* Set the queryable fields.
*
* @param queryableFields Array of the fileds that are queryable
* @return Builder
*/
@JsonProperty("queryableFields")
public Builder queryableFields(String... queryableFields) {
this.queryableFields = queryableFields;
return this;
}
/**
* Set the supported sort keys.
*
* @param supportedSortKeys Array of supported sort keys
* @return Builder
*/
@JsonProperty("supportedSortKeys")
public Builder supportedSortKeys(String... supportedSortKeys) {
this.supportedSortKeys = supportedSortKeys;
return this;
}
/**
* Builds the Query instance.
*
* @return Query instance
*/
public Query build() {
return new Query(this);
}
}
}