001/*
002 * JGrapes Event Driven Framework
003 * Copyright (C) 2017-2018 Michael N. Lipp
004 * 
005 * This program is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU Affero General Public License as published by 
007 * the Free Software Foundation; either version 3 of the License, or 
008 * (at your option) any later version.
009 * 
010 * This program is distributed in the hope that it will be useful, but 
011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 
013 * for more details.
014 * 
015 * You should have received a copy of the GNU Affero General Public License along 
016 * with this program; if not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jgrapes.util.events;
020
021import java.util.Collections;
022import java.util.HashMap;
023import java.util.Map;
024import java.util.Optional;
025import java.util.Set;
026
027import org.jgrapes.core.Event;
028import org.jgrapes.core.Manager;
029
030/**
031 * An event to indicate that configuration information has been
032 * updated.
033 * 
034 * Configurability based on this type of event can optionally be 
035 * supported by components. If supported, a component implements a 
036 * handler that checks whether the paths of this event include the 
037 * component's path in the application's component tree (see 
038 * {@link Manager#componentPath()}). If so, the component adapts itself to the 
039 * information propagated.
040 */
041public class ConfigurationUpdate extends Event<Void> {
042
043        @SuppressWarnings("PMD.UseConcurrentHashMap")
044        private final Map<String,Map<String,String>> paths = new HashMap<>();
045
046        /**
047         * Add new (updated) configuration value to the event.
048         * 
049         * @param path the value's path
050         * @param key the key of the value
051         * @param value the value
052         * @return the event for easy chaining
053         */
054        public ConfigurationUpdate add(String path, String key, String value) {
055                if (path == null || !path.startsWith("/")) {
056                        throw new IllegalArgumentException("Path must start with \"/\".");
057                }
058                @SuppressWarnings("PMD.UseConcurrentHashMap")
059                Map<String,String> scoped = paths
060                                .computeIfAbsent(path, newKey -> new HashMap<String,String>());
061                scoped.put(key, value);
062                return this;
063        }
064
065        /**
066         * Remove a path from the configuration.
067         * 
068         * @param path the path to be removed
069         * @return the event for easy chaining
070         */
071        public ConfigurationUpdate removePath(String path) {
072                if (path == null || !path.startsWith("/")) {
073                        throw new IllegalArgumentException("Path must start with \"/\".");
074                }
075                paths.put(path, null);
076                return this;
077        }
078        
079        /**
080         * Return all paths affected by this event.
081         * 
082         * @return the paths
083         */
084        public Set<String> paths() {
085                return Collections.unmodifiableSet(paths.keySet());
086        }
087        
088        /**
089         * Return the values for a given path if they exists.
090         * 
091         * @param path the path
092         * @return the updated values or `null` if the path has been
093         * removed (implies the removal of all values for that path).
094         */
095        public Optional<Map<String,String>> values(String path) {
096                Map<String,String> result = paths.get(path);
097                if (result == null) {
098                        return Optional.empty();
099                }
100                return Optional.of(Collections.unmodifiableMap(result));
101        }
102
103        /**
104         * Return the value with the given path and key if it exists.
105         * 
106         * @param path the path
107         * @param key the key
108         * @return the value
109         */
110        public Optional<String> value(String path, String key) {
111                return Optional.ofNullable(paths.get(path))
112                                .flatMap(map -> Optional.ofNullable(map.get(key)));
113        }
114}