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    public AddConletRequest setFrontendRequest() {
112        frontendRequest = true;
113        return this;
114    }
115
116    /**
117     * Checks if this request originated from the browser.
118     *
119     * @return true, if is frontend request
120     */
121    public boolean isFrontendRequest() {
122        return frontendRequest;
123    }
124
125    /**
126     * Returns the web console component type
127     * 
128     * @return the web console component type
129     */
130    public String conletType() {
131        return conletType;
132    }
133
134    /**
135     * Returns the properties. Every event returns a mutable map,
136     * thus allowing event handlers to modify the map even if
137     * none was passed to the constructor.
138     */
139    public Map<Object, Object> properties() {
140        if (properties == null) {
141            properties = new HashMap<>();
142        }
143        @SuppressWarnings("unchecked")
144        Map<Object, Object> props = (Map<Object, Object>) properties;
145        return props;
146    }
147
148    /**
149     * Convenience method for adding properties one-by-one.
150     * 
151     * @param key the property key
152     * @param value the property value
153     * @return the event for easy chaining
154     */
155    public AddConletRequest addProperty(Object key, Object value) {
156        properties().put(key, value);
157        return this;
158    }
159
160    /**
161     * Convenience method that performs the given action if a property
162     * with the given key exists.
163     * 
164     * @param key the property key
165     * @param action the action to perform
166     */
167    public AddConletRequest ifPresent(
168            Object key, BiConsumer<Object, Object> action) {
169        if (properties().containsKey(key)) {
170            action.accept(key, properties().get(key));
171        }
172        return this;
173    }
174}