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}