View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *  http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17
    18  package org.apache.any23.configuration;
    19
    20  import java.util.AbstractSet;
    21  import java.util.Collection;
    22  import java.util.Collections;
    23  import java.util.HashMap;
    24  import java.util.Iterator;
    25  import java.util.Map;
    26  import java.util.Optional;
    27  import java.util.Set;
    28
    29  /**
    30   * This class represents an <i>immutable</i> {@link Set} of {@link Setting} objects, with the additional property that
    31   * no two settings having the same {@link Setting#getIdentifier() identifier} can be simultaneously present in a
    32   * {@code Settings} object.
    33   *
    34   * @author Hans Brende (hansbrende@apache.org)
    35   */
    36  public final class Settings extends AbstractSet<Setting<?>> {
    37
    38      private static final Settingss.html#Settings">Settings EMPTY_SETTINGS = new Settings(Collections.emptyMap());
    39
    40      private final Map<String, Setting<?>> values;
    41
    42      private Settings(Map<String, Setting<?>> values) {
    43          this.values = values;
    44      }
    45
    46      /**
    47       * @param identifier
    48       *            the identifier of the setting to find
    49       *
    50       * @return the setting with the identifier supplied, if present
    51       */
    52      public Optional<Setting<?>> find(String identifier) {
    53          return Optional.ofNullable(values.get(identifier));
    54      }
    55
    56      /**
    57       * This method is semantically equivalent to: <br>
    58       * <br>
    59       *
    60       * <pre>
    61       * {@code find(setting.getIdentifier()).flatMap(s -> s.as(setting))}
    62       * </pre>
    63       *
    64       * @param <S>
    65       *            generic setting type
    66       * @param setting
    67       *            a setting key
    68       *
    69       * @return the setting with the same setting key as the supplied setting, if present.
    70       */
    71      public <S extends Setting<?>> Optional<S> find(S setting) {
    72          Setting<?> found = values.get(setting.getIdentifier());
    73          return found == null ? Optional.empty() : found.as(setting);
    74      }
    75
    76      /**
    77       * This method is semantically equivalent to: <br>
    78       * <br>
    79       *
    80       * <pre>
    81       * {@code find(defaultSetting).orElse(defaultSetting).getValue()}
    82       * </pre>
    83       *
    84       * @param <E>
    85       *            generic setting type
    86       * @param defaultSetting
    87       *            a default key for which to obtain a value set
    88       *
    89       * @return the value set for {@code defaultSetting}'s key, if present. Otherwise, returns {@code defaultSetting}'s
    90       *         value.
    91       */
    92      public <E> E get(Setting<E> defaultSetting) {
    93          return find(defaultSetting).orElse(defaultSetting).getValue();
    94      }
    95
    96      ///////////////////////////////////////
    97      // AbstractSet overrides
    98      ///////////////////////////////////////
    99
    100     @Override
    101     public boolean contains(Object o) {
    102         if (!(o instanceof Setting<?>)) {
    103             return false;
    104         }
    105         return o.equals(values.get(((Setting<?>) o).getIdentifier()));
    106     }
    107
    108     @Override
    109     public int size() {
    110         return values.size();
    111     }
    112
    113     @Override
    114     public Iterator<Setting<?>> iterator() {
    115         return values.values().iterator();
    116     }
    117
    118     ///////////////////////////////////////
    119     // public constructors
    120     ///////////////////////////////////////
    121
    122     /**
    123      * Returns an empty {@link Settings} object.
    124      *
    125      * @return an empty {@link Settings} object
    126      */
    127     public static Settings of() {
    128         return EMPTY_SETTINGS;
    129     }
    130
    131     /**
    132      * Returns a singleton {@link Settings} object, containing only the supplied Setting.
    133      *
    134      * @param s
    135      *            one {@link org.apache.any23.configuration.Setting}
    136      *
    137      * @return a {@link Settings} object containing the supplied Setting.
    138      */
    139     public static Settings of(Setting<?> s) {
    140         return new Settings(Collections.singletonMap(s.getIdentifier(), s));
    141     }
    142
    143     /**
    144      * @param settings
    145      *            one or more {@link org.apache.any23.configuration.Setting}'s
    146      *
    147      * @return a {@link Settings} object containing the supplied settings. For any two settings having the same key, the
    148      *         first will be overwritten by the second.
    149      *
    150      * @throws IllegalArgumentException
    151      *             if any two settings have the same identifier
    152      */
    153     public static Settings of(Setting<?>... settings) {
    154         Map<String, Setting<?>> map = mapForSize(settings.length);
    155         for (Setting<?> s : settings)
    156             put(map, s);
    157         return ofModifiable(map);
    158     }
    159
    160     /**
    161      * @param c
    162      *            a populated {@link java.util.Collection} of {@link org.apache.any23.configuration.Setting}'s
    163      *
    164      * @return a {@link Settings} object containing the supplied settings.
    165      *
    166      * @throws IllegalArgumentException
    167      *             if any two settings have the same identifier
    168      */
    169     public static Settings of(Collection<? extends Setting<?>> c) {
    170         if (c instanceof Settings) {
    171             return (Settings) c;
    172         }
    173         int size = c.size();
    174         if (size == 0) {
    175             return EMPTY_SETTINGS;
    176         }
    177         Map<String, Setting<?>> map = mapForSize(size);
    178         for (Setting<?> s : c)
    179             put(map, s);
    180         return ofModifiable(map);
    181     }
    182
    183     ///////////////////////////////////////
    184     // Private static helpers
    185     ///////////////////////////////////////
    186
    187     private static Settings ofModifiable(Map<String, Setting<?>> map) {
    188         return new Settings(Collections.unmodifiableMap(map));
    189     }
    190
    191     private static void put(Map<String, Setting<?>> map, Setting<?> setting) {
    192         Setting<?> existing = map.put(setting.getIdentifier(), setting);
    193         if (existing != null) {
    194             throw new IllegalArgumentException(setting.getIdentifier() + " is already defined");
    195         }
    196     }
    197
    198     private static final float loadFactor = 0.75f;
    199
    200     private static Map<String, Setting<?>> mapForSize(int size) {
    201         return new HashMap<>((int) (size / loadFactor) + 1, loadFactor);
    202     }
    203
    204 }