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.openapi3;
018
019import static org.apache.juneau.commons.utils.CollectionUtils.*;
020import static org.apache.juneau.commons.utils.Shorts.*;
021import static org.apache.juneau.marshall.internal.ConverterUtils.*;
022
023import java.util.*;
024
025import org.apache.juneau.commons.bean.*;
026import org.apache.juneau.marshall.*;
027import org.apache.juneau.marshall.collections.*;
028import org.apache.juneau.marshall.marshaller.*;
029
030/**
031 * Root class for all Swagger beans.
032 */
033@Marshalled
034@SuppressWarnings({
035   "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants
036})
037public abstract class OpenApiElement {
038
039   private boolean strict;
040   private Map<String,Object> extra;
041
042   OpenApiElement() {}
043
044   OpenApiElement(OpenApiElement copyFrom) {
045      this.strict = copyFrom.strict;
046      this.extra = cp(copyFrom.extra);
047   }
048
049   /**
050    * Returns a copy of this swagger element as a modifiable map.
051    *
052    * <p>
053    * Each call produces a new map.
054    *
055    * @return A map containing all the values in this swagger element.
056    */
057   public JsonMap asMap() {
058      var m = new JsonMap();
059      for (var s : keySet())
060         m.put(s, get(s, Object.class));
061      return m;
062   }
063
064   /**
065    * Generic property keyset.
066    *
067    * @return
068    *    All the non-standard keys on this element.
069    *    <br>Never <jk>null</jk>.
070    */
071   @BeanProp("*")
072   public Set<String> extraKeys() {
073      return extra == null ? Collections.emptySet() : u(extra.keySet());
074   }
075
076   /**
077    * Generic property getter.
078    *
079    * <p>
080    * Can be used to retrieve non-standard Swagger fields such as <js>"$ref"</js>.
081    *
082    * @param property The property name to retrieve.  Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown.
083    * @return The property value, or <jk>null</jk> if the property does not exist or is not set.
084    */
085   @BeanProp("*")
086   public Object get(String property) {
087      reqnn("property", property);
088      return o(extra).map(x -> x.get(property)).orElse(null);
089   }
090
091   /**
092    * Generic property getter.
093    *
094    * <p>
095    * Can be used to retrieve non-standard Swagger fields such as <js>"$ref"</js>.
096    *
097    * @param property The property name to retrieve.
098    * @param type The datatype to cast the value to.
099    * @param <T> The datatype to cast the value to.
100    * @return The property value, or <jk>null</jk> if the property does not exist or is not set.
101    */
102   public <T> T get(String property, Class<T> type) {
103      reqnn("property", property);
104      return toType(get(property), type);
105   }
106
107   /**
108    * Returns all the keys on this element.
109    *
110    * @return
111    *    All the keys on this element.
112    *    <br>Never <jk>null</jk>.
113    */
114   public Set<String> keySet() {
115      return extraKeys();
116   }
117
118   /**
119    * Generic property setter.
120    *
121    * <p>
122    * Can be used to set non-standard Swagger fields such as <js>"$ref"</js>.
123    *
124    * @param property The property name to set.  Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown.
125    * @param value The new value for the property.
126    * @return This object
127    * @throws RuntimeException if strict mode is enabled.
128    */
129   @BeanProp("*")
130   public OpenApiElement set(String property, Object value) {
131      reqnn("property", property);
132      if (strict)
133         throw rex("Cannot set property '%s' in strict mode.", property);
134      if (extra == null)
135         extra = map();
136      extra.put(property, value);
137      return this;
138   }
139
140   @Override /* Overridden from Object */
141   public String toString() {
142      return Json.of(this);
143   }
144
145   /**
146    * Returns <jk>true</jk> if contents should be validated per the Swagger spec.
147    *
148    * @return <jk>true</jk> if contents should be validated per the Swagger spec.
149    */
150   protected boolean isStrict() { return strict; }
151
152   /**
153    * Sets strict mode on this bean.
154    *
155    * @return This object
156    */
157   protected OpenApiElement strict() {
158      strict = true;
159      return this;
160   }
161
162   /**
163    * Sets strict mode on this bean.
164    *
165    * @param value
166    *    The new value for this property.
167    *    <br>Non-boolean values will be converted to boolean using <code>Boolean.<jsm>valueOf</jsm>(value.toString())</code>.
168    *    <br>Can be <jk>null</jk> (interpreted as <jk>false</jk>).
169    * @return This object
170    */
171   protected OpenApiElement strict(Object value) {
172      strict = nn(value) && toBoolean(value);
173      return this;
174   }
175}