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.common.utils.Utils.*;
020import static org.apache.juneau.internal.CollectionUtils.*;
021import static org.apache.juneau.internal.ConverterUtils.*;
022
023import java.util.*;
024
025import org.apache.juneau.common.utils.*;
026import org.apache.juneau.internal.*;
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
075      name,
076      namespace,
077      prefix;
078   private Boolean
079      attribute,
080      wrapped;
081
082   /**
083    * Default constructor.
084    */
085   public Xml() {}
086
087   /**
088    * Copy constructor.
089    *
090    * @param copyFrom The object to copy.
091    */
092   public Xml(Xml copyFrom) {
093      super(copyFrom);
094
095      this.name = copyFrom.name;
096      this.namespace = copyFrom.namespace;
097      this.prefix = copyFrom.prefix;
098      this.attribute = copyFrom.attribute;
099      this.wrapped = copyFrom.wrapped;
100   }
101
102   /**
103    * Make a deep copy of this object.
104    *
105    * @return A deep copy of this object.
106    */
107   public Xml copy() {
108      return new Xml(this);
109   }
110
111   /**
112    * Bean property getter:  <property>name</property>.
113    *
114    * <p>
115    * Replaces the name of the element/attribute used for the described schema property.
116    *
117    * <p>
118    * When defined within the Items Object (<code>items</code>), it will affect the name of the individual XML elements
119    * within the list.
120    * <br>When defined alongside <code>type</code> being array (outside the <code>items</code>), it will affect the
121    * wrapping element and only if wrapped is <jk>true</jk>.
122    * <br>If wrapped is <jk>false</jk>, it will be ignored.
123    *
124    * @return The property value, or <jk>null</jk> if it is not set.
125    */
126   public String getName() {
127      return name;
128   }
129
130   /**
131    * Bean property setter:  <property>name</property>.
132    *
133    * <p>
134    * Replaces the name of the element/attribute used for the described schema property.
135    *
136    * <p>
137    * When defined within the Items Object (<code>items</code>), it will affect the name of the individual XML elements
138    * within the list.
139    * <br>When defined alongside <code>type</code> being array (outside the <code>items</code>), it will affect the
140    * wrapping element and only if wrapped is <jk>true</jk>.
141    * <br>If wrapped is <jk>false</jk>, it will be ignored.
142    *
143    * @param value
144    *    The new value for this property.
145    *    <br>Can be <jk>null</jk> to unset the property.
146    * @return This object
147    */
148   public Xml setName(String value) {
149      name = value;
150      return this;
151   }
152
153   /**
154    * Bean property getter:  <property>namespace</property>.
155    *
156    * <p>
157    * The URL of the namespace definition. Value SHOULD be in the form of a URL.
158    *
159    * @return The property value, or <jk>null</jk> if it is not set.
160    */
161   public String getNamespace() {
162      return namespace;
163   }
164
165   /**
166    * Bean property setter:  <property>namespace</property>.
167    *
168    * <p>
169    * The URL of the namespace definition. Value SHOULD be in the form of a URL.
170    *
171    * @param value
172    *    The new value for this property.
173    *    <br>Can be <jk>null</jk> to unset the property.
174    * @return This object
175    */
176   public Xml setNamespace(String value) {
177      namespace = value;
178      return this;
179   }
180
181   /**
182    * Bean property getter:  <property>prefix</property>.
183    *
184    * <p>
185    * The prefix to be used for the name.
186    *
187    * @return The property value, or <jk>null</jk> if it is not set.
188    */
189   public String getPrefix() {
190      return prefix;
191   }
192
193   /**
194    * Bean property setter:  <property>prefix</property>.
195    *
196    * <p>
197    * The prefix to be used for the name.
198    *
199    * @param value
200    *    The new value for this property.
201    *    <br>Can be <jk>null</jk> to unset the property.
202    * @return This object
203    */
204   public Xml setPrefix(String value) {
205      prefix = value;
206      return this;
207   }
208
209   /**
210    * Bean property getter:  <property>attribute</property>.
211    *
212    * <p>
213    * Declares whether the property definition translates to an attribute instead of an element.
214    *
215    * @return The property value, or <jk>null</jk> if it is not set.
216    */
217   public Boolean getAttribute() {
218      return attribute;
219   }
220
221   /**
222    * Bean property setter:  <property>attribute</property>.
223    *
224    * <p>
225    * Declares whether the property definition translates to an attribute instead of an element.
226    *
227    * @param value
228    *    The new value for this property.
229    *    <br>Default value is <jk>false</jk>.
230    *    <br>Can be <jk>null</jk> to unset the property.
231    * @return This object
232    */
233   public Xml setAttribute(Boolean value) {
234      attribute = value;
235      return this;
236   }
237
238   /**
239    * Bean property getter:  <property>wrapped</property>.
240    *
241    * <p>
242    * MAY be used only for an array definition.
243    *
244    * <p>
245    * Signifies whether the array is wrapped (for example,
246    * <code>&lt;books&gt;&lt;book/&gt;&lt;book/&gt;&lt;books&gt;</code>) or unwrapped
247    * (<code>&lt;book/&gt;&lt;book/&gt;</code>).
248    * <br>The definition takes effect only when defined alongside <code>type</code> being <code>array</code>
249    * (outside the <code>items</code>).
250    *
251    * @return The property value, or <jk>null</jk> if it is not set.
252    */
253   public Boolean getWrapped() {
254      return wrapped;
255   }
256
257   /**
258    * Bean property setter:  <property>wrapped</property>.
259    *
260    * <p>
261    * MAY be used only for an array definition.
262    *
263    * <p>
264    * Signifies whether the array is wrapped (for example,
265    * <code>&lt;books&gt;&lt;book/&gt;&lt;book/&gt;&lt;books&gt;</code>) or unwrapped
266    * (<code>&lt;book/&gt;&lt;book/&gt;</code>).
267    * <br>The definition takes effect only when defined alongside <code>type</code> being <code>array</code>
268    * (outside the <code>items</code>).
269    *
270    * @param value
271    *    The new value for this property.
272    *    <br>Can be <jk>null</jk> to unset the property.
273    * @return This object
274    */
275   public Xml setWrapped(Boolean value) {
276      this.wrapped = value;
277      return this;
278   }
279
280   @Override /* Overridden from OpenApiElement */
281   public <T> T get(String property, Class<T> type) {
282      assertArgNotNull("property", property);
283      return switch (property) {
284         case "name" -> toType(getName(), type);
285         case "namespace" -> toType(getNamespace(), type);
286         case "prefix" -> toType(getPrefix(), type);
287         case "attribute" -> toType(getAttribute(), type);
288         case "wrapped" -> toType(getWrapped(), type);
289         default -> super.get(property, type);
290      };
291   }
292
293   @Override /* Overridden from OpenApiElement */
294   public Xml set(String property, Object value) {
295      assertArgNotNull("property", property);
296      return switch (property) {
297         case "attribute" -> setAttribute(toBoolean(value));
298         case "name" -> setName(Utils.s(value));
299         case "namespace" -> setNamespace(Utils.s(value));
300         case "prefix" -> setPrefix(Utils.s(value));
301         case "wrapped" -> setWrapped(toBoolean(value));
302         default -> {
303            super.set(property, value);
304            yield this;
305         }
306      };
307   }
308
309   @Override /* Overridden from OpenApiElement */
310   public Set<String> keySet() {
311      var s = setBuilder(String.class)
312         .addIf(attribute != null, "attribute")
313         .addIf(name != null, "name")
314         .addIf(namespace != null, "namespace")
315         .addIf(prefix != null, "prefix")
316         .addIf(wrapped != null, "wrapped")
317         .build();
318      return new MultiSet<>(s, super.keySet());
319   }
320
321   @Override /* Overridden from OpenApiElement */
322   public Xml strict() {
323      super.strict();
324      return this;
325   }
326
327   @Override /* Overridden from OpenApiElement */
328   public Xml strict(Object value) {
329      super.strict(value);
330      return this;
331   }
332
333}