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.AssertionUtils.*;
020import static org.apache.juneau.commons.utils.CollectionUtils.*;
021import static org.apache.juneau.commons.utils.Utils.*;
022import static org.apache.juneau.internal.ConverterUtils.*;
023
024import java.util.*;
025
026import org.apache.juneau.commons.collections.*;
027
028/**
029 * A metadata object that allows for more fine-tuned XML model definitions.
030 *
031 * <p>
032 * The Xml Object is a metadata object that allows for more fine-tuned XML model definitions. When using arrays,
033 * XML element names are not inferred (for singular/plural forms) and the name property should be used to add that
034 * information. This object is used to control how schema properties are serialized to XML.
035 *
036 * <h5 class='section'>OpenAPI Specification:</h5>
037 * <p>
038 * The Xml Object is composed of the following fields:
039 * <ul class='spaced-list'>
040 *    <li><c>name</c> (string) - Replaces the name of the element/attribute used for the described schema property
041 *    <li><c>namespace</c> (string) - The URI of the namespace definition
042 *    <li><c>prefix</c> (string) - The prefix to be used for the name
043 *    <li><c>attribute</c> (boolean) - Declares whether the property definition translates to an attribute instead of an element
044 *    <li><c>wrapped</c> (boolean) - May be used only for an array definition. Signifies whether the array is wrapped
045 * </ul>
046 *
047 * <h5 class='section'>Example:</h5>
048 * <p class='bcode'>
049 *    <jc>// Construct using SwaggerBuilder.</jc>
050 *    Xml <jv>x</jv> = <jsm>xml</jsm>().setName(<js>"book"</js>).setNamespace(<js>"http://example.com/schema"</js>);
051 *
052 *    <jc>// Serialize using JsonSerializer.</jc>
053 *    String <jv>json</jv> = Json.<jsm>from</jsm>(<jv>x</jv>);
054 *
055 *    <jc>// Or just use toString() which does the same as above.</jc>
056 *    <jv>json</jv> = <jv>x</jv>.toString();
057 * </p>
058 * <p class='bcode'>
059 *    <jc>// Output</jc>
060 *    {
061 *       <js>"name"</js>: <js>"book"</js>,
062 *       <js>"namespace"</js>: <js>"http://example.com/schema"</js>
063 *    }
064 * </p>
065 *
066 * <h5 class='section'>See Also:</h5><ul>
067 *    <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#xml-object">OpenAPI Specification &gt; XML Object</a>
068 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/data-models/representing-xml/">OpenAPI Representing XML</a>
069 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
070 * </ul>
071 */
072public class Xml extends OpenApiElement {
073
074   private String name, namespace, prefix;
075   private Boolean attribute, wrapped;
076
077   /**
078    * Default constructor.
079    */
080   public Xml() {}
081
082   /**
083    * Copy constructor.
084    *
085    * @param copyFrom The object to copy.
086    */
087   public Xml(Xml copyFrom) {
088      super(copyFrom);
089
090      this.name = copyFrom.name;
091      this.namespace = copyFrom.namespace;
092      this.prefix = copyFrom.prefix;
093      this.attribute = copyFrom.attribute;
094      this.wrapped = copyFrom.wrapped;
095   }
096
097   /**
098    * Make a deep copy of this object.
099    *
100    * @return A deep copy of this object.
101    */
102   public Xml copy() {
103      return new Xml(this);
104   }
105
106   @Override /* Overridden from OpenApiElement */
107   public <T> T get(String property, Class<T> type) {
108      assertArgNotNull("property", property);
109      return switch (property) {
110         case "name" -> toType(getName(), type);
111         case "namespace" -> toType(getNamespace(), type);
112         case "prefix" -> toType(getPrefix(), type);
113         case "attribute" -> toType(getAttribute(), type);
114         case "wrapped" -> toType(getWrapped(), type);
115         default -> super.get(property, type);
116      };
117   }
118
119   /**
120    * Bean property getter:  <property>attribute</property>.
121    *
122    * <p>
123    * Declares whether the property definition translates to an attribute instead of an element.
124    *
125    * @return The property value, or <jk>null</jk> if it is not set.
126    */
127   public Boolean getAttribute() { return attribute; }
128
129   /**
130    * Bean property getter:  <property>name</property>.
131    *
132    * <p>
133    * Replaces the name of the element/attribute used for the described schema property.
134    *
135    * <p>
136    * When defined within the Items Object (<code>items</code>), it will affect the name of the individual XML elements
137    * within the list.
138    * <br>When defined alongside <code>type</code> being array (outside the <code>items</code>), it will affect the
139    * wrapping element and only if wrapped is <jk>true</jk>.
140    * <br>If wrapped is <jk>false</jk>, it will be ignored.
141    *
142    * @return The property value, or <jk>null</jk> if it is not set.
143    */
144   public String getName() { return name; }
145
146   /**
147    * Bean property getter:  <property>namespace</property>.
148    *
149    * <p>
150    * The URL of the namespace definition. Value SHOULD be in the form of a URL.
151    *
152    * @return The property value, or <jk>null</jk> if it is not set.
153    */
154   public String getNamespace() { return namespace; }
155
156   /**
157    * Bean property getter:  <property>prefix</property>.
158    *
159    * <p>
160    * The prefix to be used for the name.
161    *
162    * @return The property value, or <jk>null</jk> if it is not set.
163    */
164   public String getPrefix() { return prefix; }
165
166   /**
167    * Bean property getter:  <property>wrapped</property>.
168    *
169    * <p>
170    * MAY be used only for an array definition.
171    *
172    * <p>
173    * Signifies whether the array is wrapped (for example,
174    * <code>&lt;books&gt;&lt;book/&gt;&lt;book/&gt;&lt;books&gt;</code>) or unwrapped
175    * (<code>&lt;book/&gt;&lt;book/&gt;</code>).
176    * <br>The definition takes effect only when defined alongside <code>type</code> being <code>array</code>
177    * (outside the <code>items</code>).
178    *
179    * @return The property value, or <jk>null</jk> if it is not set.
180    */
181   public Boolean getWrapped() { return wrapped; }
182
183   @Override /* Overridden from OpenApiElement */
184   public Set<String> keySet() {
185      // @formatter:off
186      var s = setb(String.class)
187         .addIf(nn(attribute), "attribute")
188         .addIf(nn(name), "name")
189         .addIf(nn(namespace), "namespace")
190         .addIf(nn(prefix), "prefix")
191         .addIf(nn(wrapped), "wrapped")
192         .build();
193      // @formatter:on
194      return new MultiSet<>(s, super.keySet());
195   }
196
197   @Override /* Overridden from OpenApiElement */
198   public Xml set(String property, Object value) {
199      assertArgNotNull("property", property);
200      return switch (property) {
201         case "attribute" -> setAttribute(toBoolean(value));
202         case "name" -> setName(s(value));
203         case "namespace" -> setNamespace(s(value));
204         case "prefix" -> setPrefix(s(value));
205         case "wrapped" -> setWrapped(toBoolean(value));
206         default -> {
207            super.set(property, value);
208            yield this;
209         }
210      };
211   }
212
213   /**
214    * Bean property setter:  <property>attribute</property>.
215    *
216    * <p>
217    * Declares whether the property definition translates to an attribute instead of an element.
218    *
219    * @param value
220    *    The new value for this property.
221    *    <br>Default value is <jk>false</jk>.
222    *    <br>Can be <jk>null</jk> to unset the property.
223    * @return This object
224    */
225   public Xml setAttribute(Boolean value) {
226      attribute = value;
227      return this;
228   }
229
230   /**
231    * Bean property setter:  <property>name</property>.
232    *
233    * <p>
234    * Replaces the name of the element/attribute used for the described schema property.
235    *
236    * <p>
237    * When defined within the Items Object (<code>items</code>), it will affect the name of the individual XML elements
238    * within the list.
239    * <br>When defined alongside <code>type</code> being array (outside the <code>items</code>), it will affect the
240    * wrapping element and only if wrapped is <jk>true</jk>.
241    * <br>If wrapped is <jk>false</jk>, it will be ignored.
242    *
243    * @param value
244    *    The new value for this property.
245    *    <br>Can be <jk>null</jk> to unset the property.
246    * @return This object
247    */
248   public Xml setName(String value) {
249      name = value;
250      return this;
251   }
252
253   /**
254    * Bean property setter:  <property>namespace</property>.
255    *
256    * <p>
257    * The URL of the namespace definition. Value SHOULD be in the form of a URL.
258    *
259    * @param value
260    *    The new value for this property.
261    *    <br>Can be <jk>null</jk> to unset the property.
262    * @return This object
263    */
264   public Xml setNamespace(String value) {
265      namespace = value;
266      return this;
267   }
268
269   /**
270    * Bean property setter:  <property>prefix</property>.
271    *
272    * <p>
273    * The prefix to be used for the name.
274    *
275    * @param value
276    *    The new value for this property.
277    *    <br>Can be <jk>null</jk> to unset the property.
278    * @return This object
279    */
280   public Xml setPrefix(String value) {
281      prefix = value;
282      return this;
283   }
284
285   /**
286    * Bean property setter:  <property>wrapped</property>.
287    *
288    * <p>
289    * MAY be used only for an array definition.
290    *
291    * <p>
292    * Signifies whether the array is wrapped (for example,
293    * <code>&lt;books&gt;&lt;book/&gt;&lt;book/&gt;&lt;books&gt;</code>) or unwrapped
294    * (<code>&lt;book/&gt;&lt;book/&gt;</code>).
295    * <br>The definition takes effect only when defined alongside <code>type</code> being <code>array</code>
296    * (outside the <code>items</code>).
297    *
298    * @param value
299    *    The new value for this property.
300    *    <br>Can be <jk>null</jk> to unset the property.
301    * @return This object
302    */
303   public Xml setWrapped(Boolean value) {
304      wrapped = value;
305      return this;
306   }
307
308   @Override /* Overridden from OpenApiElement */
309   public Xml strict() {
310      super.strict();
311      return this;
312   }
313
314   @Override /* Overridden from OpenApiElement */
315   public Xml strict(Object value) {
316      super.strict(value);
317      return this;
318   }
319}