Repository: eclipse/microprofile-open-api
Pull request #283 (2018-10-03 19:46:05)

Add manipulations methods for maps
Add manipulations methods for maps in following interfaces:

* Paths
* Callback
* Content
* APIResponses
* Scopes
* SecurityRequirement
* ServerVariables

See Issue #248 

jmini: @arthurdm: Your comment about the null safety is really interesting.

For the moment (with version `1.1`) having a `null` map is almost not possible because the container itself (`Paths`, `Callback`, `Content`, `APIResponses`, …) is the map.

My guess is that all implementations will do something like this:

```java
    public Map<String, PathItem> getPathItems() {
        return java.util.Collections.unmodifiableMap(this);
    }
```

But if we think on `2.0` this will be an other story.
For the moment there is no enforcement for any map getters to return empty map instead of `null`.
Example: `schema.getProperties()` can be null.

And from a Yaml point of view, this is also not the same.

Empty Map case:

```yaml
MyObject:
  type: object
  Description: Some generic Object
  properties: {}
```

Null case:

```yaml
MyObject:
  type: object
  Description: Some generic Object
```

At the end this express the same thing (no properties in this Schema), but I do not think that that the decision should be taken at API level. Each implementation can decide to if they prefer `null` or "empty map".

So I propose to change the convenience default implementation in the API.

What do you think?

 

Patch:
diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/models/Paths.java b/api/src/main/java/org/eclipse/microprofile/openapi/models/Paths.java
index 66ee5d0..7cae430 100644
--- a/api/src/main/java/org/eclipse/microprofile/openapi/models/Paths.java
+++ b/api/src/main/java/org/eclipse/microprofile/openapi/models/Paths.java
@@ -40,14 +40,6 @@ public interface Paths extends Constructible, Extensible<Paths>, Map<String, Pat
      */
     Paths addPathItem(String name, PathItem item);
 
-    /**
-     * Adds all the path items to this Paths and return this instance of Paths
-     * 
-     * @param items a map containing the list of paths. Keys MUST begin with a slash.
-     * @return the current Paths instance
-     */
-    Paths addPathItems(Map<String, PathItem> items);
-
     /**
      * Removes the given path item to this Paths.
      * 
@@ -56,19 +48,32 @@ public interface Paths extends Constructible, Extensible<Paths>, Map<String, Pat
     void removePathItem(String name);
 
     /**
-     * Returns an immutable map of the path items.
+     * Returns a copy map (potentially immutable) of the path items.
      * 
      * @return all items
      */
     Map<String, PathItem> getPathItems();
 
     /**
-     * Check whether a path item is present to the map. This is a convenience method for <code>getPathItems().containsKey(name)</code>
+     * Set the path items map to this Paths
+     * 
+     * @param items a map containing the list of paths. Keys MUST begin with a slash.
+     */
+    void setPathItems(Map<String, PathItem> items);
+
+    /**
+     * Check whether a path item is present in the map. This is a convenience method for <code>getPathItems().containsKey(name)</code>
      * 
      * @param name a path name in the format valid for a Paths object.
      * @return a boolean to indicate if the path item is present or not.
      */
-    boolean hasPathItem(String name);
+    default boolean hasPathItem(String name) {
+        Map<String, PathItem> map = getPathItems();
+        if (map == null) {
+            return false;
+        }
+        return map.containsKey(name);
+    }
 
     /**
      * Returns a path item for a given name. This is a convenience method for <code>getPathItems().get(name)</code>
@@ -76,7 +81,13 @@ public interface Paths extends Constructible, Extensible<Paths>, Map<String, Pat
      * @param name a path name in the format valid for a Paths object.
      * @return the corresponding path item or null.
      */
-    PathItem getPathItem(String name);
+    default PathItem getPathItem(String name) {
+        Map<String, PathItem> map = getPathItems();
+        if (map == null) {
+            return null;
+        }
+        return map.get(name);
+    }
 
     /**
      * In the next version, {@link Paths} will no longer extends {@link Map}, this method will no longer be present.
@@ -107,7 +118,7 @@ public interface Paths extends Constructible, Extensible<Paths>, Map<String, Pat
 
     /**
      * In the next version, {@link Paths} will no longer extends {@link Map}, this method will no longer be present.
-     * Use {@link #addPathItems(Map)} instead.
+     * Use {@link #setPathItems(Map)} instead.
      * @deprecated since 1.1
      */
     @Deprecated
diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/models/callbacks/Callback.java b/api/src/main/java/org/eclipse/microprofile/openapi/models/callbacks/Callback.java
index 0680add..c1c702c 100644
--- a/api/src/main/java/org/eclipse/microprofile/openapi/models/callbacks/Callback.java
+++ b/api/src/main/java/org/eclipse/microprofile/openapi/models/callbacks/Callback.java
@@ -48,4 +48,99 @@ public interface Callback extends Constructible, Extensible<Callback>, Reference
      */
     Callback addPathItem(String name, PathItem pathItem);
 
