/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2012-2015 ForgeRock AS. */ package org.opends.server.types; import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashSet; import java.util.NoSuchElementException; import java.util.Set; import org.forgerock.opendj.ldap.ByteString; import org.forgerock.opendj.ldap.Functions; import org.forgerock.opendj.ldap.GeneralizedTime; import org.forgerock.opendj.ldap.schema.Schema; import org.forgerock.util.Function; import org.forgerock.util.promise.NeverThrowsException; /** * A fluent API for parsing attributes as different types of object. An * attribute parser is obtained from an entry using the method * {@link Entry#parseAttribute} or from an attribute using * {@link Attribute#parse}. * <p> * Methods throw an {@code IllegalArgumentException} when a value cannot be * parsed (e.g. because its syntax is invalid). Methods which return a * {@code Set} always return a modifiable non-{@code null} result, even if the * attribute is {@code null} or empty. * <p> * Examples: * * <pre> * Entry entry = ...; * * Calendar timestamp = entry.parseAttribute("createTimestamp").asCalendar(); * boolean isEnabled = entry.parseAttribute("enabled").asBoolean(false); * * Entry group = ...; * Schema schema = ...; * * Set<DN> members = group.parseAttribute("member").usingSchema(schema).asSetOfDN(); * </pre> * * @see Entry#parseAttribute * @see Attribute#parse */ public final class AttributeParser { // TODO: enums, filters, rdns? private static final AttributeParser NULL_INSTANCE = new AttributeParser(null); /** * Returns an attribute parser for the provided attribute. {@code null} * attributes are permitted and will be treated as if an empty attribute was * provided. * * @param attribute * The attribute to be parsed, which may be {@code null}. * @return The attribute parser. */ public static AttributeParser parseAttribute(final Attribute attribute) { return isEmpty(attribute) ? NULL_INSTANCE : new AttributeParser(attribute); } private static boolean isEmpty(final Attribute attribute) { return attribute == null || attribute.isEmpty(); } private final Attribute attribute; private Schema schema; private AttributeParser(final Attribute attribute) { this.attribute = attribute; } /** * Returns the first value decoded as a {@code T} using the provided * {@link Function}, or {@code null} if the attribute does not contain any * values. * * @param <T> * The type of the value to be decoded. * @param f * The function which should be used to decode the value. * @return The first value decoded as a {@code T}. */ public <T> T as(final Function<ByteString, ? extends T, NeverThrowsException> f) { return as(f, null); } /** * Returns the first value decoded as a {@code T} using the provided * {@link Function}, or {@code defaultValue} if the attribute does not * contain any values. * * @param <T> * The type of the value to be decoded. * @param f * The function which should be used to decode the value. * @param defaultValue * The default value to return if the attribute is empty. * @return The first value decoded as a {@code T}. */ public <T> T as(final Function<ByteString, ? extends T, NeverThrowsException> f, final T defaultValue) { if (!isEmpty(attribute)) { return f.apply(attribute.iterator().next()); } else { return defaultValue; } } /** * Returns the first value decoded as a boolean, or {@code null} if the * attribute does not contain any values. * * @return The first value decoded as a boolean. */ public Boolean asBoolean() { return isEmpty(attribute) ? null : asBoolean(false /* ignored */); } /** * Returns the first value decoded as an {@code Boolean}, or * {@code defaultValue} if the attribute does not contain any values. * * @param defaultValue * The default value to return if the attribute is empty. * @return The first value decoded as an {@code Boolean}. */ public boolean asBoolean(final boolean defaultValue) { return as(Functions.byteStringToBoolean(), defaultValue); } /** * Returns the first value, or {@code null} if the attribute does not * contain any values. * * @return The first value. */ public ByteString asByteString() { return asByteString(null); } /** * Returns the first value, or {@code defaultValue} if the attribute does * not contain any values. * * @param defaultValue * The default value to return if the attribute is empty. * @return The first value. */ public ByteString asByteString(final ByteString defaultValue) { return as(Functions.<ByteString> identityFunction(), defaultValue); } /** * Returns the first value decoded as a {@code GeneralizedTime} using the * generalized time syntax, or {@code null} if the attribute does not * contain any values. * * @return The first value decoded as a {@code GeneralizedTime}. */ public GeneralizedTime asGeneralizedTime() { return asGeneralizedTime(null); } /** * Returns the first value decoded as an {@code GeneralizedTime} using the * generalized time syntax, or {@code defaultValue} if the attribute does * not contain any values. * * @param defaultValue * The default value to return if the attribute is empty. * @return The first value decoded as an {@code GeneralizedTime}. */ public GeneralizedTime asGeneralizedTime(final GeneralizedTime defaultValue) { return as(Functions.byteStringToGeneralizedTime(), defaultValue); } /** * Returns the first value decoded as an {@code Integer}, or {@code null} if * the attribute does not contain any values. * * @return The first value decoded as an {@code Integer}. */ public Integer asInteger() { return isEmpty(attribute) ? null : asInteger(0 /* ignored */); } /** * Returns the first value decoded as an {@code Integer}, or * {@code defaultValue} if the attribute does not contain any values. * * @param defaultValue * The default value to return if the attribute is empty. * @return The first value decoded as an {@code Integer}. */ public int asInteger(final int defaultValue) { return as(Functions.byteStringToInteger(), defaultValue); } /** * Returns the first value decoded as a {@code Long}, or {@code null} if the * attribute does not contain any values. * * @return The first value decoded as a {@code Long}. */ public Long asLong() { return isEmpty(attribute) ? null : asLong(0L /* ignored */); } /** * Returns the first value decoded as a {@code Long}, or * {@code defaultValue} if the attribute does not contain any values. * * @param defaultValue * The default value to return if the attribute is empty. * @return The first value decoded as a {@code Long}. */ public long asLong(final long defaultValue) { return as(Functions.byteStringToLong(), defaultValue); } /** * Returns the values decoded as a set of {@code T}s using the provided * {@link Function}, or {@code defaultValues} if the attribute does not * contain any values. * * @param <T> * The type of the values to be decoded. * @param f * The function which should be used to decode values. * @param defaultValues * The default values to return if the attribute is empty. * @return The values decoded as a set of {@code T}s. */ public <T> Set<T> asSetOf(final Function<ByteString, ? extends T, NeverThrowsException> f, final Collection<? extends T> defaultValues) { if (!isEmpty(attribute)) { final LinkedHashSet<T> result = new LinkedHashSet<>(attribute.size()); for (final ByteString v : attribute) { result.add(f.apply(v)); } return result; } else if (defaultValues != null) { return new LinkedHashSet<>(defaultValues); } else { return new LinkedHashSet<>(0); } } /** * Returns the values decoded as a set of {@code T}s using the provided * {@link Function}, or {@code defaultValues} if the attribute does not * contain any values. * * @param <T> * The type of the values to be decoded. * @param f * The function which should be used to decode values. * @param defaultValues * The default values to return if the attribute is empty. * @return The values decoded as a set of {@code T}s. */ public <T> Set<T> asSetOf(final Function<ByteString, ? extends T, NeverThrowsException> f, final T... defaultValues) { return asSetOf(f, Arrays.asList(defaultValues)); } /** * Returns the values decoded as a set of {@code Boolean}s, or * {@code defaultValues} if the attribute does not contain any values. * * @param defaultValues * The default values to return if the attribute is empty. * @return The values decoded as a set of {@code Boolean}s. */ public Set<Boolean> asSetOfBoolean(final Boolean... defaultValues) { return asSetOfBoolean(Arrays.asList(defaultValues)); } /** * Returns the values decoded as a set of {@code Boolean}s, or * {@code defaultValues} if the attribute does not contain any values. * * @param defaultValues * The default values to return if the attribute is empty. * @return The values decoded as a set of {@code Boolean}s. */ public Set<Boolean> asSetOfBoolean(final Collection<Boolean> defaultValues) { return asSetOf(Functions.byteStringToBoolean(), defaultValues); } /** * Returns the values contained in the attribute, or {@code defaultValues} * if the attribute does not contain any values. * * @param defaultValues * The default values to return if the attribute is empty. * @return The values contained in the attribute. */ public Set<ByteString> asSetOfByteString(final ByteString... defaultValues) { return asSetOfByteString(Arrays.asList(defaultValues)); } /** * Returns the values contained in the attribute, or {@code defaultValues} * if the attribute does not contain any values. * * @param defaultValues * The default values to return if the attribute is empty. * @return The values contained in the attribute. */ public Set<ByteString> asSetOfByteString(final Collection<ByteString> defaultValues) { return asSetOf(Functions.<ByteString> identityFunction(), defaultValues); } /** * Returns the values decoded as a set of {@code GeneralizedTime}s using the * generalized time syntax, or {@code defaultValues} if the attribute does * not contain any values. * * @param defaultValues * The default values to return if the attribute is empty. * @return The values decoded as a set of {@code GeneralizedTime}s. */ public Set<GeneralizedTime> asSetOfGeneralizedTime( final Collection<GeneralizedTime> defaultValues) { return asSetOf(Functions.byteStringToGeneralizedTime(), defaultValues); } /** * Returns the values decoded as a set of {@code GeneralizedTime}s using the * generalized time syntax, or {@code defaultValues} if the attribute does * not contain any values. * * @param defaultValues * The default values to return if the attribute is empty. * @return The values decoded as a set of {@code GeneralizedTime}s. */ public Set<GeneralizedTime> asSetOfGeneralizedTime(final GeneralizedTime... defaultValues) { return asSetOfGeneralizedTime(Arrays.asList(defaultValues)); } /** * Returns the values decoded as a set of {@code Integer}s, or * {@code defaultValues} if the attribute does not contain any values. * * @param defaultValues * The default values to return if the attribute is empty. * @return The values decoded as a set of {@code Integer}s. */ public Set<Integer> asSetOfInteger(final Collection<Integer> defaultValues) { return asSetOf(Functions.byteStringToInteger(), defaultValues); } /** * Returns the values decoded as a set of {@code Integer}s, or * {@code defaultValues} if the attribute does not contain any values. * * @param defaultValues * The default values to return if the attribute is empty. * @return The values decoded as a set of {@code Integer}s. */ public Set<Integer> asSetOfInteger(final Integer... defaultValues) { return asSetOfInteger(Arrays.asList(defaultValues)); } /** * Returns the values decoded as a set of {@code Long}s, or * {@code defaultValues} if the attribute does not contain any values. * * @param defaultValues * The default values to return if the attribute is empty. * @return The values decoded as a set of {@code Long}s. */ public Set<Long> asSetOfLong(final Collection<Long> defaultValues) { return asSetOf(Functions.byteStringToLong(), defaultValues); } /** * Returns the values decoded as a set of {@code Long}s, or * {@code defaultValues} if the attribute does not contain any values. * * @param defaultValues * The default values to return if the attribute is empty. * @return The values decoded as a set of {@code Long}s. */ public Set<Long> asSetOfLong(final Long... defaultValues) { return asSetOfLong(Arrays.asList(defaultValues)); } /** * Returns the values decoded as a set of {@code String}s, or * {@code defaultValues} if the attribute does not contain any values. * * @param defaultValues * The default values to return if the attribute is empty. * @return The values decoded as a set of {@code String}s. */ public Set<String> asSetOfString(final Collection<String> defaultValues) { return asSetOf(Functions.byteStringToString(), defaultValues); } /** * Returns the values decoded as a set of {@code String}s, or * {@code defaultValues} if the attribute does not contain any values. * * @param defaultValues * The default values to return if the attribute is empty. * @return The values decoded as a set of {@code String}s. */ public Set<String> asSetOfString(final String... defaultValues) { return asSetOfString(Arrays.asList(defaultValues)); } /** * Returns the first value decoded as a {@code String}, or {@code null} if * the attribute does not contain any values. * * @return The first value decoded as a {@code String}. */ public String asString() { return asString(null); } /** * Returns the first value decoded as a {@code String}, or * {@code defaultValue} if the attribute does not contain any values. * * @param defaultValue * The default value to return if the attribute is empty. * @return The first value decoded as a {@code String}. */ public String asString(final String defaultValue) { return as(Functions.byteStringToString(), defaultValue); } /** * Throws a {@code NoSuchElementException} if the attribute referenced by * this parser is {@code null} or empty. * * @return A reference to this attribute parser. * @throws NoSuchElementException * If the attribute referenced by this parser is {@code null} or * empty. */ public AttributeParser requireValue() throws NoSuchElementException { if (isEmpty(attribute)) { throw new NoSuchElementException(); } else { return this; } } /** * Sets the {@code Schema} which will be used when parsing schema sensitive * values such as DNs and attribute descriptions. * * @param schema * The {@code Schema} which will be used when parsing schema * sensitive values. * @return This attribute parser. */ public AttributeParser usingSchema(final Schema schema) { // Avoid modifying the null instance: a schema will not be needed // anyway. if (this != NULL_INSTANCE) { this.schema = schema; } return this; } private Schema getSchema() { return schema == null ? Schema.getDefaultSchema() : schema; } }