001/* 002 * Licensed under the Apache License, Version 2.0 (the "License"); 003 * you may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at 005 * 006 * http://www.apache.org/licenses/LICENSE-2.0 007 * 008 * Unless required by applicable law or agreed to in writing, software 009 * distributed under the License is distributed on an "AS IS" BASIS, 010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 011 * See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014package org.gbif.api.model.common.search; 015 016import com.fasterxml.jackson.annotation.JsonSubTypes; 017import com.fasterxml.jackson.annotation.JsonTypeInfo; 018import com.fasterxml.jackson.core.JsonParser; 019import com.fasterxml.jackson.core.JsonToken; 020import com.fasterxml.jackson.databind.DeserializationContext; 021import com.fasterxml.jackson.databind.JsonDeserializer; 022import com.fasterxml.jackson.databind.JsonNode; 023import com.fasterxml.jackson.databind.ObjectMapper; 024import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 025import java.io.IOException; 026import java.util.Optional; 027import org.gbif.api.model.event.search.EventSearchParameter; 028import org.gbif.api.model.occurrence.search.OccurrenceSearchParameter; 029 030/** 031 * Interface to be implemented by all search parameter enumerations. The enumeration member name 032 * should avoid the following names which are already defined as standard (faceted) search 033 * parameters: 034 * 035 * <ul> 036 * <li>limit 037 * <li>offset 038 * <li>q 039 * <li>hl 040 * <li>language 041 * <li>facet 042 * </ul> 043 */ 044public interface SearchParameter { 045 046 String name(); 047 048 Class<?> type(); 049 050 // This deserializer is used only when the type property is not set. Otherwise it uses the custom 051 // deserializers of each type 052 class SearchParameterDeserializer extends JsonDeserializer<SearchParameter> { 053 054 @Override 055 public SearchParameter deserialize(JsonParser p, DeserializationContext ctx) 056 throws IOException { 057 JsonToken token = p.currentToken(); 058 059 if (token == JsonToken.VALUE_STRING) { 060 String raw = p.getText(); 061 Optional<SearchParameter> opt = fromString(raw); 062 if (opt.isPresent()) { 063 return opt.get(); 064 } 065 } 066 067 // If parameter is an object, try to use the "name" property or map directly 068 ObjectMapper mapper = (ObjectMapper) p.getCodec(); 069 JsonNode node = mapper.readTree(p); 070 if (node.isObject()) { 071 JsonNode nameNode = node.get("name"); 072 if (nameNode != null && nameNode.isTextual()) { 073 String name = nameNode.asText(); 074 Optional<SearchParameter> opt = fromString(name); 075 if (opt.isPresent()) { 076 return opt.get(); 077 } 078 } 079 } 080 081 return null; 082 } 083 084 private Optional<SearchParameter> fromString(String value) { 085 if (value == null || value.isEmpty()) { 086 return Optional.empty(); 087 } 088 089 String name = value.trim(); 090 091 // Try occurrence first 092 Optional<OccurrenceSearchParameter> occ = OccurrenceSearchParameter.lookup(name); 093 if (occ.isPresent()) { 094 return occ.map(o -> o); 095 } 096 097 // Then event 098 return EventSearchParameter.lookupEventParam(name).map(v -> v); 099 } 100 } 101}