+    /**
+     * Removes the given path item of the Callback PathItems.
+     * 
+     * @param name a path name that will be removed.
+     */
+    void removePathItem(String name);
+
+    /**
+     * Returns a copy map (potentially immutable) of the path items.
+     * 
+     * @return all items
+     */
+    Map<String, PathItem> getPathItems();
+
+    /**
+     * Set the path items map to this Callback.
+     * 
+     * @param items a map containing the list of paths.
+     */
+    void setPathItems(Map<String, PathItem> items);
+
+    /**
+     * Check whether a path item is present to the map. This is a convenience method for <code>getPathItems().containsKey(name)</code>
+     * 
+     * @param name a path name in the format valid for a Paths object.
+     * @return a boolean to indicate if the path item is present or not.
+     */
+    default boolean hasPathItem(String name) {
+        Map<String, PathItem> map = getPathItems();
+        if (map == null) {
+            return false;
+        }
+        return map.containsKey(name);
+    }
+
+    /**
+     * Returns a path item for a given name. This is a convenience method for <code>getPathItems().get(name)</code>
+     * 
+     * @param name a path name in the format valid for a Paths object.
+     * @return the corresponding path item or null.
+     */
+    default PathItem getPathItem(String name) {
+        Map<String, PathItem> map = getPathItems();
+        if (map == null) {
+            return null;
+        }
+        return map.get(name);
+    }
+
+    /**
+     * In the next version, {@link Callback} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #getPathItem(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    PathItem get(Object key);
+
+    /**
+     * In the next version, {@link Callback} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #hasPathItem(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    boolean containsKey(Object key);
+    
+    /**
+     * In the next version, {@link Callback} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #addPathItem(String, PathItem)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    PathItem put(String key, PathItem value);
+
+    /**
+     * In the next version, {@link Callback} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #setPathItems(Map)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    void putAll(Map<? extends String, ? extends PathItem> m);
+
+    /**
+     * In the next version, {@link Callback} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #removePathItem(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    PathItem remove(Object key);
+
+
 }
\ No newline at end of file
diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/models/media/Content.java b/api/src/main/java/org/eclipse/microprofile/openapi/models/media/Content.java
index cf6f0ac..367372c 100644
--- a/api/src/main/java/org/eclipse/microprofile/openapi/models/media/Content.java
+++ b/api/src/main/java/org/eclipse/microprofile/openapi/models/media/Content.java
@@ -39,4 +39,98 @@ public interface Content extends Constructible, Map<String, MediaType> {
      */
     Content addMediaType(String name, MediaType mediaType);
 
+    /**
+     * Removes the given MediaType for this Content by its name.
+     * 
+     * @param name a path name that will be removed.
+     */
+    void removeMediaType(String name);
+
+    /**
+     * Returns a copy map (potentially immutable) of media types.
+     * 
+     * @return all items
+     */
+    Map<String, MediaType> getMediaTypes();
+
+    /**
+     * Set the media types map to this Content
+     * 
+     * @param mediaTypes a map containing the list of media types. Keys are name of a media type e.g. application/json.
+     */
+    void setMediaTypes(Map<String, MediaType> mediaTypes);
+
+    /**
+     * Check whether a media type is present in the map. This is a convenience method for <code>getMediaTypes().containsKey(name)</code>
+     * 
+     * @param name the name of a media type e.g. application/json.
+     * @return a boolean to indicate if the media type is present or not.
+     */
+    default boolean hasMediaType(String name) {
+        Map<String, MediaType> map = getMediaTypes();
+        if (map == null) {
+            return false;
+        }
+        return map.containsKey(name);
+    }
+
+    /**
+     * Returns a media type for a given name. This is a convenience method for <code>getMediaTypes().get(name)</code>
+     * 
+     * @param name the name of a media type e.g. application/json.
+     * @return the corresponding media type or null.
+     */
+    default MediaType getMediaType(String name) {
+        Map<String, MediaType> map = getMediaTypes();
+        if (map == null) {
+            return null;
+        }
+        return map.get(name);
+    }
+
+    /**
+     * In the next version, {@link Content} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #getMediaType(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    MediaType get(Object key);
+
+    /**
+     * In the next version, {@link Content} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #hasMediaType(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    boolean containsKey(Object key);
+    
+    /**
+     * In the next version, {@link Content} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #addMediaType(String, MediaType)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    MediaType put(String key, MediaType value);
+
+    /**
+     * In the next version, {@link Content} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #setMediaTypes(Map)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    void putAll(Map<? extends String, ? extends MediaType> m);
+
+    /**
+     * In the next version, {@link Content} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #removeMediaType(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    MediaType remove(Object key);
+
 }
\ No newline at end of file
diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/models/responses/APIResponses.java b/api/src/main/java/org/eclipse/microprofile/openapi/models/responses/APIResponses.java
index 8784e8d..7947b56 100644
--- a/api/src/main/java/org/eclipse/microprofile/openapi/models/responses/APIResponses.java
+++ b/api/src/main/java/org/eclipse/microprofile/openapi/models/responses/APIResponses.java
@@ -35,7 +35,7 @@ public interface APIResponses extends Constructible, Extensible<APIResponses>, M
     /**
      * Adds an APIResponse in the format of the name as a key and the item as the value to APIResponses map
      * 
-     * @param name the name of APIResponse 
+     * @param name the name of APIResponse (http status code eventually with wildcard or {@value #DEFAULT})
      * @param apiResponse the APIResponse object to be added to APIResponses map
      * @return APIResponses map with the added ApiResponse instance
      * @deprecated since 1.1, use {@link #addAPIResponse(String, APIResponse)} instead
@@ -48,12 +48,107 @@ public interface APIResponses extends Constructible, Extensible<APIResponses>, M
     /**
      * Adds an APIResponse in the format of the name as a key and the item as the value to APIResponses map
      * 
-     * @param name the name of APIResponse 
+     * @param name the name of APIResponse (http status code eventually with wildcard or {@value #DEFAULT})
      * @param apiResponse the APIResponse object to be added to APIResponses map
      * @return APIResponses map with the added ApiResponse instance
      **/
     APIResponses addAPIResponse(String name, APIResponse apiResponse);
 
