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.io.IOException;
022import java.io.Writer;
023import java.util.Collections;
024import java.util.EnumSet;
025import java.util.Set;
026import java.util.concurrent.ExecutionException;
027import java.util.concurrent.Future;
028import org.jgrapes.webconsole.base.Conlet.RenderMode;
029
030/**
031 * A console command for conlet rendering. Sent to the web console page
032 * for adding or updating a complete web console component representation. 
033 * The actual content (the HTML) is passed to the constructor as a
034 * {@link Future}, allowing the content to be evaluated in parallel
035 * to the event handling.
036 * 
037 * The content must be valid HTML, usable as inner HTML of a block level
038 * element (typically a `div`). If the root element has an attribute
039 * `data-conlet-title`, its value overrides the default title (the display
040 * name of the conlet type, see {@link AddConletType#displayNames()}).
041 * 
042 * The content provided is searched for attributes `data-jgwc-on-load`
043 * and `data-jgwc-on-unload` which must have as value the name of a 
044 * function. When the HTML has been loaded or unloaded (i.e. added to
045 * the DOM or removed from the DOM), the respective functions are 
046 * invoked with the element containing the attribute as their first 
047 * parameter. A second boolean parameter is `true` if the on-load 
048 * function is called due to an update of an already existing container.
049 * 
050 * The HTML elements of edit dialogs ({@link RenderMode#Edit})
051 * can have an additional attribute `data-jgwc-on-apply`
052 * which must have as its value the name of a function. This
053 * function is invoked when changes made in the form must be
054 * applied (e.g. before the dialog is closed).
055 */
056public class RenderConlet extends ConsoleCommand {
057
058    private static final Set<RenderMode> DEFAULT_SUPPORTED
059        = Collections.unmodifiableSet(RenderMode.asSet(RenderMode.Preview));
060
061    private final String conletType;
062    private final String conletId;
063    private Set<RenderMode> renderAs = RenderMode.asSet(RenderMode.Preview);
064    private Set<RenderMode> supportedModes = DEFAULT_SUPPORTED;
065    private final Future<String> content;
066
067    /**
068     * Creates a new event.
069     *
070     * @param conletType the conlet type
071     * @param conletId the id of the web console component
072     * @param content the content
073     */
074    public RenderConlet(String conletType, String conletId,
075            Future<String> content) {
076        this.conletType = conletType;
077        this.conletId = conletId;
078        this.content = content;
079    }
080
081    /**
082     * Returns the web console component type as specified on creation.
083     *
084     * @return the type
085     */
086    public String conletType() {
087        return conletType;
088    }
089
090    /**
091     * Returns the web console component id as specified on creation.
092     * 
093     * @return the web console component id
094     */
095    public String conletId() {
096        return conletId;
097    }
098
099    /**
100     * Set the render mode. The default value is {@link RenderMode#Preview}.
101     * 
102     * @param renderMode the render mode to set
103     * @return the event for easy chaining
104     */
105    public RenderConlet setRenderAs(RenderMode renderMode) {
106        this.renderAs = RenderMode.asSet(renderMode);
107        return this;
108    }
109
110    /**
111     * Set the render mode (including modifier).
112     * 
113     * @param renderAs the render mode to use
114     * @return the event for easy chaining
115     */
116    public RenderConlet setRenderAs(Set<RenderMode> renderAs) {
117        this.renderAs = EnumSet.copyOf(renderAs);
118        return this;
119    }
120
121    /**
122     * Returns the render mode.
123     * 
124     * @return the render mode
125     */
126    public Set<RenderMode> renderAs() {
127        return Collections.unmodifiableSet(renderAs);
128    }
129
130    /**
131     * Set the supported render modes. The default value is 
132     * {@link RenderMode#Preview}.
133     * 
134     * @param supportedModes the supported render modes to set
135     * @return the event for easy chaining
136     */
137    public RenderConlet setSupportedModes(Set<RenderMode> supportedModes) {
138        this.supportedModes = supportedModes;
139        return this;
140    }
141
142    /**
143     * Add the given render mode to the supported render modes.
144     * 
145     * @param supportedMode the supported render modes to add
146     * @return the event for easy chaining
147     */
148    public RenderConlet addSupportedMode(RenderMode supportedMode) {
149        if (supportedModes == DEFAULT_SUPPORTED) {
150            supportedModes = EnumSet.copyOf(DEFAULT_SUPPORTED);
151        }
152        supportedModes.add(supportedMode);
153        return this;
154    }
155
156    /**
157     * Returns the supported modes.
158     * 
159     * @return the supported modes
160     */
161    public Set<RenderMode> supportedRenderModes() {
162        return supportedModes;
163    }
164
165    /**
166     * Provides the HTML that displays the web console component 
167     * on the page.
168     * 
169     * @return the HTML
170     */
171    public Future<String> content() {
172        return content;
173    }
174
175    /**
176     * Writes the JSON notification to the given writer.
177     *
178     * @param writer the writer
179     * @throws ExecutionException 
180     * @throws InterruptedException 
181     */
182    @Override
183    public void emitJson(Writer writer)
184            throws InterruptedException, IOException {
185        try {
186            emitJson(writer, "updateConlet", conletType(), conletId(),
187                renderAs().stream().map(RenderMode::name)
188                    .toArray(size -> new String[size]),
189                supportedRenderModes().stream().map(RenderMode::name)
190                    .toArray(size -> new String[size]),
191                content().get());
192        } catch (ExecutionException e) {
193            throw new IOException(e);
194        }
195    }
196}