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