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.internal;
018
019import static org.apache.juneau.commons.utils.CollectionUtils.*;
020import static org.apache.juneau.commons.utils.StringUtils.*;
021import static org.apache.juneau.commons.utils.ThrowableUtils.*;
022
023import java.util.*;
024
025import org.apache.juneau.*;
026import org.apache.juneau.commons.collections.*;
027import org.apache.juneau.parser.*;
028import org.apache.juneau.swap.*;
029import org.apache.juneau.utils.*;
030
031/**
032 * Utility class for efficiently converting objects between types.
033 *
034 * <p>
035 * If the value isn't an instance of the specified type, then converts the value if possible.
036 *
037 * <p>
038 * The following conversions are valid:
039 * <table class='styled'>
040 *    <tr><th>Convert to type</th><th>Valid input value types</th><th>Notes</th></tr>
041 *    <tr>
042 *       <td>
043 *          A class that is the normal type of a registered {@link ObjectSwap}.
044 *       </td>
045 *       <td>
046 *          A value whose class matches the transformed type of that registered {@link ObjectSwap}.
047 *       </td>
048 *       <td>&nbsp;</td>
049 *    </tr>
050 *    <tr>
051 *       <td>
052 *          A class that is the transformed type of a registered {@link ObjectSwap}.
053 *       </td>
054 *       <td>
055 *          A value whose class matches the normal type of that registered {@link ObjectSwap}.
056 *       </td>
057 *       <td>&nbsp;</td>
058 *    </tr>
059 *    <tr>
060 *       <td>
061 *          {@code Number} (e.g. {@code Integer}, {@code Short}, {@code Float},...)
062 *          <br><code>Number.<jsf>TYPE</jsf></code> (e.g. <code>Integer.<jsf>TYPE</jsf></code>,
063 *          <code>Short.<jsf>TYPE</jsf></code>, <code>Float.<jsf>TYPE</jsf></code>,...)
064 *       </td>
065 *       <td>
066 *          {@code Number}, {@code String}, <jk>null</jk>
067 *       </td>
068 *       <td>
069 *          For primitive {@code TYPES}, <jk>null</jk> returns the JVM default value for that type.
070 *       </td>
071 *    </tr>
072 *    <tr>
073 *       <td>
074 *          {@code Map} (e.g. {@code Map}, {@code HashMap}, {@code TreeMap}, {@code JsonMap})
075 *       </td>
076 *       <td>
077 *          {@code Map}
078 *       </td>
079 *       <td>
080 *          If {@code Map} is not constructible, an {@code JsonMap} is created.
081 *       </td>
082 *    </tr>
083 *    <tr>
084 *       <td>
085 *          <c>Collection</c> (e.g. <c>List</c>, <c>LinkedList</c>, <c>HashSet</c>, <c>JsonList</c>)
086 *       </td>
087 *       <td>
088 *          <c>Collection&lt;Object&gt;</c>
089 *          <br><c>Object[]</c>
090 *       </td>
091 *       <td>
092 *          If <c>Collection</c> is not constructible, a <c>JsonList</c> is created.
093 *       </td>
094 *    </tr>
095 *    <tr>
096 *       <td>
097 *          <c>X[]</c> (array of any type X)
098 *       </td>
099 *       <td>
100 *          <c>List&lt;X&gt;</c>
101 *       </td>
102 *       <td>&nbsp;</td>
103 *    </tr>
104 *    <tr>
105 *       <td>
106 *          <c>X[][]</c> (multi-dimensional arrays)
107 *       </td>
108 *       <td>
109 *          <c>List&lt;List&lt;X&gt;&gt;</c>
110 *          <br><c>List&lt;X[]&gt;</c>
111 *          <br><c> List[]&lt;X&gt;</c>
112 *       </td>
113 *       <td>&nbsp;</td>
114 *    </tr>
115 *    <tr>
116 *       <td>
117 *          <c>Enum</c>
118 *       </td>
119 *       <td>
120 *          <c>String</c>
121 *       </td>
122 *       <td>&nbsp;</td>
123 *    </tr>
124 *    <tr>
125 *       <td>
126 *          Bean
127 *       </td>
128 *       <td>
129 *          <c>Map</c>
130 *       </td>
131 *       <td>&nbsp;</td>
132 *    </tr>
133 *    <tr>
134 *       <td>
135 *          <c>String</c>
136 *       </td>
137 *       <td>
138 *          Anything
139 *       </td>
140 *       <td>
141 *          Arrays are converted to JSON arrays
142 *       </td>
143 *    </tr>
144 *    <tr>
145 *       <td>
146 *          Anything with one of the following methods:
147 *          <br><code><jk>public static</jk> T fromString(String)</code>
148 *          <br><code><jk>public static</jk> T valueOf(String)</code>
149 *          <br><code><jk>public</jk> T(String)</code>
150 *       </td>
151 *       <td>
152 *          <c>String</c>
153 *       </td>
154 *       <td>
155 *          <br>
156 *       </td>
157 *    </tr>
158 * </table>
159 *
160 */
161public class ConverterUtils {
162
163   // Session objects are usually not thread safe, but we're not using any feature
164   // of bean sessions that would cause thread safety issues.
165   private static final BeanSession session = BeanContext.DEFAULT_SESSION;
166
167   /**
168    * Converts an object to a Boolean.
169    *
170    * @param o The object to convert.
171    * @return The converted object.
172    */
173   public static Boolean toBoolean(Object o) {
174      return toType(o, Boolean.class);
175   }
176
177   /**
178    * Converts an object to an Integer.
179    *
180    * @param o The object to convert.
181    * @return The converted object.
182    */
183   public static Integer toInteger(Object o) {
184      return toType(o, Integer.class);
185   }
186
187   /**
188    * Converts an object to a Number.
189    *
190    * @param o The object to convert.
191    * @return The converted object.
192    */
193   public static Number toNumber(Object o) {
194      if (o == null)
195         return null;
196      if (o instanceof Number o2)
197         return o2;
198      try {
199         return parseNumber(o.toString(), null);
200      } catch (ParseException e) {
201         throw toRex(e);
202      }
203   }
204
205   /**
206    * Converts the specified object to the specified type.
207    *
208    * @param <T> The class type to convert the value to.
209    * @param value The value to convert.
210    * @param type The class type to convert the value to.
211    * @throws InvalidDataConversionException If the specified value cannot be converted to the specified type.
212    * @return The converted value.
213    */
214   public static <T> T toType(Object value, Class<T> type) {
215      return session.convertToType(value, type);
216   }
217
218   /**
219    * Converts the specified object to a {@link Lists} with elements of the specified type.
220    *
221    * <p>
222    * The input value can be any of the following:
223    * <ul>
224    *    <li>An array of objects convertible to the element type
225    *    <li>A {@link Collection} of objects convertible to the element type
226    *    <li>A single object convertible to the element type (creates a list with one element)
227    *    <li>A JSON array string that can be parsed into objects of the element type
228    * </ul>
229    *
230    * @param <T> The element type.
231    * @param value The value to convert. Can be <jk>null</jk>.
232    * @param type The element type class.
233    * @return A new {@link Lists} containing the converted elements.
234    */
235   public static <T> Lists<T> toListBuilder(Object value, Class<T> type) {
236      return listb(type).elementFunction(o -> GenericConverter.INSTANCE.convertTo(type, o)).addAny(value);
237   }
238
239   /**
240    * Converts the specified object to a {@link Maps} with keys and values of the specified types.
241    *
242    * <p>
243    * The input value can be any of the following:
244    * <ul>
245    *    <li>A {@link java.util.Map Map} with entries convertible to the key/value types
246    *    <li>A JSON object string that can be parsed into a map with the specified key/value types
247    *    <li>An object with bean properties that can be converted to map entries
248    * </ul>
249    *
250    * @param <K> The key type.
251    * @param <V> The value type.
252    * @param value The value to convert. Can be <jk>null</jk>.
253    * @param keyType The key type class.
254    * @param valueType The value type class.
255    * @return A new {@link Maps} containing the converted entries.
256    */
257   public static <K,V> Maps<K,V> toMapBuilder(Object value, Class<K> keyType, Class<V> valueType) {
258      return mapb(keyType, valueType)
259         .keyFunction(o -> GenericConverter.INSTANCE.convertTo(keyType, o))
260         .valueFunction(o -> GenericConverter.INSTANCE.convertTo(valueType, o))
261         .addAny(value);
262   }
263
264   /**
265    * Converts the specified object to a {@link Sets} with elements of the specified type.
266    *
267    * <p>
268    * The input value can be any of the following:
269    * <ul>
270    *    <li>An array of objects convertible to the element type
271    *    <li>A {@link Collection} of objects convertible to the element type
272    *    <li>A single object convertible to the element type (creates a set with one element)
273    *    <li>A JSON array string that can be parsed into objects of the element type
274    * </ul>
275    *
276    * <p>
277    * Duplicate elements (after conversion) will be automatically removed as per {@link java.util.Set Set} semantics.
278    *
279    * @param <T> The element type.
280    * @param value The value to convert. Can be <jk>null</jk>.
281    * @param type The element type class.
282    * @return A new {@link Sets} containing the converted elements.
283    */
284   public static <T> Sets<T> toSetBuilder(Object value, Class<T> type) {
285      return setb(type).elementFunction(o -> GenericConverter.INSTANCE.convertTo(type, o)).addAny(value);
286   }
287}