ExpandableResourceBundle.java
001 /*
002  * SPDX-License-Identifier: Apache-2.0
003  *
004  * Copyright 2008-2017 the original author or authors.
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     http://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 package griffon.util;
019 
020 import javax.annotation.Nonnull;
021 import javax.annotation.Nullable;
022 import java.util.Enumeration;
023 import java.util.Iterator;
024 import java.util.LinkedHashMap;
025 import java.util.Map;
026 import java.util.ResourceBundle;
027 
028 import static griffon.util.ConfigUtils.getConfigValue;
029 import static griffon.util.GriffonNameUtils.requireNonBlank;
030 import static java.util.Objects.requireNonNull;
031 
032 /**
033  @author Andres Almiray
034  @since 2.0.0
035  */
036 public class ExpandableResourceBundle extends ResourceBundle {
037     private final Map<String, Object> entries = new LinkedHashMap<>();
038 
039     @Nonnull
040     public static ResourceBundle wrapResourceBundle(@Nonnull ResourceBundle resourceBundle) {
041         requireNonNull(resourceBundle, "Argument 'resourceBundle' must not be null");
042         if (!(resourceBundle instanceof ExpandableResourceBundle)) {
043             return new ExpandableResourceBundle(resourceBundle);
044         }
045         return resourceBundle;
046     }
047 
048     public ExpandableResourceBundle(@Nonnull ResourceBundle delegate) {
049         requireNonNull(delegate, "Argument 'delegate' must not be null");
050         for (String key : delegate.keySet()) {
051             Object value = getConfigValue(delegate, key);
052             processKey(key, entries, value);
053             entries.put(key, value);
054         }
055     }
056 
057     @SuppressWarnings("unchecked")
058     private void processKey(@Nonnull String key, @Nonnull Map<String, Object> map, @Nullable Object value) {
059         String[] keys = split(key);
060         if (keys[1== null) {
061             map.put(keys[0], value);
062         else {
063             Map<String, Object> m = (Map<String, Object>map.get(keys[0]);
064             if (m == null) {
065                 m = new LinkedHashMap<>();
066                 map.put(keys[0], m);
067             }
068             processKey(keys[1], m, value);
069         }
070     }
071 
072     @Nonnull
073     private String[] split(@Nonnull String input) {
074         int split = input.indexOf('.');
075         String head = split < ? input : input.substring(0, split);
076         String tail = split > ? input.substring(split + 1null;
077         return new String[]{head, tail};
078     }
079 
080     @Nullable
081     @Override
082     protected final Object handleGetObject(@Nonnull String key) {
083         return entries.get(requireNonBlank(key, "Argument 'key' must not be blank"));
084     }
085 
086     @Nonnull
087     @Override
088     public final Enumeration<String> getKeys() {
089         return new IteratorAsEnumeration<>(entries.keySet().iterator());
090     }
091 
092     private static class IteratorAsEnumeration<E> implements Enumeration<E> {
093         private final Iterator<E> iterator;
094 
095         public IteratorAsEnumeration(Iterator<E> iterator) {
096             this.iterator = iterator;
097         }
098 
099         public boolean hasMoreElements() {
100             return iterator.hasNext();
101         }
102 
103         public E nextElement() {
104             return iterator.next();
105         }
106     }
107 }