+
+    /**
+     * Removes the given APIResponse to this APIResponses.
+     * 
+     * @param name the name of APIResponse that will be removed (http status code eventually with wildcard or {@value #DEFAULT})
+     */
+    void removeAPIResponse(String name);
+
+    /**
+     * Returns a copy map (potentially immutable) of the APIResponses.
+     * 
+     * @return all responses
+     */
+    Map<String, APIResponse> getAPIResponses();
+
+    /**
+     * Set the APIResponses map to this APIResponses
+     * 
+     * @param items a map containing all responses. Keys are http statuses code eventually with wildcard or {@value #DEFAULT}.
+     */
+    void setAPIResponses(Map<String, APIResponse> items);
+
+    /**
+     * Check whether a APIResponse is present in the map. This is a convenience method for <code>getAPIResponses().containsKey(name)</code>
+     * 
+     * @param name the name of APIResponse (http status code eventually with wildcard or {@value #DEFAULT})
+     * @return a boolean to indicate if the APIResponse is present or not.
+     */
+    default boolean hasAPIResponse(String name) {
+        Map<String, APIResponse> map = getAPIResponses();
+        if (map == null) {
+            return false;
+        }
+        return map.containsKey(name);
+    }
+
+    /**
+     * Returns a APIResponse for a given name. This is a convenience method for <code>getAPIResponses().get(name)</code>
+     * 
+     * @param name the name of APIResponse (http status code eventually with wildcard or {@value #DEFAULT})
+     * @return the corresponding APIResponse or null.
+     */
+    default APIResponse getAPIResponse(String name) {
+        Map<String, APIResponse> map = getAPIResponses();
+        if (map == null) {
+            return null;
+        }
+        return map.get(name);
+    }
+
+    /**
+     * In the next version, {@link APIResponses} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #getAPIResponse(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    APIResponse get(Object key);
+
+    /**
+     * In the next version, {@link APIResponses} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #hasAPIResponse(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    boolean containsKey(Object key);
+    
+    /**
+     * In the next version, {@link APIResponses} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #addAPIResponse(String, APIResponse)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    APIResponse put(String key, APIResponse value);
+
+    /**
+     * In the next version, {@link APIResponses} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #setAPIResponses(Map)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    void putAll(Map<? extends String, ? extends APIResponse> m);
+
+    /**
+     * In the next version, {@link APIResponses} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #removeAPIResponse(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    APIResponse remove(Object key);
+
     /**
      * Returns the default documentation of responses other than the ones declared for specific HTTP response codes in this instance of ApiResponses.
      * <p>
diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/models/security/Scopes.java b/api/src/main/java/org/eclipse/microprofile/openapi/models/security/Scopes.java
index 639bf86..597a852 100644
--- a/api/src/main/java/org/eclipse/microprofile/openapi/models/security/Scopes.java
+++ b/api/src/main/java/org/eclipse/microprofile/openapi/models/security/Scopes.java
@@ -39,4 +39,98 @@ public interface Scopes extends Constructible, Extensible<Scopes>, Map<String, S
      */
     Scopes addScope(String scope, String description);
 
+    /**
+     * Removes the given scope item to this Scopes.
+     * 
+     * @param scope the name of a scope
+     */
+    void removeScope(String scope);
+
+    /**
+     * Returns a copy map (potentially immutable) of scopes.
+     * 
+     * @return all items
+     */
+    Map<String, String> getScopes();
+
+    /**
+     * Set the scope items map to this Scopes
+     * 
+     * @param items key-value pair in a map.
+     */
+    void setScopes(Map<String, String> items);
+
+    /**
+     * Check whether a scope item is present in the map. This is a convenience method for <code>getScopes().containsKey(name)</code>
+     * 
+     * @param scope the name of a scope.
+     * @return a boolean to indicate if the scope item is present or not.
+     */
+    default boolean hasScope(String scope) {
+        Map<String, String> map = getScopes();
+        if (map == null) {
+            return false;
+        }
+        return map.containsKey(scope);
+    }
+
+    /**
+     * Returns a scope description for a given scope name. This is a convenience method for <code>getScopes().get(name)</code>
+     * 
+     * @param scope the name of a scope.
+     * @return the corresponding description or null.
+     */
+    default String getScope(String scope) {
+        Map<String, String> map = getScopes();
+        if (map == null) {
+            return null;
+        }
+        return map.get(scope);
+    }
+
+    /**
+     * In the next version, {@link Scopes} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #getScope(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    String get(Object key);
+
+    /**
+     * In the next version, {@link Scopes} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #hasScope(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    boolean containsKey(Object key);
+    
+    /**
+     * In the next version, {@link Scopes} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #addScope(String, String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    String put(String key, String value);
+
+    /**
+     * In the next version, {@link Scopes} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #setScopes(Map)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    void putAll(Map<? extends String, ? extends String> m);
+
+    /**
+     * In the next version, {@link Scopes} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #removeScope(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    String remove(Object key);
+
 }
\ No newline at end of file
diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/models/security/SecurityRequirement.java b/api/src/main/java/org/eclipse/microprofile/openapi/models/security/SecurityRequirement.java
index c925c93..70ad9d5 100644
--- a/api/src/main/java/org/eclipse/microprofile/openapi/models/security/SecurityRequirement.java
+++ b/api/src/main/java/org/eclipse/microprofile/openapi/models/security/SecurityRequirement.java
@@ -59,4 +59,99 @@ public interface SecurityRequirement extends Constructible, Map<String, List<Str
      */
     SecurityRequirement addScheme(String securitySchemeName);
 
