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 }