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.HashSet;
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 */
056@SuppressWarnings("PMD.LinguisticNaming")
057public class RenderConlet extends ConsoleCommand {
058
059    private static final Set<RenderMode> DEFAULT_SUPPORTED
060        = Collections.unmodifiableSet(RenderMode.asSet(RenderMode.Preview));
061
062    private final String conletType;
063    private final String conletId;
064    private Set<RenderMode> renderAs = RenderMode.asSet(RenderMode.Preview);
065    private Set<RenderMode> supportedModes = DEFAULT_SUPPORTED;
066    private final Future<String> content;
067
068    /**
069     * Creates a new event.
070     *
071     * @param conletType the conlet type
072     * @param conletId the id of the web console component
073     * @param content the content
074     */
075    public RenderConlet(String conletType, String conletId,
076            Future<String> content) {
077        this.conletType = conletType;
078        this.conletId = conletId;
079        this.content = content;
080    }
081
082    /**
083     * Returns the web console component type as specified on creation.
084     *
085     * @return the type
086     */
087    public String conletType() {
088        return conletType;
089    }
090
091    /**
092     * Returns the web console component id as specified on creation.
093     * 
094     * @return the web console component id
095     */
096    public String conletId() {
097        return conletId;
098    }
099
100    /**
101     * Set the render mode. The default value is {@link RenderMode#Preview}.
102     * 
103     * @param renderMode the render mode to set
104     * @return the event for easy chaining
105     */
106    public RenderConlet setRenderAs(RenderMode renderMode) {
107        this.renderAs = RenderMode.asSet(renderMode);
108        return this;
109    }
110
111    /**
112     * Set the render mode (including modifier).
113     * 
114     * @param renderAs the render mode to use
115     * @return the event for easy chaining
116     */
117    public RenderConlet setRenderAs(Set<RenderMode> renderAs) {
118        this.renderAs = new HashSet<>(renderAs);
119        return this;
120    }
121
122    /**
123     * Returns the render mode.
124     * 
125     * @return the render mode
126     */
127    public Set<RenderMode> renderAs() {
128        return Collections.unmodifiableSet(renderAs);
129    }
130
131    /**
132     * Set the supported render modes. The default value is 
133     * {@link RenderMode#Preview}.
134     * 
135     * @param supportedModes the supported render modes to set
136     * @return the event for easy chaining
137     */
138    public RenderConlet setSupportedModes(Set<RenderMode> supportedModes) {
139        this.supportedModes = supportedModes;
140        return this;
141    }
142
143    /**
144     * Add the given render mode to the supported render modes.
145     * 
146     * @param supportedMode the supported render modes to add
147     * @return the event for easy chaining
148     */
149    public RenderConlet addSupportedMode(RenderMode supportedMode) {
150        if (supportedModes == DEFAULT_SUPPORTED) { // NOPMD, check identity
151            supportedModes = new HashSet<>(DEFAULT_SUPPORTED);
152        }
153        supportedModes.add(supportedMode);
154        return this;
155    }
156
157    /**
158     * Returns the supported modes.
159     * 
160     * @return the supported modes
161     */
162    public Set<RenderMode> supportedRenderModes() {
163        return supportedModes;
164    }
165
166    /**
167     * Provides the HTML that displays the web console component 
168     * on the page.
169     * 
170     * @return the HTML
171     */
172    public Future<String> content() {
173        return content;
174    }
175
176    /**
177     * Writes the JSON notification to the given writer.
178     *
179     * @param writer the writer
180     * @throws ExecutionException 
181     * @throws InterruptedException 
182     */
183    @Override
184    public void emitJson(Writer writer)
185            throws InterruptedException, IOException {
186        try {
187            emitJson(writer, "updateConlet", conletType(), conletId(),
188                renderAs().stream().map(RenderMode::name)
189                    .toArray(size -> new String[size]),
190                supportedRenderModes().stream().map(RenderMode::name)
191                    .toArray(size -> new String[size]),
192                content().get());
193        } catch (ExecutionException e) {
194            throw new IOException(e);
195        }
196    }
197}