001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2016 ForgeRock AS. 015 */ 016 017package org.forgerock.api.models; 018 019import static org.forgerock.api.util.ValidationUtil.*; 020 021import java.lang.reflect.Method; 022import java.util.Arrays; 023import java.util.Objects; 024 025import com.fasterxml.jackson.annotation.JsonProperty; 026import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 027import org.wrensecurity.guava.common.base.Strings; 028 029import org.forgerock.api.ApiValidationException; 030import org.forgerock.api.enums.CountPolicy; 031import org.forgerock.api.enums.PagingMode; 032import org.forgerock.api.enums.QueryType; 033 034/** 035 * Class that represents the Create Operation type in API descriptor. 036 */ 037@JsonDeserialize(builder = Query.Builder.class) 038public final class Query extends Operation implements Comparable<Query> { 039 040 private final QueryType type; 041 private final PagingMode[] pagingModes; 042 private final CountPolicy[] countPolicies; 043 private final String queryId; 044 private final String[] queryableFields; 045 private final String[] supportedSortKeys; 046 047 private Query(Builder builder) { 048 super(builder); 049 this.type = builder.type; 050 this.pagingModes = builder.pagingModes; 051 this.countPolicies = builder.countPolicies; 052 this.queryId = builder.queryId; 053 this.queryableFields = builder.queryableFields; 054 this.supportedSortKeys = builder.supportedSortKeys; 055 056 if (type == null) { 057 throw new ApiValidationException("type is required"); 058 } 059 if (type == QueryType.ID && isEmpty(queryId)) { 060 throw new ApiValidationException("queryId required for type = ID"); 061 } 062 } 063 064 /** 065 * Getter of the query type. 066 * 067 * @return Query type num 068 */ 069 public QueryType getType() { 070 return type; 071 } 072 073 /** 074 * Getter of the paging modes. 075 * 076 * @return Paging mode enums 077 */ 078 public PagingMode[] getPagingModes() { 079 return pagingModes; 080 } 081 082 /** 083 * Getter of the supported paging policies. 084 * If the array is empty, this means that the query does not support any form of count policy, and no value for 085 * count policy should be specified. 086 * 087 * @return Supported paging policy enums 088 */ 089 public CountPolicy[] getCountPolicies() { 090 return countPolicies; 091 } 092 093 /** 094 * Getter of the query id. 095 * 096 * @return Query id 097 */ 098 public String getQueryId() { 099 return queryId; 100 } 101 102 /** 103 * Getter of the queryable fields. 104 * 105 * @return Queryable fields 106 */ 107 public String[] getQueryableFields() { 108 return queryableFields; 109 } 110 111 /** 112 * Getter of the supported sort keys. 113 * 114 * @return Supported sort keys 115 */ 116 public String[] getSupportedSortKeys() { 117 return supportedSortKeys; 118 } 119 120 @Override 121 public boolean equals(Object o) { 122 if (this == o) { 123 return true; 124 } 125 if (o == null || getClass() != o.getClass()) { 126 return false; 127 } 128 if (!super.equals(o)) { 129 return false; 130 } 131 Query query = (Query) o; 132 return type == query.type 133 && Arrays.equals(pagingModes, query.pagingModes) 134 && Arrays.equals(countPolicies, query.countPolicies) 135 && Objects.equals(queryId, query.queryId) 136 && Arrays.equals(queryableFields, query.queryableFields) 137 && Arrays.equals(supportedSortKeys, query.supportedSortKeys); 138 } 139 140 @Override 141 public int hashCode() { 142 return Objects.hash(super.hashCode(), type, pagingModes, countPolicies, queryId, queryableFields, 143 supportedSortKeys); 144 } 145 146 /** 147 * Creates a new builder for Query. 148 * 149 * @return New builder instance 150 */ 151 public static Builder query() { 152 return new Builder(); 153 } 154 155 /** 156 * Allocates the Query operation type to the given Resource Builder. 157 * 158 * @param resourceBuilder - Resource Builder to add the operation 159 */ 160 @Override 161 protected void allocateToResource(Resource.Builder resourceBuilder) { 162 resourceBuilder.query(this); 163 } 164 165 /** 166 * Builds a Query object from the data stored in the annotation. 167 * 168 * @param query The annotation that stores the data. 169 * @param annotated The method that the annotation was found on. 170 * @param descriptor The root descriptor to add definitions to. 171 * @param relativeType The type relative to which schema resources should be resolved. 172 * @return Query instance 173 */ 174 public static Query fromAnnotation(org.forgerock.api.annotations.Query query, Method annotated, 175 ApiDescription descriptor, Class<?> relativeType) { 176 String queryId = query.id(); 177 if (query.type() == QueryType.ID && Strings.isNullOrEmpty(queryId)) { 178 if (annotated == null) { 179 throw new IllegalArgumentException("Query is missing ID: " + query); 180 } 181 queryId = annotated.getName(); 182 } 183 return query() 184 .detailsFromAnnotation(query.operationDescription(), descriptor, relativeType) 185 .type(query.type()) 186 .pagingModes(query.pagingModes()) 187 .countPolicies(query.countPolicies()) 188 .queryId(queryId) 189 .queryableFields(query.queryableFields()) 190 .supportedSortKeys(query.sortKeys()) 191 .build(); 192 } 193 194 /** 195 * Compares two queries. 196 * 197 * @param query Query to compare to 198 * @return the value {@code 0} if the argument string is equal to 199 * this string; a value less than {@code 0} if this string 200 * is lexicographically less than the string argument; and a 201 * value greater than {@code 0} if this string is 202 * lexicographically greater than the string argument. 203 */ 204 @Override 205 public int compareTo(final Query query) { 206 // Sort Order: EXPRESSION, FILTER, ID 207 // @Checkstyle:off 208 switch (query.getType()) { 209 case EXPRESSION: 210 if (this.getType() == QueryType.EXPRESSION) { 211 return 0; 212 } 213 return 1; 214 case FILTER: 215 if (this.getType() == QueryType.FILTER) { 216 return 0; 217 } 218 if (this.getType() == QueryType.EXPRESSION) { 219 return -1; 220 } 221 return 1; 222 case ID: 223 if (this.getType() == QueryType.ID) { 224 return this.queryId.compareTo(query.getQueryId()); 225 } 226 return -1; 227 default: 228 throw new IllegalStateException("Unsupported QueryType: " + query.getType()); 229 } 230 // @Checkstyle:on 231 } 232 233 /** 234 * Builder to help construct the Read. 235 */ 236 public static final class Builder extends Operation.Builder<Builder> { 237 238 private QueryType type; 239 private PagingMode[] pagingModes; 240 private CountPolicy[] countPolicies; 241 private String queryId; 242 private String[] queryableFields; 243 private String[] supportedSortKeys; 244 245 /** 246 * Returns the builder instance. 247 * 248 * @return Builder 249 */ 250 @Override 251 protected Builder self() { 252 return this; 253 } 254 255 /** 256 * Set the query type. 257 * 258 * @param type query type enum 259 * @return Builder 260 */ 261 @JsonProperty("type") 262 public Builder type(QueryType type) { 263 this.type = type; 264 return this; 265 } 266 267 /** 268 * Set the paging mode. 269 * 270 * @param pagingMode Query paging mode enum 271 * @return Builder 272 */ 273 @JsonProperty("pagingModes") 274 public Builder pagingModes(PagingMode... pagingMode) { 275 this.pagingModes = pagingMode; 276 return this; 277 } 278 279 /** 280 * Set the supported page count policies. 281 * If the array is empty, this means that the query does not support any form of count policy, and no value 282 * for count policy should be specified. 283 * 284 * @param countPolicy Array of supported paging mode policies 285 * @return Builder 286 */ 287 @JsonProperty("countPolicies") 288 public Builder countPolicies(CountPolicy... countPolicy) { 289 this.countPolicies = countPolicy; 290 return this; 291 } 292 293 /** 294 * Set the query id. Required if “type” is ID. 295 * 296 * @param queryId Query id 297 * @return Builder 298 */ 299 @JsonProperty("queryId") 300 public Builder queryId(String queryId) { 301 this.queryId = queryId; 302 return this; 303 } 304 305 /** 306 * Set the queryable fields. 307 * 308 * @param queryableFields Array of the fileds that are queryable 309 * @return Builder 310 */ 311 @JsonProperty("queryableFields") 312 public Builder queryableFields(String... queryableFields) { 313 this.queryableFields = queryableFields; 314 return this; 315 } 316 317 /** 318 * Set the supported sort keys. 319 * 320 * @param supportedSortKeys Array of supported sort keys 321 * @return Builder 322 */ 323 @JsonProperty("supportedSortKeys") 324 public Builder supportedSortKeys(String... supportedSortKeys) { 325 this.supportedSortKeys = supportedSortKeys; 326 return this; 327 } 328 329 /** 330 * Builds the Query instance. 331 * 332 * @return Query instance 333 */ 334 public Query build() { 335 return new Query(this); 336 } 337 } 338 339}