DelegatingMutableConfiguration.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 org.codehaus.griffon.runtime.core.configuration;
019 
020 import griffon.core.Configuration;
021 import griffon.core.MutableConfiguration;
022 import griffon.util.AbstractMapResourceBundle;
023 import griffon.util.CompositeResourceBundle;
024 import griffon.util.ConfigUtils;
025 
026 import javax.annotation.Nonnull;
027 import javax.annotation.Nullable;
028 import java.util.LinkedHashMap;
029 import java.util.LinkedHashSet;
030 import java.util.Map;
031 import java.util.MissingResourceException;
032 import java.util.ResourceBundle;
033 import java.util.Set;
034 
035 import static griffon.util.ConfigUtils.getConfigValue;
036 import static griffon.util.GriffonNameUtils.requireNonBlank;
037 import static java.util.Arrays.asList;
038 import static java.util.Collections.unmodifiableMap;
039 import static java.util.Objects.requireNonNull;
040 
041 /**
042  @author Andres Almiray
043  @since 2.2.0
044  */
045 public class DelegatingMutableConfiguration extends ConfigurationDecorator implements MutableConfiguration {
046     private static final String ERROR_KEY_BLANK = "Argument 'key' must not be blank";
047     private static final String ERROR_VALUE_NULL = "Argument 'value' must not be null";
048 
049     private final Map<String, Object> mutableKeyValues = new LinkedHashMap<>();
050     private final Set<String> removedKeys = new LinkedHashSet<>();
051 
052     public DelegatingMutableConfiguration(@Nonnull Configuration delegate) {
053         super(delegate);
054     }
055 
056     @Override
057     public void set(@Nonnull String key, @Nonnull Object value) {
058         requireNonBlank(key, ERROR_KEY_BLANK);
059         requireNonNull(value, ERROR_VALUE_NULL);
060         mutableKeyValues.put(key, value);
061     }
062 
063     @Nullable
064     @Override
065     public Object remove(@Nonnull String key) {
066         requireNonBlank(key, ERROR_KEY_BLANK);
067         if (mutableKeyValues.containsKey(key)) {
068             removedKeys.add(key);
069             return mutableKeyValues.remove(key);
070         else if (!removedKeys.contains(key&& delegate.containsKey(key)) {
071             removedKeys.add(key);
072             return delegate.get(key);
073         }
074         return null;
075     }
076 
077     @Nullable
078     @Override
079     @SuppressWarnings("unchecked")
080     public <T> T removeAs(@Nonnull String key) {
081         return (Tremove(key);
082     }
083 
084     @Nullable
085     @Override
086     @SuppressWarnings("unchecked")
087     public <T> T removeConverted(@Nonnull String key, @Nonnull Class<T> type) {
088         return convertValue(remove(key), type);
089     }
090 
091     @Nonnull
092     @Override
093     public Map<String, Object> asFlatMap() {
094         Map<String, Object> flatMap = new LinkedHashMap<>(delegate.asFlatMap());
095         flatMap.putAll(mutableKeyValues);
096         for (String removedKey : removedKeys) {
097             flatMap.remove(removedKey);
098         }
099         return unmodifiableMap(flatMap);
100     }
101 
102     @Nonnull
103     @Override
104     public ResourceBundle asResourceBundle() {
105         return new CompositeResourceBundle(asList(new PrivateMapResourceBundle(asFlatMap()), delegate.asResourceBundle()));
106     }
107 
108     @Nullable
109     @Override
110     public Object get(@Nonnull String key) {
111         requireNonBlank(key, ERROR_KEY_BLANK);
112         try {
113             return getConfigValue(mutableKeyValues, key);
114         catch (MissingResourceException mre) {
115             if (removedKeys.contains(key)) {
116                 return null;
117             }
118             return super.get(key);
119         }
120     }
121 
122     @Nullable
123     @Override
124     public <T> T get(@Nonnull String key, @Nullable T defaultValue) {
125         T value = (Tget(key);
126         return value != null ? value : defaultValue;
127     }
128 
129     @Nullable
130     @Override
131     public String getAsString(@Nonnull String key, @Nullable String defaultValue) {
132         Object value = get(key);
133         return value != null ? String.valueOf(value: defaultValue;
134     }
135 
136     @Override
137     public boolean containsKey(@Nonnull String key) {
138         requireNonBlank(key, ERROR_KEY_BLANK);
139         return ConfigUtils.containsKey(mutableKeyValues, key|| (!removedKeys.contains(key&& delegate.containsKey(key));
140     }
141 
142     private static class PrivateMapResourceBundle extends AbstractMapResourceBundle {
143         private final Map<String, Object> map = new LinkedHashMap<>();
144 
145         private PrivateMapResourceBundle(Map<String, Object> map) {
146             this.map.putAll(map);
147             initialize(entries);
148             initializeKeys();
149         }
150 
151         @Override
152         protected void initialize(@Nonnull Map<String, Object> entries) {
153             if (map != null && entries != null) {
154                 entries.putAll(map);
155             }
156         }
157     }
158 }