001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.bean.swagger;
018
019import static org.apache.juneau.common.utils.Utils.*;
020import static org.apache.juneau.internal.CollectionUtils.*;
021
022import java.util.*;
023
024import org.apache.juneau.common.utils.*;
025
026/**
027 * Map meant for method-name/operation mappings.
028 *
029 * <p>
030 * The OperationMap is a specialized TreeMap that represents the operations available on a single path in Swagger 2.0. 
031 * It forces entries to be sorted in a specific order to ensure consistent output. This map is used within PathItem 
032 * objects to define the HTTP methods and their corresponding operations.
033 *
034 * <h5 class='section'>Swagger Specification:</h5>
035 * <p>
036 * The OperationMap represents the operations field in a Path Item Object, where each key is an HTTP method and each 
037 * value is an Operation object. The supported HTTP methods are:
038 * <ul class='spaced-list'>
039 *    <li><c>get</c> ({@link Operation}) - A definition of a GET operation
040 *    <li><c>put</c> ({@link Operation}) - A definition of a PUT operation
041 *    <li><c>post</c> ({@link Operation}) - A definition of a POST operation
042 *    <li><c>delete</c> ({@link Operation}) - A definition of a DELETE operation
043 *    <li><c>options</c> ({@link Operation}) - A definition of an OPTIONS operation
044 *    <li><c>head</c> ({@link Operation}) - A definition of a HEAD operation
045 *    <li><c>patch</c> ({@link Operation}) - A definition of a PATCH operation
046 * </ul>
047 *
048 * <p>
049 * Forces entries to be sorted in the following order:
050 * <ul>
051 *    <li><c>GET</c>
052 *    <li><c>PUT</c>
053 *    <li><c>POST</c>
054 *    <li><c>DELETE</c>
055 *    <li><c>OPTIONS</c>
056 *    <li><c>HEAD</c>
057 *    <li><c>PATCH</c>
058 *    <li>Everything else.
059 * </ul>
060 *
061 * <h5 class='section'>Example:</h5>
062 * <p class='bjava'>
063 *    <jc>// Construct using SwaggerBuilder.</jc>
064 *    OperationMap <jv>operations</jv> = <jsm>operationMap</jsm>()
065 *       .append(<js>"get"</js>, <jsm>operation</jsm>().setSummary(<js>"Get users"</js>))
066 *       .append(<js>"post"</jsm>, <jsm>operation</jsm>().setSummary(<js>"Create user"</js>));
067 *
068 *    <jc>// Serialize using JsonSerializer.</jc>
069 *    String <jv>json</jv> = Json.<jsm>from</jsm>(<jv>operations</jv>);
070 *
071 *    <jc>// Or just use toString() which does the same as above.</jc>
072 *    <jv>json</jv> = <jv>operations</jv>.toString();
073 * </p>
074 * <p class='bjson'>
075 *    <jc>// Output</jc>
076 *    {
077 *       <js>"get"</js>: { <js>"summary"</js>: <js>"Get users"</js> },
078 *       <js>"post"</js>: { <js>"summary"</js>: <js>"Create user"</js> }
079 *    }
080 * </p>
081 *
082 * <h5 class='section'>See Also:</h5><ul>
083 *    <li class='link'><a class="doclink" href="https://swagger.io/specification/v2/#path-item-object">Swagger 2.0 Specification &gt; Path Item Object</a>
084 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/2-0/paths-and-operations/">Swagger Paths and Operations</a>
085 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanSwagger2">juneau-bean-swagger-v2</a>
086 * </ul>
087 *
088 * @serial exclude
089 */
090public class OperationMap extends TreeMap<String,Operation> {
091   private static final long serialVersionUID = 1L;
092
093   private static final Comparator<String> OP_SORTER = new Comparator<>() {
094      private final Map<String,String> methods = mapBuilder(String.class,String.class)
095         .add("get","0").add("put","1").add("post","2").add("delete","3").add("options","4").add("head","5").add("patch","6")
096         .build();
097
098      @Override
099      public int compare(String o1, String o2) {
100         // Since keys are now stored in lowercase, we need to normalize them for comparison
101         var s1 = methods.get(emptyIfNull(o1).toLowerCase());
102         var s2 = methods.get(emptyIfNull(o2).toLowerCase());
103         if (s1 == null)
104            s1 = emptyIfNull(o1).toLowerCase();
105         if (s2 == null)
106            s2 = emptyIfNull(o2).toLowerCase();
107         return StringUtils.compare(s1, s2);
108      }
109   };
110
111   /**
112    * Constructor.
113    */
114   public OperationMap() {
115      super(OP_SORTER);
116   }
117
118   /**
119    * Override put to normalize keys to lowercase.
120    *
121    * @param key The key.
122    * @param value The value.
123    * @return The previous value associated with key, or null if there was no mapping for key.
124    */
125   @Override
126   public Operation put(String key, Operation value) {
127      return super.put(emptyIfNull(key).toLowerCase(), value);
128   }
129
130   /**
131    * Fluent-style put method.
132    *
133    * @param httpMethodName The HTTP method name.
134    * @param operation The operation.
135    * @return This object.
136    */
137   public OperationMap append(String httpMethodName, Operation operation) {
138      put(httpMethodName, operation);
139      return this;
140   }
141}