/*
* Copyright 2000-2016 Vaadin Ltd.
*
* 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.vaadin.event;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import com.vaadin.event.Action.Container;
import com.vaadin.event.Action.Handler;
import com.vaadin.server.KeyMapper;
import com.vaadin.server.PaintException;
import com.vaadin.server.PaintTarget;
import com.vaadin.server.VariableOwner;
import com.vaadin.ui.Component;
/**
* Javadoc TODO
*
* Notes:
* <p>
* Empties the keymapper for each repaint to avoid leaks; can cause problems in
* the future if the client assumes key don't change. (if lazyloading, one must
* not cache results)
* </p>
*
*
*/
public class ActionManager
implements Action.Container, Action.Handler, Action.Notifier {
private static final long serialVersionUID = 1641868163608066491L;
/**
* List of action handlers. Guaranteed to keep the original insertion order.
*/
protected HashSet<Action> ownActions = null;
/**
* List of action handlers. Guaranteed to keep the original insertion order.
*/
protected HashSet<Handler> actionHandlers = null;
/** Action mapper */
protected KeyMapper<Action> actionMapper = null;
protected Component viewer;
private boolean clientHasActions = false;
public ActionManager() {
}
public <T extends Component & Container & VariableOwner> ActionManager(
T viewer) {
this.viewer = viewer;
}
private void requestRepaint() {
if (viewer != null) {
viewer.markAsDirty();
}
}
public <T extends Component & Container & VariableOwner> void setViewer(
T viewer) {
// This somewhat complicated check exists to make sure that proxies are
// handled correctly
if (this.viewer == viewer
|| (this.viewer != null && this.viewer.equals(viewer))) {
return;
}
if (this.viewer != null) {
((Container) this.viewer).removeActionHandler(this);
}
requestRepaint(); // this goes to the old viewer
if (viewer != null) {
viewer.addActionHandler(this);
}
this.viewer = viewer;
requestRepaint(); // this goes to the new viewer
}
@Override
public <T extends Action & Action.Listener> void addAction(T action) {
if (ownActions == null) {
ownActions = new LinkedHashSet<>();
}
if (ownActions.add(action)) {
requestRepaint();
}
}
@Override
public <T extends Action & Action.Listener> void removeAction(T action) {
if (ownActions != null) {
if (ownActions.remove(action)) {
requestRepaint();
}
}
}
@Override
public void addActionHandler(Handler actionHandler) {
if (equals(actionHandler)) {
// don't add the actionHandler to itself
return;
}
if (actionHandler != null) {
if (actionHandlers == null) {
actionHandlers = new LinkedHashSet<>();
}
if (actionHandlers.add(actionHandler)) {
requestRepaint();
}
}
}
@Override
public void removeActionHandler(Action.Handler actionHandler) {
if (actionHandlers != null && actionHandlers.contains(actionHandler)) {
if (actionHandlers.remove(actionHandler)) {
requestRepaint();
}
if (actionHandlers.isEmpty()) {
actionHandlers = null;
}
}
}
public void removeAllActionHandlers() {
if (actionHandlers != null) {
actionHandlers = null;
requestRepaint();
}
}
public void paintActions(Object actionTarget, PaintTarget paintTarget)
throws PaintException {
actionMapper = null;
LinkedHashSet<Action> actions = getActionSet(actionTarget, viewer);
/*
* Must repaint whenever there are actions OR if all actions have been
* removed but still exist on client side
*/
if (!actions.isEmpty() || clientHasActions) {
actionMapper = new KeyMapper<>();
paintTarget.addVariable((VariableOwner) viewer, "action", "");
paintTarget.startTag("actions");
for (final Action a : actions) {
paintTarget.startTag("action");
final String akey = actionMapper.key(a);
paintTarget.addAttribute("key", akey);
if (a.getCaption() != null) {
paintTarget.addAttribute("caption", a.getCaption());
}
if (a.getIcon() != null) {
paintTarget.addAttribute("icon", a.getIcon());
}
if (a instanceof ShortcutAction) {
final ShortcutAction sa = (ShortcutAction) a;
paintTarget.addAttribute("kc", sa.getKeyCode());
final int[] modifiers = sa.getModifiers();
if (modifiers != null) {
final String[] smodifiers = new String[modifiers.length];
for (int i = 0; i < modifiers.length; i++) {
smodifiers[i] = String.valueOf(modifiers[i]);
}
paintTarget.addAttribute("mk", smodifiers);
}
}
paintTarget.endTag("action");
}
paintTarget.endTag("actions");
}
/*
* Update flag for next repaint so we know if we need to paint empty
* actions or not (must send actions is client had actions before and
* all actions were removed).
*/
clientHasActions = !actions.isEmpty();
}
public void handleActions(Map<String, Object> variables, Container sender) {
if (variables.containsKey("action") && actionMapper != null) {
final String key = (String) variables.get("action");
final Action action = actionMapper.get(key);
final Object target = variables.get("actiontarget");
if (action != null) {
handleAction(action, sender, target);
}
}
}
@Override
public Action[] getActions(Object target, Object sender) {
LinkedHashSet<Action> actions = getActionSet(target, sender);
return actions.toArray(new Action[actions.size()]);
}
@Override
public void handleAction(Action action, Object sender, Object target) {
if (actionHandlers != null) {
Handler[] array = actionHandlers
.toArray(new Handler[actionHandlers.size()]);
for (Handler handler : array) {
handler.handleAction(action, sender, target);
}
}
if (ownActions != null && ownActions.contains(action)
&& action instanceof Action.Listener) {
((Action.Listener) action).handleAction(sender, target);
}
}
private LinkedHashSet<Action> getActionSet(Object target, Object sender) {
LinkedHashSet<Action> actions = new LinkedHashSet<>();
if (ownActions != null) {
actions.addAll(ownActions);
}
if (actionHandlers != null) {
for (Action.Handler h : actionHandlers) {
Action[] as = h.getActions(target, sender);
if (as != null) {
actions.addAll(Arrays.asList(as));
}
}
}
return actions;
}
}