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