+    /**
+     * Removes a security scheme to the SecurityRequirement instance based on the scheme name.
+     * 
+     * @param securitySchemeName the name of security scheme
+     */
+    void removeScheme(String securitySchemeName);
+
+    /**
+     * Returns a copy map (potentially immutable) of the schemes.
+     * 
+     * @return all items
+     */
+    Map<String, List<String>> getSchemes();
+
+    /**
+     * Set all security schemes to the SecurityRequirement instance. Keys are the name of security scheme declared in the Components 
+     * section of the OpenAPI document, values are a list of required scope - only valid when the defined scheme is 'oauth2' or 'openIdConnect'
+     * 
+     * @param items a map containing the security schemes.
+     */
+    void setSchemes(Map<String, List<String>> items);
+
+    /**
+     * Check whether a scheme is present in the map. This is a convenience method for <code>getSchemes().containsKey(name)</code>
+     * 
+     * @param securitySchemeName the name of security scheme
+     * @return a boolean to indicate if the scheme is present or not.
+     */
+    default boolean hasScheme(String securitySchemeName) {
+        Map<String, List<String>> map = getSchemes();
+        if (map == null) {
+            return false;
+        }
+        return map.containsKey(securitySchemeName);
+    }
+
+    /**
+     * Returns a path item for a given name. This is a convenience method for <code>getSchemes().get(name)</code>
+     * 
+     * @param securitySchemeName the name of security scheme
+     * @return the corresponding path item or null.
+     */
+    default List<String> getScheme(String securitySchemeName) {
+        Map<String, List<String>> map = getSchemes();
+        if (map == null) {
+            return null;
+        }
+        return map.get(securitySchemeName);
+    }
+
+    /**
+     * In the next version, {@link SecurityRequirement} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #getScheme(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    List<String> get(Object key);
+
+    /**
+     * In the next version, {@link SecurityRequirement} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #hasScheme(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    boolean containsKey(Object key);
+    
+    /**
+     * In the next version, {@link SecurityRequirement} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #addScheme(String, List)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    List<String> put(String key, List<String> value);
+
+    /**
+     * In the next version, {@link SecurityRequirement} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #setSchemes(Map)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    void putAll(Map<? extends String, ? extends List<String>> m);
+
+    /**
+     * In the next version, {@link SecurityRequirement} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #removeScheme(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    List<String> remove(Object key);
+
 }
\ No newline at end of file
diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/models/servers/ServerVariables.java b/api/src/main/java/org/eclipse/microprofile/openapi/models/servers/ServerVariables.java
index 8842043..782894a 100644
--- a/api/src/main/java/org/eclipse/microprofile/openapi/models/servers/ServerVariables.java
+++ b/api/src/main/java/org/eclipse/microprofile/openapi/models/servers/ServerVariables.java
@@ -38,4 +38,98 @@ public interface ServerVariables extends Constructible, Extensible<ServerVariabl
      */
     ServerVariables addServerVariable(String name, ServerVariable serverVariable);
 
+    /**
+     * Removes the given server variables.
+     * 
+     * @param name the name of ServerVariable instance
+     */
+    void removeServerVariable(String name);
+
+    /**
+     * Returns a copy map (potentially immutable) of the server variables.
+     * 
+     * @return all items
+     */
+    Map<String, ServerVariable> getServerVariables();
+
+    /**
+     * Set the server variables map to this ServerVariables object.
+     * 
+     * @param items a map containing key-value item.
+     */
+    void setServerVariables(Map<String, ServerVariable> items);
+
+    /**
+     * Check whether a server variable is present in the map. This is a convenience method for <code>getServerVariables().containsKey(name)</code>
+     * 
+     * @param name the name of ServerVariable instance
+     * @return a boolean to indicate if the server variable is present or not.
+     */
+    default boolean hasServerVariable(String name) {
+        Map<String, ServerVariable> map = getServerVariables();
+        if (map == null) {
+            return false;
+        }
+        return map.containsKey(name);
+    }
+
+    /**
+     * Returns a server variable for a given name. This is a convenience method for <code>getServerVariables().get(name)</code>
+     * 
+     * @param name the name of ServerVariable instance
+     * @return the corresponding server variable or null.
+     */
+    default ServerVariable getServerVariable(String name) {
+        Map<String, ServerVariable> map = getServerVariables();
+        if (map == null) {
+            return null;
+        }
+        return map.get(name);
+    }
+
+    /**
+     * In the next version, {@link ServerVariables} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #getServerVariable(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    ServerVariable get(Object key);
+
+    /**
+     * In the next version, {@link ServerVariables} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #hasServerVariable(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    boolean containsKey(Object key);
+    
+    /**
+     * In the next version, {@link ServerVariables} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #addServerVariable(String, ServerVariable)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    ServerVariable put(String key, ServerVariable value);
+
+    /**
+     * In the next version, {@link ServerVariables} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #setServerVariables(Map)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    void putAll(Map<? extends String, ? extends ServerVariable> m);
+
+    /**
+     * In the next version, {@link ServerVariables} will no longer extends {@link Map}, this method will no longer be present.
+     * Use {@link #removeServerVariable(String)} instead.
+     * @deprecated since 1.1
+     */
+    @Deprecated
+    @Override
+    ServerVariable remove(Object key);
+
 }
