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.webconsole.base.events;
020
021import java.util.HashMap;
022import java.util.Map;
023import java.util.Set;
024import java.util.function.BiConsumer;
025import org.jgrapes.webconsole.base.Conlet.RenderMode;
026import org.jgrapes.webconsole.base.RenderSupport;
027
028/**
029 * Sent to the console (server) if a new web console component instance 
030 * of a given type should be added to the web console page. The console 
031 * server usually responds with a {@link RenderConlet} event that has as
032 * payload the HTML that displays the web console component on the console
033 * page.
034 * 
035 * Properties may be passed with the event. The interpretation
036 * of the properties is completely dependent on the web console
037 * component that handles the request. It is recommended to use 
038 * {@link String}s as keys and JDK types as values. This avoids 
039 * classpath dependencies on the web console component that is 
040 * to be added. 
041 * 
042 * {@link AddConletRequest} can also be generated on the server side
043 * to automatically add a conlet in response to some event. Usually,
044 * the origin of the event is not important when handling the event.
045 * Nevertheless, the origin can be determined by calling
046 * {@link #isFrontendRequest()} as it may be important e.g. for 
047 * security related checks.
048 * 
049 * The event's result is the web console component id of the new 
050 * web console component instance.
051 * 
052 * ![Event Sequence](AddConletRequestSeq.svg)
053 * 
054 * @startuml AddConletRequestSeq.svg
055 * hide footbox
056 * 
057 * Browser -> WebConsole: "addConlet"
058 * activate WebConsole
059 * WebConsole -> Conlet: AddConletRequest
060 * deactivate WebConsole
061 * activate Conlet
062 * Conlet -> WebConsole: RenderConlet
063 * deactivate Conlet
064 * activate WebConsole
065 * WebConsole -> Browser: "renderConlet"
066 * deactivate WebConsole
067 * 
068 * @enduml
069 * 
070 */
071public class AddConletRequest extends RenderConletRequestBase<String> {
072
073    private final String conletType;
074    private Map<? extends Object, ? extends Object> properties;
075    private boolean frontendRequest;
076
077    /**
078     * Creates a new event.
079     *
080     * @param renderSupport the render support
081     * @param conletType the type of the web console component
082     * @param renderModes the render modes
083     */
084    public AddConletRequest(RenderSupport renderSupport, String conletType,
085            Set<RenderMode> renderModes) {
086        super(renderSupport, renderModes);
087        this.conletType = conletType;
088        this.properties = new HashMap<>();
089    }
090
091    /**
092     * Creates a new event.
093     *
094     * @param renderSupport the render support
095     * @param conletType the type of the web console component
096     * @param renderModes the render modes
097     * @param properties optional values for properties of the 
098     * web console component instance
099     */
100    public AddConletRequest(RenderSupport renderSupport, String conletType,
101            Set<RenderMode> renderModes, Map<?, ?> properties) {
102        this(renderSupport, conletType, renderModes);
103        this.properties = new HashMap<>(properties);
104    }
105
106    /**
107     * Marks this event as originating from the browser.
108     *
109     * @return the adds the conlet request
110     */
111    @SuppressWarnings("PMD.LinguisticNaming")
112    public AddConletRequest setFrontendRequest() {
113        frontendRequest = true;
114        return this;
115    }
116
117    /**
118     * Checks if this request originated from the browser.
119     *
120     * @return true, if is frontend request
121     */
122    public boolean isFrontendRequest() {
123        return frontendRequest;
124    }
125
126    /**
127     * Returns the web console component type
128     * 
129     * @return the web console component type
130     */
131    public String conletType() {
132        return conletType;
133    }
134
135    /**
136     * Returns the properties. Every event returns a mutable map,
137     * thus allowing event handlers to modify the map even if
138     * none was passed to the constructor.
139     */
140    public Map<Object, Object> properties() {
141        if (properties == null) {
142            properties = new HashMap<>();
143        }
144        @SuppressWarnings("unchecked")
145        Map<Object, Object> props = (Map<Object, Object>) properties;
146        return props;
147    }
148
149    /**
150     * Convenience method for adding properties one-by-one.
151     * 
152     * @param key the property key
153     * @param value the property value
154     * @return the event for easy chaining
155     */
156    public AddConletRequest addProperty(Object key, Object value) {
157        properties().put(key, value);
158        return this;
159    }
160
161    /**
162     * Convenience method that performs the given action if a property
163     * with the given key exists.
164     * 
165     * @param key the property key
166     * @param action the action to perform
167     */
168    public AddConletRequest ifPresent(
169            Object key, BiConsumer<Object, Object> action) {
170        if (properties().containsKey(key)) {
171            action.accept(key, properties().get(key));
172        }
173        return this;
174    }
175}