/*
* Copyright 2008-2017 the original author or authors.
*
* 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 org.codehaus.griffon.runtime.core.view;
import griffon.core.CallableWithArgs;
import griffon.core.GriffonApplication;
import griffon.core.RunnableWithArgs;
import griffon.core.view.WindowDisplayHandler;
import griffon.exceptions.InstanceNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.Collections;
import java.util.Map;
import static griffon.util.AnnotationUtils.named;
import static griffon.util.ConfigUtils.getConfigValue;
import static griffon.util.GriffonNameUtils.requireNonBlank;
import static java.util.Objects.requireNonNull;
/**
* Implementation of a per window {@code WindowDisplayHandler} that can be configured via a DSL.<p>
* This is the default {@code WindowDisplayHandler} used by {@code SwingApplication}. It expects a configuration
* entry in <code>griffon-app/conf/Config.groovy</code> that looks like the following one<p>
* <pre>
* windowManager {
* myWindowName = [
* show: {name, window -> ... },
* hide: {name, window -> ... }
* ]
* myOtherWindowName = [
* show: {name, window -> ... }
* ]
* }
* </pre>
* <p>
* For these settings to work you must specify a <code>name:</code> property on the Window/Frame instance. This
* {@code WindowDisplayHandler} is smart enough to use the default show/hide behavior should any or both are not specified
* or if a window name does not have a matching configuration. The default behavior will also be used if the Window/Frame
* does not have a value for its <code>name:</code> property.<p>
* There's a third option that can be set for each configured window, and that is a delegate {@code WindowDisplayHandler} that
* will be used for that window alone. The following example shows how it can be configured<p>
* <pre>
* windowManager {
* myWindowName = [
* handler: new MyCustomWindowDisplayHandler()
* ]
* myOtherWindowName = [
* show: {name, window -> ... }
* ]
* }
* </pre>
* <p>
* Lastly, a global handler can be specified for all windows that have not been configured. If specified, this handler will
* override the usage of the default one. It can be configured as follows<p>
* <pre>
* windowManager {
* defaultHandler = new MyCustomWindowDisplayHandler()
* myOtherWindowName = [
* show: {name, window -> ... }
* ]
* }
* </pre>
* <p>
* Fine grained control for default <code>show</code> and <code>hide</code> is also possible, by specifying <code>defaultShow</code>
* and/or <code>defaultHide</code> properties at the global level. These properties take precedence over <code>defaultHandler</code> .
* <p>
* <pre>
* windowManager {
* defaultHide = {name, window -> ... }
* myOtherWindowName = [
* show: {name, window -> ... }
* ]
* }
* </pre>
* <p>
* <strong>Note:</strong> the value for <code>show</code> and <code>hide</code> can be either a Closure or a {@code RunnableWithArgs}.
*
* @author Andres Almiray
* @since 2.0.0
*/
public class ConfigurableWindowDisplayHandler<W> implements WindowDisplayHandler<W> {
protected static final String ERROR_NAME_BLANK = "Argument 'name' must not be blank";
protected static final String ERROR_WINDOW_NULL = "Argument 'window' must not be null";
private static final Logger LOG = LoggerFactory.getLogger(ConfigurableWindowDisplayHandler.class);
private final GriffonApplication application;
private final WindowDisplayHandler<W> delegateWindowsDisplayHandler;
@Inject
public ConfigurableWindowDisplayHandler(@Nonnull GriffonApplication application, @Nonnull @Named("defaultWindowDisplayHandler") WindowDisplayHandler<W> delegateWindowsDisplayHandler) {
this.application = requireNonNull(application, "Argument 'application' must not be null");
this.delegateWindowsDisplayHandler = requireNonNull(delegateWindowsDisplayHandler, "Argument 'delegateWindowsDisplayHandler' must not be null");
}
@SuppressWarnings("unchecked")
public void show(@Nonnull String name, @Nonnull W window) {
requireNonBlank(name, ERROR_NAME_BLANK);
requireNonNull(window, ERROR_WINDOW_NULL);
Map<String, Object> options = windowBlock(name);
if (!options.isEmpty()) {
Object handler = options.get("show");
if (canBeRun(handler)) {
LOG.trace("Showing {} with show: handler", name);
run(handler, name, window);
return;
} else if (options.get("handler") instanceof WindowDisplayHandler) {
LOG.trace("Showing {} with handler: handler", name);
((WindowDisplayHandler<W>) options.get("handler")).show(name, window);
return;
}
}
if (handleShowByInjectedHandler(name, window)) {
return;
}
options = windowManagerBlock();
if (!options.isEmpty()) {
Object defaultShow = options.get("defaultShow");
if (canBeRun(defaultShow)) {
LOG.trace("Showing {} with defaultShow: handler", name);
run(defaultShow, name, window);
return;
}
}
LOG.trace("Showing {} with default handler", name);
fetchDefaultWindowDisplayHandler().show(name, window);
}
@SuppressWarnings("unchecked")
public void hide(@Nonnull String name, @Nonnull W window) {
requireNonBlank(name, ERROR_NAME_BLANK);
requireNonNull(window, ERROR_WINDOW_NULL);
Map<String, Object> options = windowBlock(name);
if (!options.isEmpty()) {
Object handler = options.get("hide");
if (canBeRun(handler)) {
LOG.trace("Hiding {} with hide: handler", name);
run(handler, name, window);
return;
} else if (options.get("handler") instanceof WindowDisplayHandler) {
LOG.trace("Hiding {} with handler: handler", name);
((WindowDisplayHandler<W>) options.get("handler")).hide(name, window);
return;
}
}
if (handleHideByInjectedHandler(name, window)) {
return;
}
options = windowManagerBlock();
if (!options.isEmpty()) {
Object defaultHide = options.get("defaultHide");
if (canBeRun(defaultHide)) {
LOG.trace("Hiding {} with defaultHide: handler", name);
run(defaultHide, name, window);
return;
}
}
LOG.trace("Hiding {} with default handler", name);
fetchDefaultWindowDisplayHandler().hide(name, window);
}
@SuppressWarnings("unchecked")
protected boolean handleShowByInjectedHandler(@Nonnull String name, @Nonnull W window) {
try {
WindowDisplayHandler<W> handler = getApplication().getInjector()
.getInstance(WindowDisplayHandler.class, named(name));
LOG.trace("Showing {} with injected handler", name);
handler.show(name, window);
return true;
} catch (InstanceNotFoundException infe) {
// ignore
}
return false;
}
@SuppressWarnings("unchecked")
protected boolean handleHideByInjectedHandler(@Nonnull String name, @Nonnull W window) {
try {
WindowDisplayHandler<W> handler = getApplication().getInjector()
.getInstance(WindowDisplayHandler.class, named(name));
LOG.trace("Hiding {} with injected handler", name);
handler.hide(name, window);
return true;
} catch (InstanceNotFoundException infe) {
// ignore
}
return false;
}
public WindowDisplayHandler<W> getDelegateWindowsDisplayHandler() {
return delegateWindowsDisplayHandler;
}
protected boolean canBeRun(@Nullable Object obj) {
return obj instanceof RunnableWithArgs || obj instanceof CallableWithArgs;
}
protected void run(@Nonnull Object handler, @Nonnull String name, @Nonnull W window) {
if (handler instanceof RunnableWithArgs) {
((RunnableWithArgs) handler).run(name, window);
} else if (handler instanceof CallableWithArgs) {
((CallableWithArgs<?>) handler).call(name, window);
}
}
protected Map<String, Object> windowManagerBlock() {
return application.getConfiguration().get("windowManager", Collections.<String, Object>emptyMap());
}
protected Map<String, Object> windowBlock(String windowName) {
Map<String, Object> options = windowManagerBlock();
return getConfigValue(options, windowName, Collections.<String, Object>emptyMap());
}
protected GriffonApplication getApplication() {
return application;
}
@Nonnull
@SuppressWarnings("unchecked")
protected WindowDisplayHandler<W> fetchDefaultWindowDisplayHandler() {
Object handler = windowManagerBlock().get("defaultHandler");
return handler instanceof WindowDisplayHandler ? (WindowDisplayHandler<W>) handler : delegateWindowsDisplayHandler;
}
}