\ No newline at end of file


Test patch:
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelConstructionTest.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelConstructionTest.java
index bd8ce6b..3878840 100644
--- a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelConstructionTest.java
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelConstructionTest.java
@@ -35,6 +35,7 @@ import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Function;
 
 import org.eclipse.microprofile.openapi.OASFactory;
 import org.eclipse.microprofile.openapi.models.Components;
@@ -387,13 +388,16 @@ public class ModelConstructionTest {
         
         p.removePathItem(pathItemKey);
         assertFalse(p.hasPathItem(pathItemKey), pathItemKey + " is absent in the map");
-        assertEquals(p.size(), 1, "The map is expected to contain two entries.");
-        assertEquals(p.getPathItems().size(), 1, "The map is expected to contain two entries.");
+        assertEquals(p.size(), 1, "The map is expected to contain one entry.");
+        assertEquals(p.getPathItems().size(), 1, "The map is expected to contain one entry.");
         
         p.remove(pathItemKey2);
         assertFalse(p.hasPathItem(pathItemKey2), pathItemKey + " is absent in the map");
         assertEquals(p.size(), 0, "The map is expected to contain 0 entries.");
         assertEquals(p.getPathItems().size(), 0, "The map is expected to contain 0 entries.");
+        
+        final PathItem otherValue = createConstructibleInstance(PathItem.class);
+        checkMapImmutable(p, Paths::getPathItems, "/otherPathItem", otherValue);
     }
     
     @Test
@@ -401,16 +405,40 @@ public class ModelConstructionTest {
         final Callback c = processConstructible(Callback.class);
         
         final String pathItemKey = "myPathItem";
+        assertFalse(c.hasPathItem(pathItemKey), pathItemKey + " is absent in the map");
         final PathItem pathItemValue = createConstructibleInstance(PathItem.class);
         checkSameObject(c, c.addPathItem(pathItemKey, pathItemValue));
+        assertTrue(c.hasPathItem(pathItemKey), pathItemKey + " is present in the map");
+        assertSame(c.getPathItem(pathItemKey), pathItemValue, 
+                "The value associated with the key: " + pathItemKey + " is expected to be the same one that was added.");
         checkMapEntry(c, pathItemKey, pathItemValue);
+        checkMapEntry(c.getPathItems(), pathItemKey, pathItemValue);
         
         final String pathItemKey2 = "myPathItem2";
+        assertFalse(c.hasPathItem(pathItemKey2), pathItemKey2 + " is absent in the map");
         final PathItem pathItemValue2 = createConstructibleInstance(PathItem.class);
         assertNull(c.put(pathItemKey2, pathItemValue2), "No previous mapping expected.");
+        assertTrue(c.hasPathItem(pathItemKey2), pathItemKey2 + " is present in the map");
+        assertSame(c.getPathItem(pathItemKey2), pathItemValue2, 
+                "The value associated with the key: " + pathItemKey2 + " is expected to be the same one that was added.");
         checkMapEntry(c, pathItemKey2, pathItemValue2);
+        checkMapEntry(c.getPathItems(), pathItemKey2, pathItemValue2);
         
         assertEquals(c.size(), 2, "The map is expected to contain two entries.");
+        assertEquals(c.getPathItems().size(), 2, "The map is expected to contain two entries.");
+        
+        c.removePathItem(pathItemKey);
+        assertFalse(c.hasPathItem(pathItemKey), pathItemKey + " is absent in the map");
+        assertEquals(c.size(), 1, "The map is expected to contain one entry.");
+        assertEquals(c.getPathItems().size(), 1, "The map is expected to contain one entry.");
+        
+        c.remove(pathItemKey2);
+        assertFalse(c.hasPathItem(pathItemKey2), pathItemKey + " is absent in the map");
+        assertEquals(c.size(), 0, "The map is expected to contain 0 entries.");
+        assertEquals(c.getPathItems().size(), 0, "The map is expected to contain 0 entries.");
+        
+        final PathItem otherValue = createConstructibleInstance(PathItem.class);
+        checkMapImmutable(c, Callback::getPathItems, "otherPathItem", otherValue);
     }
     
     @Test
