Revision: 6355 Author: [email protected] Date: Tue Oct 13 09:24:19 2009 Log: Create type-safe callbacks for UI, moving UI types to a separate package.
Patch by: jat Review by: rdayal (TBR) http://code.google.com/p/google-web-toolkit/source/detail?r=6355 Added: /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/ui /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/ui/DevModeUI.java /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/ui/DoneCallback.java /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/ui/DoneEvent.java /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/ui/RestartServerCallback.java /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/ui/RestartServerEvent.java /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/ui/UiCallback.java /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/ui/UiEvent.java Deleted: /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/DevModeUI.java Modified: /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/HeadlessUI.java /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/HostedModeBase.java /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/SwingUI.java /changes/jat/abstractui/dev/oophm/overlay/com/google/gwt/dev/HostedMode.java ======================================= --- /dev/null +++ /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/ui/DevModeUI.java Tue Oct 13 09:24:19 2009 @@ -0,0 +1,171 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dev.ui; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.TreeLogger.Type; +import com.google.gwt.dev.util.log.PrintWriterTreeLogger; + +import java.util.HashMap; +import java.util.Map; + +/** + * Defines the interaction between DevelopmentMode and the UI, so that + * alternate UIs can be implemented. + */ +public abstract class DevModeUI { + + /** + * Opaque handle to a module - it is returned from loadModule and the client + * can only pass it to unloadModule or get a logger for messages about that + * module. + */ + public interface ModuleHandle { + + /** + * @return the logger for this module. + */ + TreeLogger getLogger(); + } + + /** + * Map of callbacks. + */ + private final Map<UiEvent.Type<?>, UiCallback> callbacks = new HashMap< + UiEvent.Type<?>, UiCallback>(); + + /** + * Log level for all logging in this UI. + */ + protected Type logLevel; + + /** + * A lazily-initialized console logger - see {...@link #getConsoleLogger()}. + */ + private PrintWriterTreeLogger consoleLogger = null; + + /** + * Create a top-level logger for messages which are not associated with the + * web server or any module. Defaults to logging to stdout. + * + * @return TreeLogger instance to use + */ + public TreeLogger getTopLogger() { + return getConsoleLogger(); + } + + /** + * Create the web server portion of the UI if not already created, and + * return its TreeLogger instance. + * + * <p>Note that the {...@link RestartServerEvent} should already have a callback + * registered when this is called -- the UI is not required to change the + * UI if it is registered later. + * + * @param serverName short name of the web server or null if only the icon + * should be used + * @param serverIcon byte array containing an icon (fitting into 24x24) to + * use for the server, or null if only the name should be used + * @return TreeLogger instance + */ + public abstract TreeLogger getWebServerLogger(String serverName, + byte[] serverIcon); + + /** + * Initialize the UI - must be called before any other method. + * + * <p>Subclasses should call super.initialize(logLevel). + * + * @param logLevel log level for all logging + */ + public void initialize(Type logLevel) { + this.logLevel = logLevel; + } + + /** + * Show that a module is loaded in the UI. + * + * @param userAgent full user agent name + * @param remoteSocket name of remote socket endpoint in host:port format + * @param url URL of top-level window + * @param tabKey stable browser tab identifier, or the empty string if no + * such identifier is available + * @param moduleName the name of the module loaded + * @param sessionKey a unique session key + * @param agentTag short-form user agent identifier, suitable for use in + * a label for this connection + * @param agentIcon icon to use for the user agent (fits inside 24x24) or + * null if unavailable + * @param logLevel logging detail requested + * @return a handle to the module + */ + public abstract ModuleHandle loadModule(String userAgent, + String remoteSocket, String url, String tabKey, String moduleName, + String sessionKey, String agentTag, byte[] agentIcon, Type logLevel); + + /** + * Sets the callback for a given event type.. + * + * @param <C> callback type + * @param type UI event type token + * @param callback event callback, or null to clear the callback + */ + public <C extends UiCallback> void setCallback(UiEvent.Type<C> type, + C callback) { + assert type != null; + callbacks.put(type, callback); + } + + /** + * Show that a previously loaded module has been unloaded. + * + * @param module ModuleHandle instance returned from loadModule on this UI + * instance + */ + public abstract void unloadModule(ModuleHandle module); + + /** + * Call callbacks for a given event. + * + * @param event + * @param callbackData arbitrary object, depending on the event type + */ + @SuppressWarnings("unchecked") + protected <C extends UiCallback> C getCallback(UiEvent.Type<?> eventType) { + return (C) callbacks.get(eventType); + } + + /** + * @return a console-based logger. + */ + protected TreeLogger getConsoleLogger() { + if (consoleLogger == null) { + consoleLogger = new PrintWriterTreeLogger(); + consoleLogger.setMaxDetail(logLevel); + } + return consoleLogger; + } + + /** + * Returns true if a callback has been registered for an event. + * + * @param event + * @return true if a callback has been registered for event + */ + protected boolean hasCallback(String event) { + return callbacks.get(event) != null; + } +} ======================================= --- /dev/null +++ /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/ui/DoneCallback.java Tue Oct 13 09:24:19 2009 @@ -0,0 +1,12 @@ +package com.google.gwt.dev.ui; + +/** + * Callback for "done" from the UI. + */ +public interface DoneCallback extends UiCallback { + + /** + * The UI is closing. + */ + void onDone(); +} ======================================= --- /dev/null +++ /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/ui/DoneEvent.java Tue Oct 13 09:24:19 2009 @@ -0,0 +1,29 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dev.ui; + +/** + * An event indicating the UI is closing. + */ +public class DoneEvent extends UiEvent<DoneCallback> { + + private static final Type<DoneCallback> TYPE = new Type<DoneCallback>( + "done"); + + public static Type<DoneCallback> getType() { + return TYPE; + } +} ======================================= --- /dev/null +++ /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/ui/RestartServerCallback.java Tue Oct 13 09:24:19 2009 @@ -0,0 +1,31 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dev.ui; + +import com.google.gwt.core.ext.TreeLogger; + +/** + * Callback for "restart server" from the UI. + */ +public interface RestartServerCallback extends UiCallback { + + /** + * The user has requested the web server should be restarted. + * + * @param logger TreeLogger for any logging required + */ + void onRestartServer(TreeLogger logger); +} ======================================= --- /dev/null +++ /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/ui/RestartServerEvent.java Tue Oct 13 09:24:19 2009 @@ -0,0 +1,32 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dev.ui; + +/** + * Event used to request a restart of the server. + * + * <p>Registering a callback for this event instructs the UI to provide some + * control for restarting the server. + */ +public class RestartServerEvent extends UiEvent<RestartServerCallback> { + + private static final Type<RestartServerCallback> TYPE = new Type<RestartServerCallback>( + "restart-server"); + + public static Type<RestartServerCallback> getType() { + return TYPE; + } +} ======================================= --- /dev/null +++ /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/ui/UiCallback.java Tue Oct 13 09:24:19 2009 @@ -0,0 +1,22 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dev.ui; + +/** + * Base interface for any UI callbacks. + */ +public interface UiCallback { +} ======================================= --- /dev/null +++ /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/ui/UiEvent.java Tue Oct 13 09:24:19 2009 @@ -0,0 +1,62 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dev.ui; + +/** + * Base class for any UI event that has a callback. + * + * @param <C> callback type + */ +public abstract class UiEvent<C extends UiCallback> { + + /** + * Type token for a UI event. + * + * <p>Any UiEvent subclasses must have exactly one corresponding Type instance + * created. + * + * @param <C> callback type + */ + public static class Type<C> { + + private static int nextId = 0; + + private final int id; + private final String name; + + protected Type(String name) { + id = nextId++; + this.name = name; + } + + @Override + public int hashCode() { + return id; + } + + @Override + public boolean equals(Object obj) { + // Since we require only one instance of each type object to be created, + // identity is safe here. + return this == obj; + } + + @Override + public String toString() { + return name; + } + } +} ======================================= --- /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/DevModeUI.java Mon Oct 12 16:39:12 2009 +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.google.gwt.dev; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.TreeLogger.Type; -import com.google.gwt.dev.util.log.PrintWriterTreeLogger; - -import java.util.HashMap; -import java.util.Map; - -/** - * Defines the interaction between DevelopmentMode and the UI, so that - * alternate UIs can be implemented. - */ -public abstract class DevModeUI { - - /** - * Callback interface for events from the UI. - */ - public interface Callback { - - /** - * UI-initiated callback for some event. - * - * @param event event name (TODO: make this a type-safe event in some - * way, need to consider alternate UI implementations though) - * @param callbackData arbitrary data, defined for the event type - */ - void callback(String event, Object callbackData); - } - - /** - * Opaque handle to a module - it is returned from loadModule and can - * only be passed to unloadModule, and the client can get a logger for - * messages about that module. - */ - public interface ModuleHandle { - - /** - * @return the logger for this module. - */ - TreeLogger getLogger(); - } - - // TODO: typesafe callbacks rather than strings - - /** - * Event used to indicate the UI is ready to exit. - * - * <p>Argument is ignored. - */ - public static final String DONE = "done"; - - /** - * Event used to request a restart of the server. - * - * <p>Argument is a TreeLogger instance. Registering a callback for this - * event instructs the UI to provide some control for restarting the server. - */ - public static final String RESTART_SERVER = "restart-server"; - - private final Map<String, Callback> callbacks = new HashMap<String, Callback>(); - - /** - * Log level for all logging in this UI. - */ - protected Type logLevel; - - private PrintWriterTreeLogger consoleLogger = null; - - /** - * Create a top-level logger for messages which are not associated with the - * web server or any module. Defaults to logging to stdout. - * - * @return TreeLogger instance to use - */ - public TreeLogger getTopLogger() { - return getConsoleLogger(); - } - - /** - * Create the web server portion of the UI if not already created, and - * return its TreeLogger instance. - * - * @param serverName short name of the web server or null if only the icon - * should be used - * @param serverIcon byte array containing an icon (fitting into 24x24) to - * use for the server, or null if only the name should be used - * @return TreeLogger instance - */ - public abstract TreeLogger getWebServerLogger(String serverName, - byte[] serverIcon); - - /** - * Initialize the UI - must be called before any other method. - * - * <p>Subclasses should call super.initialize(logLevel). - * - * @param logLevel log level for all logging - */ - public void initialize(Type logLevel) { - this.logLevel = logLevel; - } - - /** - * Load a module - * - * @param userAgent full user agent name - * @param remoteSocket name of remote socket endpoint in host:port format - * @param url URL of top-level window - * @param tabKey stable browser tab identifier, or the empty string if no - * such identifier is available - * @param moduleName the name of the module loaded - * @param sessionKey a unique session key - * @param agentTag short-form user agent identifier, suitable for use in - * a label for this connection - * @param agentIcon icon to use for the user agent (fits inside 24x24) or - * null if unavailable - * @param logLevel logging detail requested - * @return a handle to the module - */ - public abstract ModuleHandle loadModule(String userAgent, - String remoteSocket, String url, String tabKey, String moduleName, - String sessionKey, String agentTag, byte[] agentIcon, Type logLevel); - - /** - * Set a callback for the UI to report events. - * - * @param event - * @param callback - */ - public void setCallback(String event, Callback callback) { - callbacks.put(event, callback); - } - - /** - * Unload a previously loaded module. - * - * @param module ModuleHandle instance returned from loadModule on this UI - * instance - */ - public abstract void unloadModule(ModuleHandle module); - - /** - * Call callbacks for a given event. - * - * @param event - * @param callbackData arbitrary object, depending on the event type - */ - protected void callback(String event, Object callbackData) { - Callback callback = callbacks.get(event); - if (callback != null) { - callback.callback(event, callbackData); - } - } - - /** - * @return a console-based logger. - */ - protected TreeLogger getConsoleLogger() { - if (consoleLogger == null) { - consoleLogger = new PrintWriterTreeLogger(); - consoleLogger.setMaxDetail(logLevel); - } - return consoleLogger; - } - - /** - * Returns true if a callback has been registered for an event. - * - * @param event - * @return true if a callback has been registered for event - */ - protected boolean hasCallback(String event) { - return callbacks.get(event) != null; - } -} ======================================= --- /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/HeadlessUI.java Mon Oct 12 16:39:12 2009 +++ /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/HeadlessUI.java Tue Oct 13 09:24:19 2009 @@ -18,6 +18,7 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.TreeLogger.Type; import com.google.gwt.dev.HostedModeBase.HostedModeBaseOptions; +import com.google.gwt.dev.ui.DevModeUI; /** * UI used in headless operation. ======================================= --- /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/HostedModeBase.java Mon Oct 12 16:39:12 2009 +++ /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/HostedModeBase.java Tue Oct 13 09:24:19 2009 @@ -18,8 +18,6 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.TypeOracle; -import com.google.gwt.dev.DevModeUI.Callback; -import com.google.gwt.dev.DevModeUI.ModuleHandle; import com.google.gwt.dev.Precompile.PrecompileOptionsImpl; import com.google.gwt.dev.cfg.ModuleDef; import com.google.gwt.dev.cfg.ModuleDefLoader; @@ -32,6 +30,10 @@ import com.google.gwt.dev.shell.ModuleSpaceHost; import com.google.gwt.dev.shell.OophmSessionHandler; import com.google.gwt.dev.shell.ShellModuleSpaceHost; +import com.google.gwt.dev.ui.DevModeUI; +import com.google.gwt.dev.ui.DoneCallback; +import com.google.gwt.dev.ui.DoneEvent; +import com.google.gwt.dev.ui.DevModeUI.ModuleHandle; import com.google.gwt.dev.util.BrowserInfo; import com.google.gwt.dev.util.Util; import com.google.gwt.dev.util.arg.ArgHandlerDisableAggressiveOptimization; @@ -64,7 +66,7 @@ * The main executable class for the hosted mode shell. This class must not have * any GUI dependencies. */ -abstract class HostedModeBase { +abstract class HostedModeBase implements DoneCallback { public class UiBrowserWidgetHostImpl extends BrowserWidgetHostImpl { @@ -621,6 +623,10 @@ return normalizeURL(unknownUrlText, getPort(), getHost()); } + public void onDone() { + setDone(); + } + /** * Sets up all the major aspects of running the shell graphically, including * creating the main window and optionally starting an embedded web server. @@ -713,11 +719,10 @@ topLogger = ui.getTopLogger(); // Set done callback - ui.setCallback(DevModeUI.DONE, new Callback() { - public void callback(String doneEvent, Object ignored) { - setDone(); - } - }); + ui.setCallback(DoneEvent.getType(), this); +// public void callback(String doneEvent, Object ignored) { +// setDone(); +// } // Check for updates final TreeLogger logger = getTopLogger(); ======================================= --- /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/SwingUI.java Mon Oct 12 16:39:12 2009 +++ /changes/jat/abstractui/dev/core/src/com/google/gwt/dev/SwingUI.java Tue Oct 13 09:24:19 2009 @@ -20,6 +20,11 @@ import com.google.gwt.dev.HostedModeBase.HostedModeBaseOptions; import com.google.gwt.dev.WebServerPanel.RestartAction; import com.google.gwt.dev.shell.ShellMainWindow; +import com.google.gwt.dev.ui.DevModeUI; +import com.google.gwt.dev.ui.DoneCallback; +import com.google.gwt.dev.ui.DoneEvent; +import com.google.gwt.dev.ui.RestartServerCallback; +import com.google.gwt.dev.ui.RestartServerEvent; import com.google.gwt.dev.util.collect.HashMap; import java.awt.event.WindowAdapter; @@ -149,10 +154,12 @@ public TreeLogger getWebServerLogger(String serverName, byte[] serverIcon) { if (webServerLog == null) { RestartAction restartAction = null; - if (hasCallback(RESTART_SERVER)) { + final RestartServerCallback callback = getCallback( + RestartServerEvent.getType()); + if (callback != null) { restartAction = new RestartAction() { public void restartServer(TreeLogger logger) { - callback(RESTART_SERVER, logger); + callback.onRestartServer(logger); } }; } @@ -185,7 +192,10 @@ frame.addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent e) { - callback(DONE, null); + DoneCallback callback = getCallback(DoneEvent.getType()); + if (callback != null) { + callback.onDone(); + } } }); frame.setIconImage(loadImageIcon("icon16.png").getImage()); ======================================= --- /changes/jat/abstractui/dev/oophm/overlay/com/google/gwt/dev/HostedMode.java Mon Oct 12 16:39:12 2009 +++ /changes/jat/abstractui/dev/oophm/overlay/com/google/gwt/dev/HostedMode.java Tue Oct 13 09:24:19 2009 @@ -22,10 +22,11 @@ import com.google.gwt.core.ext.linker.ArtifactSet; import com.google.gwt.core.ext.linker.impl.StandardLinkerContext; import com.google.gwt.dev.Compiler.CompilerOptionsImpl; -import com.google.gwt.dev.DevModeUI.Callback; import com.google.gwt.dev.cfg.ModuleDef; import com.google.gwt.dev.shell.ArtifactAcceptor; import com.google.gwt.dev.shell.jetty.JettyLauncher; +import com.google.gwt.dev.ui.RestartServerCallback; +import com.google.gwt.dev.ui.RestartServerEvent; import com.google.gwt.dev.util.InstalledHelpInfo; import com.google.gwt.dev.util.Util; import com.google.gwt.dev.util.arg.ArgHandlerExtraDir; @@ -47,7 +48,8 @@ * this class is to be determined. Consider this class as having <b>no</b> * public API other than {...@link #main(String[])}. */ -public class HostedMode extends HostedModeBase { +public class HostedMode extends HostedModeBase + implements RestartServerCallback { /** * Handles the -server command line flag. @@ -376,15 +378,7 @@ @Override protected int doStartUpServer() { try { - ui.setCallback(DevModeUI.RESTART_SERVER, new Callback() { - public void callback(String event, Object callbackData) { - try { - server.refresh(); - } catch (UnableToCompleteException e) { - // ignore, problem already logged - } - } - }); + ui.setCallback(RestartServerEvent.getType(), this); // TODO(jat): find a safe way to get an icon for the servlet container TreeLogger serverLogger = ui.getWebServerLogger(getWebServerName(), null); serverLogger.log(TreeLogger.INFO, "Starting HTTP on port " + getPort(), @@ -401,7 +395,8 @@ System.err.println("Unable to start embedded HTTP server"); e.printStackTrace(); } - ui.setCallback(DevModeUI.RESTART_SERVER, null); + // Clear the callback if we failed to start the server + ui.setCallback(RestartServerEvent.getType(), null); return -1; } @@ -534,4 +529,15 @@ } } } -} + + /** + * Called on a restart server event. + */ + public void onRestartServer(TreeLogger logger) { + try { + server.refresh(); + } catch (UnableToCompleteException e) { + // ignore, problem already logged + } + } +} --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---
