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