001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2009-2010 Sun Microsystems, Inc.
025 *      Portions copyright 2012-2014 ForgeRock AS.
026 */
027package org.forgerock.opendj.ldap;
028
029import org.forgerock.i18n.LocalizableMessage;
030import org.forgerock.i18n.LocalizedIllegalArgumentException;
031import org.forgerock.opendj.ldap.schema.Schema;
032import org.forgerock.util.Function;
033import org.forgerock.util.promise.NeverThrowsException;
034
035import com.forgerock.opendj.util.StaticUtils;
036
037import static org.forgerock.opendj.ldap.schema.Schema.*;
038
039import static com.forgerock.opendj.ldap.CoreMessages.*;
040
041/**
042 * Common {@link Function} implementations which may be used when parsing
043 * attributes.
044 *
045 * @see Entry#parseAttribute
046 * @see Attribute#parse
047 * @see AttributeParser
048 */
049public final class Functions {
050
051    private static final Function<ByteString, String, NeverThrowsException> BYTESTRING_TO_STRING =
052            new Function<ByteString, String, NeverThrowsException>() {
053                public String apply(final ByteString value) {
054                    return value.toString();
055                }
056            };
057
058    private static final Function<Object, Object, NeverThrowsException> IDENTITY =
059            new Function<Object, Object, NeverThrowsException>() {
060                public Object apply(final Object value) {
061                    return value;
062                }
063            };
064
065    private static final Function<String, String, NeverThrowsException> NORMALIZE_STRING =
066            new Function<String, String, NeverThrowsException>() {
067                public String apply(final String value) {
068                    return StaticUtils.toLowerCase(value).trim();
069                }
070            };
071
072    private static final Function<Object, ByteString, NeverThrowsException> OBJECT_TO_BYTESTRING =
073            new Function<Object, ByteString, NeverThrowsException>() {
074                public ByteString apply(final Object value) {
075                    return ByteString.valueOf(value);
076                }
077            };
078
079    private static final Function<String, Boolean, NeverThrowsException> STRING_TO_BOOLEAN =
080            new Function<String, Boolean, NeverThrowsException>() {
081                public Boolean apply(final String value) {
082                    final String valueString = StaticUtils.toLowerCase(value);
083                    if ("true".equals(valueString) || "yes".equals(valueString)
084                            || "on".equals(valueString) || "1".equals(valueString)) {
085                        return Boolean.TRUE;
086                    } else if ("false".equals(valueString) || "no".equals(valueString)
087                            || "off".equals(valueString) || "0".equals(valueString)) {
088                        return Boolean.FALSE;
089                    } else {
090                        throw new LocalizedIllegalArgumentException(
091                                WARN_ATTR_SYNTAX_ILLEGAL_BOOLEAN.get(valueString));
092                    }
093                }
094            };
095
096    private static final Function<String, GeneralizedTime, NeverThrowsException> STRING_TO_GENERALIZED_TIME =
097            new Function<String, GeneralizedTime, NeverThrowsException>() {
098                public GeneralizedTime apply(final String value) {
099                    return GeneralizedTime.valueOf(value);
100                }
101            };
102
103    private static final Function<String, Integer, NeverThrowsException> STRING_TO_INTEGER =
104            new Function<String, Integer, NeverThrowsException>() {
105                public Integer apply(final String value) {
106                    try {
107                        return Integer.valueOf(value);
108                    } catch (final NumberFormatException e) {
109                        final LocalizableMessage message = FUNCTIONS_TO_INTEGER_FAIL.get(value);
110                        throw new LocalizedIllegalArgumentException(message);
111                    }
112                }
113            };
114
115    private static final Function<String, Long, NeverThrowsException> STRING_TO_LONG =
116            new Function<String, Long, NeverThrowsException>() {
117                public Long apply(final String value) {
118                    try {
119                        return Long.valueOf(value);
120                    } catch (final NumberFormatException e) {
121                        final LocalizableMessage message = FUNCTIONS_TO_LONG_FAIL.get(value);
122                        throw new LocalizedIllegalArgumentException(message);
123                    }
124                }
125            };
126
127    private static final Function<ByteString, Boolean, NeverThrowsException> BYTESTRING_TO_BOOLEAN = compose(
128            byteStringToString(), STRING_TO_BOOLEAN);
129
130    private static final Function<ByteString, GeneralizedTime, NeverThrowsException> BYTESTRING_TO_GENERALIZED_TIME =
131            compose(byteStringToString(), STRING_TO_GENERALIZED_TIME);
132
133    private static final Function<ByteString, Integer, NeverThrowsException> BYTESTRING_TO_INTEGER = compose(
134            byteStringToString(), STRING_TO_INTEGER);
135
136    private static final Function<ByteString, Long, NeverThrowsException> BYTESTRING_TO_LONG = compose(
137            byteStringToString(), STRING_TO_LONG);
138
139    /**
140     * Returns the composition of two functions. The result of the first
141     * function will be passed to the second.
142     *
143     * @param <M>
144     *            The type of input values transformed by this function.
145     * @param <N>
146     *            The type of output values returned by this function.
147     * @param <X>
148     *            The type of intermediate values passed between the two
149     *            functions.
150     * @param first
151     *            The first function which will consume the input.
152     * @param second
153     *            The second function which will produce the result.
154     * @return The composition.
155     */
156    public static <M, X, N> Function<M, N, NeverThrowsException> compose(
157            final Function<M, X, NeverThrowsException> first, final Function<X, N, NeverThrowsException> second) {
158        return new Function<M, N, NeverThrowsException>() {
159            public N apply(final M value) {
160                return second.apply(first.apply(value));
161            }
162        };
163    }
164
165    /**
166     * Returns a function which always returns the value that it was provided
167     * with.
168     *
169     * @param <M>
170     *            The type of values transformed by this function.
171     * @return A function which always returns the value that it was provided
172     *         with.
173     */
174    @SuppressWarnings("unchecked")
175    public static <M> Function<M, M, NeverThrowsException> identityFunction() {
176        return (Function<M, M, NeverThrowsException>) IDENTITY;
177    }
178
179    /**
180     * Returns a function which converts a {@code String} to lower case using
181     * {@link StaticUtils#toLowerCase} and then trims it.
182     *
183     * @return A function which converts a {@code String} to lower case using
184     *         {@link StaticUtils#toLowerCase} and then trims it.
185     */
186    public static Function<String, String, NeverThrowsException> normalizeString() {
187        return NORMALIZE_STRING;
188    }
189
190    /**
191     * Returns a function which converts an {@code Object} to a
192     * {@code ByteString} using the {@link ByteString#valueOf(Object)} method.
193     *
194     * @return A function which converts an {@code Object} to a
195     *         {@code ByteString} .
196     */
197    public static Function<Object, ByteString, NeverThrowsException> objectToByteString() {
198        return OBJECT_TO_BYTESTRING;
199    }
200
201    /**
202     * Returns a function which parses {@code AttributeDescription}s using the
203     * default schema. Invalid values will result in a
204     * {@code LocalizedIllegalArgumentException}.
205     *
206     * @return A function which parses {@code AttributeDescription}s.
207     */
208    public static Function<String, AttributeDescription, NeverThrowsException> stringToAttributeDescription() {
209        return stringToAttributeDescription(getDefaultSchema());
210    }
211
212    /**
213     * Returns a function which parses {@code AttributeDescription}s using the
214     * provided schema. Invalid values will result in a
215     * {@code LocalizedIllegalArgumentException}.
216     *
217     * @param schema
218     *            The schema to use for decoding attribute descriptions.
219     * @return A function which parses {@code AttributeDescription}s.
220     */
221    public static Function<String, AttributeDescription, NeverThrowsException> stringToAttributeDescription(
222            final Schema schema) {
223        return new Function<String, AttributeDescription, NeverThrowsException>() {
224            public AttributeDescription apply(final String value) {
225                return AttributeDescription.valueOf(value, schema);
226            }
227        };
228    }
229
230    /**
231     * Returns a function which parses {@code Boolean} values. The function will
232     * accept the values {@code 0}, {@code false}, {@code no}, {@code off},
233     * {@code 1}, {@code true}, {@code yes}, {@code on}. All other values will
234     * result in a {@code NumberFormatException}.
235     *
236     * @return A function which parses {@code Boolean} values.
237     */
238    public static Function<String, Boolean, NeverThrowsException> stringToBoolean() {
239        return STRING_TO_BOOLEAN;
240    }
241
242    /**
243     * Returns a function which parses {@code DN}s using the default schema.
244     * Invalid values will result in a {@code LocalizedIllegalArgumentException}
245     * .
246     *
247     * @return A function which parses {@code DN}s.
248     */
249    public static Function<String, DN, NeverThrowsException> stringToDN() {
250        return stringToDN(getDefaultSchema());
251    }
252
253    /**
254     * Returns a function which parses {@code DN}s using the provided schema.
255     * Invalid values will result in a {@code LocalizedIllegalArgumentException}
256     * .
257     *
258     * @param schema
259     *            The schema to use for decoding DNs.
260     * @return A function which parses {@code DN}s.
261     */
262    public static Function<String, DN, NeverThrowsException> stringToDN(final Schema schema) {
263        return new Function<String, DN, NeverThrowsException>() {
264            public DN apply(final String value) {
265                return DN.valueOf(value, schema);
266            }
267        };
268    }
269
270    /**
271     * Returns a function which parses generalized time strings. Invalid values
272     * will result in a {@code LocalizedIllegalArgumentException}.
273     *
274     * @return A function which parses generalized time strings.
275     */
276    public static Function<String, GeneralizedTime, NeverThrowsException> stringToGeneralizedTime() {
277        return STRING_TO_GENERALIZED_TIME;
278    }
279
280    /**
281     * Returns a function which parses {@code Integer} string values. Invalid
282     * values will result in a {@code LocalizedIllegalArgumentException}.
283     *
284     * @return A function which parses {@code Integer} string values.
285     */
286    public static Function<String, Integer, NeverThrowsException> stringToInteger() {
287        return STRING_TO_INTEGER;
288    }
289
290    /**
291     * Returns a function which parses {@code Long} string values. Invalid
292     * values will result in a {@code LocalizedIllegalArgumentException}.
293     *
294     * @return A function which parses {@code Long} string values.
295     */
296    public static Function<String, Long, NeverThrowsException> stringToLong() {
297        return STRING_TO_LONG;
298    }
299
300    /**
301     * Returns a function which parses {@code AttributeDescription}s using the
302     * default schema. Invalid values will result in a
303     * {@code LocalizedIllegalArgumentException}.
304     *
305     * @return A function which parses {@code AttributeDescription}s.
306     */
307    public static Function<ByteString, AttributeDescription, NeverThrowsException> byteStringToAttributeDescription() {
308        return byteStringToAttributeDescription(getDefaultSchema());
309    }
310
311    /**
312     * Returns a function which parses {@code AttributeDescription}s using the
313     * provided schema. Invalid values will result in a
314     * {@code LocalizedIllegalArgumentException}.
315     *
316     * @param schema
317     *            The schema to use for decoding attribute descriptions.
318     * @return A function which parses {@code AttributeDescription}s.
319     */
320    public static Function<ByteString, AttributeDescription, NeverThrowsException> byteStringToAttributeDescription(
321            final Schema schema) {
322        return compose(byteStringToString(), new Function<String, AttributeDescription, NeverThrowsException>() {
323            public AttributeDescription apply(final String value) {
324                return AttributeDescription.valueOf(value, schema);
325            }
326        });
327    }
328
329    /**
330     * Returns a function which parses {@code Boolean} values. The function will
331     * accept the values {@code 0}, {@code false}, {@code no}, {@code off},
332     * {@code 1}, {@code true}, {@code yes}, {@code on}. All other values will
333     * result in a {@code NumberFormatException}.
334     *
335     * @return A function which parses {@code Boolean} values.
336     */
337    public static Function<ByteString, Boolean, NeverThrowsException> byteStringToBoolean() {
338        return BYTESTRING_TO_BOOLEAN;
339    }
340
341    /**
342     * Returns a function which parses {@code DN}s using the default schema.
343     * Invalid values will result in a {@code LocalizedIllegalArgumentException}
344     * .
345     *
346     * @return A function which parses {@code DN}s.
347     */
348    public static Function<ByteString, DN, NeverThrowsException> byteStringToDN() {
349        return byteStringToDN(getDefaultSchema());
350    }
351
352    /**
353     * Returns a function which parses {@code DN}s using the provided schema.
354     * Invalid values will result in a {@code LocalizedIllegalArgumentException}
355     * .
356     *
357     * @param schema
358     *            The schema to use for decoding DNs.
359     * @return A function which parses {@code DN}s.
360     */
361    public static Function<ByteString, DN, NeverThrowsException> byteStringToDN(final Schema schema) {
362        return compose(byteStringToString(), new Function<String, DN, NeverThrowsException>() {
363            public DN apply(final String value) {
364                return DN.valueOf(value, schema);
365            }
366        });
367    }
368
369    /**
370     * Returns a function which parses generalized time strings. Invalid values
371     * will result in a {@code LocalizedIllegalArgumentException}.
372     *
373     * @return A function which parses generalized time strings.
374     */
375    public static Function<ByteString, GeneralizedTime, NeverThrowsException> byteStringToGeneralizedTime() {
376        return BYTESTRING_TO_GENERALIZED_TIME;
377    }
378
379    /**
380     * Returns a function which parses {@code Integer} string values. Invalid
381     * values will result in a {@code LocalizedIllegalArgumentException}.
382     *
383     * @return A function which parses {@code Integer} string values.
384     */
385    public static Function<ByteString, Integer, NeverThrowsException> byteStringToInteger() {
386        return BYTESTRING_TO_INTEGER;
387    }
388
389    /**
390     * Returns a function which parses {@code Long} string values. Invalid
391     * values will result in a {@code LocalizedIllegalArgumentException}.
392     *
393     * @return A function which parses {@code Long} string values.
394     */
395    public static Function<ByteString, Long, NeverThrowsException> byteStringToLong() {
396        return BYTESTRING_TO_LONG;
397    }
398
399    /**
400     * Returns a function which parses a {@code ByteString} as a UTF-8 encoded
401     * {@code String}.
402     *
403     * @return A function which parses the string representation of a
404     *         {@code ByteString} as a UTF-8 encoded {@code String}.
405     */
406    public static Function<ByteString, String, NeverThrowsException> byteStringToString() {
407        return BYTESTRING_TO_STRING;
408    }
409
410    /** Prevent instantiation. */
411    private Functions() {
412        // Do nothing.
413    }
414
415}