@@ -463,17 +491,41 @@ public class ModelConstructionTest {
     public void contentTest() {
         final Content c = processConstructible(Content.class);
         
-        final String mediaTypeKey = "myPathItem";
+        final String mediaTypeKey = "application/json";
+        assertFalse(c.hasMediaType(mediaTypeKey), mediaTypeKey + " is absent in the map");
         final MediaType mediaTypeValue = createConstructibleInstance(MediaType.class);
         checkSameObject(c, c.addMediaType(mediaTypeKey, mediaTypeValue));
+        assertTrue(c.hasMediaType(mediaTypeKey), mediaTypeKey + " is present in the map");
+        assertSame(c.getMediaType(mediaTypeKey), mediaTypeValue, 
+                "The value associated with the key: " + mediaTypeKey + " is expected to be the same one that was added.");
         checkMapEntry(c, mediaTypeKey, mediaTypeValue);
+        checkMapEntry(c.getMediaTypes(), mediaTypeKey, mediaTypeValue);
         
-        final String mediaTypeKey2 = "myPathItem2";
+        final String mediaTypeKey2 = "*/*";
+        assertFalse(c.hasMediaType(mediaTypeKey2), mediaTypeKey2 + " is absent in the map");
         final MediaType mediaTypeValue2 = createConstructibleInstance(MediaType.class);
         assertNull(c.put(mediaTypeKey2, mediaTypeValue2), "No previous mapping expected.");
+        assertTrue(c.hasMediaType(mediaTypeKey2), mediaTypeKey2 + " is present in the map");
+        assertSame(c.getMediaType(mediaTypeKey2), mediaTypeValue2, 
+                "The value associated with the key: " + mediaTypeKey2 + " is expected to be the same one that was added.");
         checkMapEntry(c, mediaTypeKey2, mediaTypeValue2);
+        checkMapEntry(c.getMediaTypes(), mediaTypeKey2, mediaTypeValue2);
         
         assertEquals(c.size(), 2, "The map is expected to contain two entries.");
+        assertEquals(c.getMediaTypes().size(), 2, "The map is expected to contain two entries.");
+        
+        c.removeMediaType(mediaTypeKey);
+        assertFalse(c.hasMediaType(mediaTypeKey), mediaTypeKey + " is absent in the map");
+        assertEquals(c.size(), 1, "The map is expected to contain one entry.");
+        assertEquals(c.getMediaTypes().size(), 1, "The map is expected to contain one entry.");
+        
+        c.remove(mediaTypeKey2);
+        assertFalse(c.hasMediaType(mediaTypeKey2), mediaTypeKey + " is absent in the map");
+        assertEquals(c.size(), 0, "The map is expected to contain 0 entries.");
+        assertEquals(c.getMediaTypes().size(), 0, "The map is expected to contain 0 entries.");
+        
+        final MediaType otherValue = createConstructibleInstance(MediaType.class);
+        checkMapImmutable(c, Content::getMediaTypes, "application/txt", otherValue);
     }
     
     @Test
@@ -637,16 +689,40 @@ public class ModelConstructionTest {
         assertEquals(responses.size(), 0, "The map is expected to contain two entries.");
         
         final String responseKey = "200";
-        final APIResponse responseValue = createConstructibleInstance(APIResponse.class);
-        checkSameObject(responses, responses.addAPIResponse(responseKey, responseValue));
-        checkMapEntry(responses, responseKey, responseValue);
+        assertFalse(responses.hasAPIResponse(responseKey), responseKey + " is absent in the map");
+        final APIResponse pathItemValue = createConstructibleInstance(APIResponse.class);
+        checkSameObject(responses, responses.addAPIResponse(responseKey, pathItemValue));
+        assertTrue(responses.hasAPIResponse(responseKey), responseKey + " is present in the map");
+        assertSame(responses.getAPIResponse(responseKey), pathItemValue, 
+                "The value associated with the key: " + responseKey + " is expected to be the same one that was added.");
+        checkMapEntry(responses, responseKey, pathItemValue);
+        checkMapEntry(responses.getAPIResponses(), responseKey, pathItemValue);
         
         final String responseKey2 = "4XX";
-        final APIResponse responseValue2 = createConstructibleInstance(APIResponse.class);
-        assertNull(responses.put(responseKey2, responseValue2), "No previous mapping expected.");
-        checkMapEntry(responses, responseKey2, responseValue2);
+        assertFalse(responses.hasAPIResponse(responseKey2), responseKey2 + " is absent in the map");
+        final APIResponse pathItemValue2 = createConstructibleInstance(APIResponse.class);
+        assertNull(responses.put(responseKey2, pathItemValue2), "No previous mapping expected.");
+        assertTrue(responses.hasAPIResponse(responseKey2), responseKey2 + " is present in the map");
+        assertSame(responses.getAPIResponse(responseKey2), pathItemValue2, 
+                "The value associated with the key: " + responseKey2 + " is expected to be the same one that was added.");
+        checkMapEntry(responses, responseKey2, pathItemValue2);
+        checkMapEntry(responses.getAPIResponses(), responseKey2, pathItemValue2);
         
         assertEquals(responses.size(), 2, "The map is expected to contain two entries.");
+        assertEquals(responses.getAPIResponses().size(), 2, "The map is expected to contain two entries.");
+        
+        responses.removeAPIResponse(responseKey);
+        assertFalse(responses.hasAPIResponse(responseKey), responseKey + " is absent in the map");
+        assertEquals(responses.size(), 1, "The map is expected to contain one entry.");
+        assertEquals(responses.getAPIResponses().size(), 1, "The map is expected to contain one entry.");
+        
+        responses.remove(responseKey2);
+        assertFalse(responses.hasAPIResponse(responseKey2), responseKey + " is absent in the map");
+        assertEquals(responses.size(), 0, "The map is expected to contain 0 entries.");
+        assertEquals(responses.getAPIResponses().size(), 0, "The map is expected to contain 0 entries.");
+        
+        final APIResponse otherValue = createConstructibleInstance(APIResponse.class);
+        checkMapImmutable(responses, APIResponses::getAPIResponses, "500", otherValue);
         
         assertNull(responses.getDefaultValue(), "No default value expected.");
         final String responseKey3 = APIResponses.DEFAULT;
@@ -655,7 +731,7 @@ public class ModelConstructionTest {
         checkMapEntry(responses, responseKey3, responseValue3);
         checkSameObject(responseValue3, responses.getDefaultValue());
         
-        assertEquals(responses.size(), 3, "The map is expected to contain two entries.");
+        assertEquals(responses.size(), 1, "The map is expected to contain one entry.");
         
         responses.setDefaultValue(null);
         assertNull(responses.get(APIResponses.DEFAULT), "No default value expected.");
@@ -682,33 +758,81 @@ public class ModelConstructionTest {
         final Scopes s = processConstructible(Scopes.class);
         
         final String scopeKey = "myScope";
+        assertFalse(s.hasScope(scopeKey), scopeKey + " is absent in the map");
         final String scopeValue = new String("myDescription");
         checkSameObject(s, s.addScope(scopeKey, scopeValue));
+        assertTrue(s.hasScope(scopeKey), scopeKey + " is present in the map");
+        assertSame(s.getScope(scopeKey), scopeValue, 
+                "The value associated with the key: " + scopeKey + " is expected to be the same one that was added.");
         checkMapEntry(s, scopeKey, scopeValue);
+        checkMapEntry(s.getScopes(), scopeKey, scopeValue);
         
         final String scopeKey2 = "myScope2";
+        assertFalse(s.hasScope(scopeKey2), scopeKey2 + " is absent in the map");
         final String scopeValue2 = new String("myDescription2");
         assertNull(s.put(scopeKey2, scopeValue2), "No previous mapping expected.");
+        assertTrue(s.hasScope(scopeKey2), scopeKey2 + " is present in the map");
+        assertSame(s.getScope(scopeKey2), scopeValue2, 
+                "The value associated with the key: " + scopeKey2 + " is expected to be the same one that was added.");
         checkMapEntry(s, scopeKey2, scopeValue2);
+        checkMapEntry(s.getScopes(), scopeKey2, scopeValue2);
         
         assertEquals(s.size(), 2, "The map is expected to contain two entries.");
+        assertEquals(s.getScopes().size(), 2, "The map is expected to contain two entries.");
+        
+        s.removeScope(scopeKey);
+        assertFalse(s.hasScope(scopeKey), scopeKey + " is absent in the map");
+        assertEquals(s.size(), 1, "The map is expected to contain one entry.");
+        assertEquals(s.getScopes().size(), 1, "The map is expected to contain one entry.");
+        
+        s.remove(scopeKey2);
+        assertFalse(s.hasScope(scopeKey2), scopeKey + " is absent in the map");
+        assertEquals(s.size(), 0, "The map is expected to contain 0 entries.");
+        assertEquals(s.getScopes().size(), 0, "The map is expected to contain 0 entries.");
+        
+        final String otherValue = new String("otherDescription");
+        checkMapImmutable(s, Scopes::getScopes, "otherScope", otherValue);
     }
     
     @Test
     public void securityRequirementTest() {
         final SecurityRequirement sr = processConstructible(SecurityRequirement.class);
         
-        final String schemeKey = "myResponse";
+        final String schemeKey = "myScheme";
+        assertFalse(sr.hasScheme(schemeKey), schemeKey + " is absent in the map");
         final List<String> schemeValue = new ArrayList<String>();
         checkSameObject(sr, sr.addScheme(schemeKey, schemeValue));
+        assertTrue(sr.hasScheme(schemeKey), schemeKey + " is present in the map");
+        assertSame(sr.getScheme(schemeKey), schemeValue, 
+                "The value associated with the key: " + schemeKey + " is expected to be the same one that was added.");
         checkMapEntry(sr, schemeKey, schemeValue);
+        checkMapEntry(sr.getSchemes(), schemeKey, schemeValue);
         
-        final String schemeKey2 = "myResponse2";
+        final String schemeKey2 = "myScheme2";
+        assertFalse(sr.hasScheme(schemeKey2), schemeKey2 + " is absent in the map");
         final List<String> schemeValue2 = new ArrayList<String>();
         assertNull(sr.put(schemeKey2, schemeValue2), "No previous mapping expected.");
+        assertTrue(sr.hasScheme(schemeKey2), schemeKey2 + " is present in the map");
+        assertSame(sr.getScheme(schemeKey2), schemeValue2, 
+                "The value associated with the key: " + schemeKey2 + " is expected to be the same one that was added.");
         checkMapEntry(sr, schemeKey2, schemeValue2);
+        checkMapEntry(sr.getSchemes(), schemeKey2, schemeValue2);
         
         assertEquals(sr.size(), 2, "The map is expected to contain two entries.");
+        assertEquals(sr.getSchemes().size(), 2, "The map is expected to contain two entries.");
+        
+        sr.removeScheme(schemeKey);
+        assertFalse(sr.hasScheme(schemeKey), schemeKey + " is absent in the map");
+        assertEquals(sr.size(), 1, "The map is expected to contain one entry.");
+        assertEquals(sr.getSchemes().size(), 1, "The map is expected to contain one entry.");
+        
+        sr.remove(schemeKey2);
+        assertFalse(sr.hasScheme(schemeKey2), schemeKey + " is absent in the map");
+        assertEquals(sr.size(), 0, "The map is expected to contain 0 entries.");
+        assertEquals(sr.getSchemes().size(), 0, "The map is expected to contain 0 entries.");
+        
+        final List<String> otherValue = new ArrayList<String>();
+        checkMapImmutable(sr, SecurityRequirement::getSchemes, "otherScheme", otherValue);
     }
     
     @Test
@@ -738,16 +862,40 @@ public class ModelConstructionTest {
         final ServerVariables svs = processConstructible(ServerVariables.class);
         
         final String varKey = "myServerVariable";
+        assertFalse(svs.hasServerVariable(varKey), varKey + " is absent in the map");
         final ServerVariable varValue = createConstructibleInstance(ServerVariable.class);
         checkSameObject(svs, svs.addServerVariable(varKey, varValue));
+        assertTrue(svs.hasServerVariable(varKey), varKey + " is present in the map");
+        assertSame(svs.getServerVariable(varKey), varValue, 
+                "The value associated with the key: " + varKey + " is expected to be the same one that was added.");
         checkMapEntry(svs, varKey, varValue);
+        checkMapEntry(svs.getServerVariables(), varKey, varValue);
         
         final String varKey2 = "myServerVariable2";
+        assertFalse(svs.hasServerVariable(varKey2), varKey2 + " is absent in the map");
         final ServerVariable varValue2 = createConstructibleInstance(ServerVariable.class);
         assertNull(svs.put(varKey2, varValue2), "No previous mapping expected.");
+        assertTrue(svs.hasServerVariable(varKey2), varKey2 + " is present in the map");
+        assertSame(svs.getServerVariable(varKey2), varValue2, 
+                "The value associated with the key: " + varKey2 + " is expected to be the same one that was added.");
         checkMapEntry(svs, varKey2, varValue2);
+        checkMapEntry(svs.getServerVariables(), varKey2, varValue2);
         
         assertEquals(svs.size(), 2, "The map is expected to contain two entries.");
+        assertEquals(svs.getServerVariables().size(), 2, "The map is expected to contain two entries.");
+        
+        svs.removeServerVariable(varKey);
+        assertFalse(svs.hasServerVariable(varKey), varKey + " is absent in the map");
+        assertEquals(svs.size(), 1, "The map is expected to contain one entry.");
+        assertEquals(svs.getServerVariables().size(), 1, "The map is expected to contain one entry.");
+        
+        svs.remove(varKey2);
+        assertFalse(svs.hasServerVariable(varKey2), varKey + " is absent in the map");
+        assertEquals(svs.size(), 0, "The map is expected to contain 0 entries.");
+        assertEquals(svs.getServerVariables().size(), 0, "The map is expected to contain 0 entries.");
+        
+        final ServerVariable otherValue = createConstructibleInstance(ServerVariable.class);
+        checkMapImmutable(svs, ServerVariables::getServerVariables, "otherServerVariable", otherValue);
     }
     
     @Test
@@ -845,7 +993,7 @@ public class ModelConstructionTest {
     private void processConstructibleProperty(Constructible o, Property p, Class<?> enclosingInterface) {
         final Object value1 = getInstanceOf(p.getType(), false);
         p.invokeSetter(o, value1);
-        if (!p.isPrimitive()) {
+        if (!p.isPrimitive() && !p.isCompatible(Map.class)) {
             assertSame(p.invokeGetter(o), value1, "The return value of the getter method for property \"" + 
                     p.getName() + "\" of interface \"" + enclosingInterface.getName() +
                     "\" is expected to be the same as the value that was set.");
@@ -1032,6 +1180,23 @@ public class ModelConstructionTest {
         assertTrue(map.containsKey(key), "The map is expected to contain the key: " + key);
         assertSame(map.get(key), value, "The value associated with the key: " + key + " is expected to be the same one that was added.");
     }
+
+    private <O, K, T> void checkMapImmutable(O container, Function<O, Map<K,T>> mapGetter, K key, T otherValue) {
+        Map<K,T> map = mapGetter.apply(container);
+        assertNotNull(map, "The map must not be null.");
+        assertFalse(map.containsKey(key), "The map is expected to not contain the key: " + key);
+        int originalSize = map.size();
+        try {
+            map.put(key, otherValue);
+        }
+        catch (Exception e) {
+            //It is allowed to throw an exception
+        }
+        Map<K,T> map2 = mapGetter.apply(container);
+        assertNotNull(map2, "The map must not be null.");
+        assertFalse(map2.containsKey(key), "The map is expected to not contain the key: " + key);
+        assertEquals(map2.size(), originalSize, "The map is expected to have a size of " + originalSize);
+    }
     
     private <T> void checkListEntry(List<T> list, T value) {
         assertNotNull(list, "The list must not be null.");