001/*
002 * Configurate
003 * Copyright (C) zml and Configurate contributors
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * 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 ninja.leaping.configurate;
018
019import org.checkerframework.checker.nullness.qual.Nullable;
020
021/**
022 * Contains functions useful for performing configuration type conversions.
023 *
024 * <p>The naming scheme is as follows:</p>
025 *
026 * <ul>
027 *     <li><code>as</code> methods attempt to convert the data passed to the appropriate type</li>
028 *     <li><code>strictAs</code> methods will only return values if the input value is already of an appropriate type</li>
029 * </ul>
030 */
031public final class Types {
032    private Types() {}
033
034    /**
035     * Attempts to convert <code>value</code> to a {@link String}.
036     *
037     * <p>Returns null if <code>value</code> is null, and the {@link Object#toString()}
038     * representation of <code>value</code> otherwise.</p>
039     *
040     * @param value The value
041     * @return <code>value</code> as a {@link String}, or null
042     * @see Object#toString()
043     */
044    @Nullable
045    public static String asString(@Nullable Object value) {
046        return value == null ? null : value.toString();
047    }
048
049    /**
050     * Returns <code>value</code> if it is a {@link String}.
051     *
052     * @param value The value
053     * @return <code>value</code> as a {@link String}, or null
054     */
055    @Nullable
056    public static String strictAsString(@Nullable Object value) {
057        return value instanceof String ? (String) value : null;
058    }
059
060    /**
061     * Attempts to convert <code>value</code> to a {@link Float}.
062     *
063     * <p>Returns null if <code>value</code> is null.</p>
064     *
065     * <p>This method will attempt to cast <code>value</code> to {@link Float}, or
066     * {@link Float#parseFloat(String) parse} the <code>value</code> if it is a {@link String}.</p>
067     *
068     * @param value The value
069     * @return <code>value</code> as a {@link Float}, or null
070     */
071    @Nullable
072    public static Float asFloat(@Nullable Object value) {
073        if (value == null) {
074            return null;
075        }
076
077        if (value instanceof Float) {
078            return (Float) value;
079        } else if (value instanceof Integer) {
080            return ((Number) value).floatValue();
081        }
082
083        try {
084            return Float.parseFloat(value.toString());
085        } catch (IllegalArgumentException ex) {
086            return null;
087        }
088    }
089
090    /**
091     * Returns <code>value</code> if it is a {@link Float}.
092     *
093     * @param value The value
094     * @return <code>value</code> as a {@link Float}, or null
095     */
096    @Nullable
097    public static Float strictAsFloat(@Nullable Object value) {
098        if (value == null) {
099            return null;
100        }
101
102        if (value instanceof Float
103                || value instanceof Integer) {
104            return ((Number) value).floatValue();
105        }
106
107        return null;
108    }
109
110    /**
111     * Attempts to convert <code>value</code> to a {@link Double}.
112     *
113     * <p>Returns null if <code>value</code> is null.</p>
114     *
115     * <p>This method will attempt to cast <code>value</code> to {@link Double}, or
116     * {@link Double#parseDouble(String) parse} the <code>value</code> if it is a {@link String}.</p>
117     *
118     * @param value The value
119     * @return <code>value</code> as a {@link Float}, or null
120     */
121    @Nullable
122    public static Double asDouble(@Nullable Object value) {
123        if (value == null) {
124            return null;
125        }
126
127        if (value instanceof Double) {
128            return (Double) value;
129        } else if (value instanceof Integer
130                || value instanceof Long
131                || value instanceof Float) {
132            return ((Number) value).doubleValue();
133        }
134
135        try {
136            return Double.parseDouble(value.toString());
137        } catch (IllegalArgumentException ex) {
138            return null;
139        }
140    }
141
142    /**
143     * Returns <code>value</code> if it is a {@link Double}.
144     *
145     * @param value The value
146     * @return <code>value</code> as a {@link Double}, or null
147     */
148    @Nullable
149    public static Double strictAsDouble(@Nullable Object value) {
150        if (value == null) {
151            return null;
152        }
153
154        if (value instanceof Double
155                || value instanceof Float
156                || value instanceof Integer
157                || value instanceof Long) {
158            return ((Number) value).doubleValue();
159        }
160
161        return null;
162    }
163
164    /**
165     * Attempts to convert <code>value</code> to a {@link Integer}.
166     *
167     * <p>Returns null if <code>value</code> is null.</p>
168     *
169     * <p>This method will attempt to cast <code>value</code> to {@link Integer}, or
170     * {@link Integer#parseInt(String) parse} the <code>value</code> if it is a {@link String}.</p>
171     *
172     * @param value The value
173     * @return <code>value</code> as a {@link Float}, or null
174     */
175    @Nullable
176    public static Integer asInt(@Nullable Object value) {
177        if (value == null) {
178            return null;
179        }
180
181        if (value instanceof Integer) {
182            return (Integer) value;
183        }
184
185        if (value instanceof Float
186            || value instanceof Double) {
187            double val = ((Number) value).doubleValue();
188            if (val == Math.floor(val)) {
189                return (int) val;
190            }
191        }
192
193        try {
194            return Integer.parseInt(value.toString());
195        } catch (IllegalArgumentException ex) {
196            return null;
197        }
198    }
199
200    /**
201     * Returns <code>value</code> if it is a {@link Integer}.
202     *
203     * @param value The value
204     * @return <code>value</code> as a {@link Integer}, or null
205     */
206    @Nullable
207    public static Integer strictAsInt(@Nullable Object value) {
208        if (value == null) {
209            return null;
210        }
211
212        return value instanceof Integer ? (Integer) value : null;
213    }
214
215    /**
216     * Attempts to convert <code>value</code> to a {@link Long}.
217     *
218     * <p>Returns null if <code>value</code> is null.</p>
219     *
220     * <p>This method will attempt to cast <code>value</code> to {@link Long}, or
221     * {@link Long#parseLong(String) parse} the <code>value</code> if it is a {@link String}.</p>
222     *
223     * @param value The value
224     * @return <code>value</code> as a {@link Float}, or null
225     */
226    @Nullable
227    public static Long asLong(@Nullable Object value) {
228        if (value == null) {
229            return null;
230        }
231
232        if (value instanceof Long) {
233            return (Long) value;
234        } else if (value instanceof Integer) {
235            return ((Number) value).longValue();
236        }
237
238        if (value instanceof Float
239                || value instanceof Double) {
240            double val = ((Number) value).doubleValue();
241            if (val == Math.floor(val)) {
242                return (long) val;
243            }
244        }
245
246        try {
247            return Long.parseLong(value.toString());
248        } catch (IllegalArgumentException ex) {
249            return null;
250        }
251    }
252
253    /**
254     * Returns <code>value</code> if it is a {@link Long}.
255     *
256     * @param value The value
257     * @return <code>value</code> as a {@link Long}, or null
258     */
259    @Nullable
260    public static Long strictAsLong(@Nullable Object value) {
261        if (value == null) {
262            return null;
263        }
264
265        if (value instanceof Long) {
266            return (Long) value;
267        } else if (value instanceof Integer) {
268            return ((Number) value).longValue();
269        }
270
271        return null;
272    }
273
274    /**
275     * Attempts to convert <code>value</code> to a {@link Boolean}.
276     *
277     * <ul>
278     *     <li>If <code>value</code> is a {@link Boolean}, casts and returns</li>
279     *     <li>If <code>value</code> is a {@link Number}, returns true if value is not 0</li>
280     *     <li>If <code>value.toString()</code> returns true, t, yes, y, or 1, returns true</li>
281     *     <li>If <code>value.toString()</code> returns false, f, no, n, or 0, returns false</li>
282     *     <li>Otherwise returns null</li>
283     * </ul>
284     *
285     * @param value The value
286     * @return <code>value</code> as a {@link Boolean}, or null
287     */
288    @Nullable
289    public static Boolean asBoolean(@Nullable Object value) {
290        if (value == null) {
291            return null;
292        }
293
294        if (value instanceof Boolean) {
295            return (Boolean) value;
296        }
297
298        if (value instanceof Number) {
299            return !value.equals(0);
300        }
301
302        final String potential = value.toString();
303        if (potential.equals("true")
304                || potential.equals("t")
305                || potential.equals("yes")
306                || potential.equals("y")
307                || potential.equals("1")) {
308            return true;
309        } else if (potential.equals("false")
310                || potential.equals("f")
311                || potential.equals("no")
312                || potential.equals("n")
313                || potential.equals("0")) {
314            return false;
315        }
316
317        return null;
318    }
319
320    /**
321     * Returns <code>value</code> if it is a {@link Boolean}.
322     *
323     * @param value The value
324     * @return <code>value</code> as a {@link Boolean}, or null
325     */
326    @Nullable
327    public static Boolean strictAsBoolean(@Nullable Object value) {
328        if (value == null) {
329            return null;
330        }
331
332        return value instanceof Boolean ? (Boolean) value : null;
333    }
334}