/*******************************************************************************
* Copyright (c) 2007, 2014 compeople AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* compeople AG - initial API and implementation
*******************************************************************************/
package org.eclipse.riena.navigation.model;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.Assert;
import org.eclipse.riena.core.marker.IMarkable;
import org.eclipse.riena.core.marker.IMarker;
import org.eclipse.riena.core.marker.Markable;
import org.eclipse.riena.navigation.ApplicationNodeManager;
import org.eclipse.riena.navigation.IAction;
import org.eclipse.riena.navigation.IJumpTargetListener;
import org.eclipse.riena.navigation.INavigationContext;
import org.eclipse.riena.navigation.INavigationNode;
import org.eclipse.riena.navigation.INavigationNodeController;
import org.eclipse.riena.navigation.INavigationProcessor;
import org.eclipse.riena.navigation.ISimpleNavigationNodeListener;
import org.eclipse.riena.navigation.NavigationArgument;
import org.eclipse.riena.navigation.NavigationNodeId;
import org.eclipse.riena.navigation.common.TypecastingObject;
import org.eclipse.riena.navigation.listener.INavigationNodeListener;
import org.eclipse.riena.navigation.listener.INavigationNodeListenerable;
import org.eclipse.riena.ui.core.context.IContext;
import org.eclipse.riena.ui.core.marker.DisabledMarker;
import org.eclipse.riena.ui.core.marker.HiddenMarker;
import org.eclipse.riena.ui.filter.IUIFilter;
import org.eclipse.riena.ui.filter.IUIFilterable;
import org.eclipse.riena.ui.filter.impl.UIFilterable;
import org.eclipse.riena.ui.ridgets.tree2.ITreeNode2;
/**
* Default implementation of all features common to all navigation node objects The parent-child relations are not included!
*
* @param <S>
* the type of implemented node
* @param <C>
* the type of the child nodes
* @param <L>
* the type of the listener
*/
public abstract class NavigationNode<S extends INavigationNode<C>, C extends INavigationNode<?>, L extends INavigationNodeListener<S, C>> extends
TypecastingObject implements INavigationNode<C>, INavigationNodeListenerable<S, C, L>, IContext {
/**
* @since 4.0
*/
protected final PropertyChangeSupport propertyChangeSupport;
private NavigationNodeId nodeId;
private State state;
private String label;
private String icon;
private String toolTipText;
private boolean expanded;
private INavigationNodeController navigationNodeController;
private INavigationProcessor navigationProcessor;
private LinkedList<C> children;
private boolean selected;
private final List<L> listeners;
private final List<ISimpleNavigationNodeListener> simpleListeners;
private final IMarkable markable;
private final IUIFilterable filterable;
private Map<String, Object> context;
private final Set<IAction> actions;
private IMarker hiddenMarker;
private IMarker disabledMarker;
private Boolean cachedVisible;
private Boolean cachedEnabled;
private boolean isNodeIdChange;
private INavigationNode<?> parent;
private boolean blocked;
/**
* Creates a NavigationNode.
*
* @param nodeId
* Identifies the node in the application model tree.
*/
public NavigationNode(final NavigationNodeId nodeId) {
super();
this.nodeId = nodeId;
listeners = new LinkedList<L>();
propertyChangeSupport = new PropertyChangeSupport(this);
simpleListeners = new LinkedList<ISimpleNavigationNodeListener>();
children = new LinkedList<C>();
markable = createMarkable();
filterable = createFilterable();
actions = new LinkedHashSet<IAction>();
state = State.CREATED;
context = null;
isNodeIdChange = false;
}
/**
* Creates a NavigationNode.
*
* @param nodeId
* Identifies the node in the application model tree.
* @param pLabel
* The label of the node.
*/
public NavigationNode(final NavigationNodeId nodeId, final String pLabel) {
this(nodeId);
setLabel(pLabel);
}
public void setNavigationNodeController(final INavigationNodeController pNavigationNodeController) {
navigationNodeController = pNavigationNodeController;
notifyControllerChanged();
}
@SuppressWarnings("unchecked")
private void notifyControllerChanged() {
for (final L next : getListeners()) {
next.presentationChanged((S) this);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.presentationChanged(this);
}
}
@SuppressWarnings("unchecked")
private void notifySelectedChanged() {
for (final L next : getListeners()) {
next.selectedChanged((S) this);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.selectedChanged(this);
}
}
@SuppressWarnings("unchecked")
private void notifyLabelChanged() {
for (final L next : getListeners()) {
next.labelChanged((S) this);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.labelChanged(this);
}
}
@SuppressWarnings("unchecked")
private void notifyIconChanged() {
for (final L next : getListeners()) {
next.iconChanged((S) this);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.iconChanged(this);
}
}
@SuppressWarnings("unchecked")
private void notifyChildAdded(final C pChild) {
for (final L next : getListeners()) {
next.childAdded((S) this, pChild);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.childAdded(this, pChild);
}
}
/**
* Notifies every registered listener that this node is prepared now.
*/
@SuppressWarnings("unchecked")
private void notifyPrepared() {
for (final L next : getListeners()) {
next.prepared((S) this);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.prepared(this);
}
}
@SuppressWarnings("unchecked")
private void notifyActivated() {
for (final L next : getListeners()) {
next.activated((S) this);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.activated(this);
}
}
@SuppressWarnings("unchecked")
private void notifyBeforeActivated() {
for (final L next : getListeners()) {
next.beforeActivated((S) this);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.beforeActivated(this);
}
}
@SuppressWarnings("unchecked")
private void notifyAfterActivated() {
for (final L next : getListeners()) {
next.afterActivated((S) this);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.afterActivated(this);
}
}
@SuppressWarnings("unchecked")
private void notifyDeactivated() {
for (final L next : getListeners()) {
next.deactivated((S) this);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.deactivated(this);
}
}
@SuppressWarnings("unchecked")
private void notifyBeforeDeactivated() {
for (final L next : getListeners()) {
next.beforeDeactivated((S) this);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.beforeDeactivated(this);
}
}
@SuppressWarnings("unchecked")
private void notifyAfterDeactivated() {
for (final L next : getListeners()) {
next.afterDeactivated((S) this);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.afterDeactivated(this);
}
}
@SuppressWarnings("unchecked")
private void notifyChildRemoved(final C pChild) {
for (final L next : getListeners()) {
next.childRemoved((S) this, pChild);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.childRemoved(this, pChild);
}
}
public void setNavigationProcessor(final INavigationProcessor pProcessor) {
navigationProcessor = pProcessor;
}
/**
* Checks if the given class (or a superclass) implements the correct type of children.
*
* @param childClass
* class of child
*/
protected boolean checkChildClass(final Class<?> childClass) {
Assert.isNotNull(childClass);
final Type[] types = childClass.getInterfaces();
for (final Type type : types) {
if (type == getValidChildType()) {
return true;
}
if (type == INavigationNode.class) {
return false;
}
}
final Class<?> superClass = childClass.getSuperclass();
if (superClass != null) {
return checkChildClass(superClass);
}
return false;
}
public void addChild(final C child) {
addChild(children.size(), child);
}
/**
* @since 2.0
*/
public void addChild(final int index, final C child) {
checkChild(child);
final List<C> oldList = new ArrayList<C>(children);
final LinkedList<C> newList = new LinkedList<C>(children);
newList.add(index, child);
children = newList;
fireChildAdded(child, oldList);
// Adds the parent to the child after all listeners are notified that the child was added to the parent!
addChildParent(child);
}
private void fireChildAdded(final C child, final List<C> oldList) {
propertyChangeSupport.firePropertyChange(INavigationNodeListenerable.PROPERTY_CHILDREN, oldList, children);
notifyChildAdded(child);
}
private void checkChild(final C child) {
if (child == null) {
throw new NavigationModelFailure("Cannot add null!"); //$NON-NLS-1$
}
if (hasChild(child)) {
throw new NavigationModelFailure("Child node \"" + child.toString() + "\" is already added!"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (child.isDisposed()) {
throw new NavigationModelFailure("Cannot add disposed child node \"" + child.toString() + "\"!"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (child == this) {
throw new NavigationModelFailure("Cannot add node \"" + child.toString() + "\" to itself!"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (!checkChildClass(child.getClass())) {
String msg = "Cannot add \"" + child.toString() + "\" to \"" + this.toString() + "\"!"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
msg += " Because node isn't instance of " + getValidChildType().toString() + "."; //$NON-NLS-1$ //$NON-NLS-2$
throw new NavigationModelFailure(msg);
}
if (child.getNodeId() != null) {
if (child.getNodeId().equals(this.getNodeId())) {
String msg = "Cannot add \"" + child.toString() + "\" to \"" + this.toString() + "\"!"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
msg += " Because both nodes have the same NavigationNodeId."; //$NON-NLS-1$
throw new NavigationModelFailure(msg);
}
if (hasChild(child.getNodeId())) {
String msg = "Cannot add \"" + child.toString() + "\" to \"" + this.toString() + "\"!"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
msg += " Because a child with the same NavigationNodeId already exists."; //$NON-NLS-1$
throw new NavigationModelFailure(msg);
}
}
}
protected boolean hasChild(final INavigationNode<?> pChild) {
return children.contains(pChild);
}
/**
* Checks if this node has a child node with the given ID.
*
* @param nodeId
* ID of a navigation node
* @return {@code true} child with the given ID exists; otherwise {@code false}
* @since 3.0
*/
protected boolean hasChild(final NavigationNodeId nodeId) {
for (final C child : children) {
if (child.getNodeId() != null) {
if (child.getNodeId().equals(nodeId)) {
return true;
}
}
}
return false;
}
protected void addChildParent(final C child) {
child.setParent(this);
}
public List<C> getChildren() {
return children;
}
@SuppressWarnings("unchecked")
public void removeChild(final INavigationNode<?> child) {
if (child == null) {
throw new NavigationModelFailure("Cannot remove null!"); //$NON-NLS-1$
}
if (!hasChild(child)) {
throw new NavigationModelFailure("Node \"" + child.toString() + "\" isn't a child of \"" + this.toString() + "\"!"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
if (child.isActivated()) {
throw new NavigationModelFailure("Cannot remove active child \"" + child.toString() + "\"!"); //$NON-NLS-1$ //$NON-NLS-2$
}
final List<C> oldList = new ArrayList<C>(children);
final LinkedList<C> newList = new LinkedList<C>(children);
newList.remove(child);
children = newList;
child.setParent(null);
propertyChangeSupport.firePropertyChange(INavigationNodeListenerable.PROPERTY_CHILDREN, oldList, children);
// if this node has the child, than it can be casted to C,
// because it must be C
notifyChildRemoved((C) child);
}
public C getChild(final int index) {
if (children != null && children.size() > index) {
return children.get(index);
} else {
return null;
}
}
public INavigationNode<?> findNode(final NavigationNodeId nodeId) {
if (getNodeId() != null && getNodeId().equals(nodeId)) {
return this;
}
for (final C child : children) {
final INavigationNode<?> foundChild = child.findNode(nodeId);
if (foundChild != null) {
return foundChild;
}
}
return null;
}
public void activate() {
getNavigationProcessor().activate(this);
}
/**
* @since 2.0
*/
public void prepare() {
final List<C> oldChildren = new ArrayList<C>(getChildren());
getNavigationProcessor().prepare(this);
//if the prepare() creates child nodes, we have to fire an event to notify the tree about changes
if (!getChildren().isEmpty() && getChildren().size() > oldChildren.size()) {
fireChildAdded(getChild(0), oldChildren);
}
}
public boolean allowsActivate(final INavigationContext context) {
final INavigationNodeController localPresentation = getNavigationNodeController();
if (localPresentation != null) {
return localPresentation.allowsActivate(this, context);
} else {
return true;
}
}
public boolean allowsDeactivate(final INavigationContext context) {
final INavigationNodeController localPresentation = getNavigationNodeController();
if (localPresentation != null) {
return localPresentation.allowsDeactivate(this, context);
} else {
return true;
}
}
public void addPropertyChangeListener(final PropertyChangeListener propertyChangeListener) {
propertyChangeSupport.addPropertyChangeListener(propertyChangeListener);
}
public void removePropertyChangeListener(final PropertyChangeListener propertyChangeListener) {
propertyChangeSupport.removePropertyChangeListener(propertyChangeListener);
}
public void addListener(final L listener) {
listeners.add(listener);
}
public void addSimpleListener(final ISimpleNavigationNodeListener simpleListener) {
simpleListeners.add(simpleListener);
}
public void removeSimpleListener(final ISimpleNavigationNodeListener simpleListener) {
simpleListeners.remove(simpleListener);
}
public void removeListener(final L listener) {
listeners.remove(listener);
}
protected List<L> getListeners() {
return new LinkedList<L>(listeners);
}
protected List<ISimpleNavigationNodeListener> getSimpleListeners() {
return new LinkedList<ISimpleNavigationNodeListener>(simpleListeners);
}
public String getLabel() {
return label;
}
public void setLabel(final String label) {
final String old = this.label;
this.label = label;
propertyChangeSupport.firePropertyChange(INavigationNode.PROPERTY_LABEL, old, label);
notifyLabelChanged();
}
public String getIcon() {
return icon;
}
public void setIcon(final String icon) {
this.icon = icon;
notifyIconChanged();
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("NavigationNode [label=").append(getLabel()).append(", nodeId=").append(getNodeId()) //$NON-NLS-1$ //$NON-NLS-2$
.append("]"); //$NON-NLS-1$
return builder.toString();
}
public INavigationNode<?> getParent() {
return parent;
}
public void setParent(final INavigationNode<?> pParent) {
parent = pParent;
notifyParentChanged();
}
@SuppressWarnings("unchecked")
private void notifyParentChanged() {
for (final L next : getListeners()) {
next.parentChanged((S) this);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.parentChanged(this);
}
}
/**
* Look for the navigation processor in the hierarchy. The navigation processor can be set at any level
*/
public INavigationProcessor getNavigationProcessor() {
if (navigationProcessor != null) {
return navigationProcessor;
} else if (getParent() != null) {
return getParent().getNavigationProcessor();
} else if (ApplicationNodeManager.getApplicationNode() != null) {
// if no navigation processor was found in the hierarchy, maybe the application node has one
return ApplicationNodeManager.getApplicationNode().getNavigationProcessor();
} else {
// if nobody ha a navigation processor, return the default navigation processor
return ApplicationNodeManager.getDefaultNavigationProcessor();
}
}
/**
* @return the controller of this node
*/
public INavigationNodeController getNavigationNodeController() {
return navigationNodeController;
}
/**
* Look for the next in the hierarchy available controller
*/
public INavigationNodeController getNextNavigationNodeController() {
if (navigationNodeController != null) {
return navigationNodeController;
} else if (getParent() != null) {
return getParent().getNavigationNodeController();
} else {
return null;
}
}
public boolean isExpanded() {
return expanded;
}
public void setExpanded(final boolean pExpanded) {
// if (expanded != pExpanded) {
expanded = pExpanded;
notifyExpandedChanged();
// }
}
@SuppressWarnings("unchecked")
private void notifyExpandedChanged() {
for (final L next : getListeners()) {
next.expandedChanged((S) this);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.expandedChanged(this);
}
}
public boolean isLeaf() {
return getChildren().size() < 1;
}
/**
* Each node has its own markable
*
* @return the markable helper object of this node
*/
public IMarkable getMarkable() {
return markable;
}
/**
* Creates the markable, can return null, then will be the markable from the parent used
*
* @return a Markable or null
*/
protected IMarkable createMarkable() {
return new Markable();
}
public Collection<? extends IMarker> getMarkers() {
return getMarkable().getMarkers();
}
private void clearCachedValues() {
cachedVisible = null;
cachedEnabled = null;
}
public void addMarker(final IMarker marker) {
final INavigationProcessor proc = getNavigationProcessor();
if (proc != null) {
final boolean oldEnabled = isEnabled();
final boolean oldVisible = isVisible();
proc.addMarker(this, marker);
if (oldEnabled != isEnabled()) {
propertyChangeSupport.firePropertyChange(ITreeNode2.PROPERTY_ENABLED, oldEnabled, isEnabled());
}
if (oldVisible != isVisible()) {
propertyChangeSupport.firePropertyChange(ITreeNode2.PROPERTY_VISIBLE, oldVisible, isVisible());
}
}
}
public void addMarker(final INavigationContext context, final IMarker marker) {
getMarkable().addMarker(marker);
clearCachedValues();
notifyMarkersChanged(marker);
if ((marker instanceof DisabledMarker) || (marker instanceof HiddenMarker)) {
for (final C child : getChildren()) {
child.addMarker(marker);
}
}
}
public <T extends IMarker> Collection<T> getMarkersOfType(final Class<T> type) {
return getMarkable().getMarkersOfType(type);
}
public void removeAllMarkers() {
if (getMarkable().getMarkers().isEmpty()) {
return;
}
final boolean oldEnabled = isEnabled();
final boolean oldVisible = isVisible();
// getMarkable().removeAllMarkers();
while (!getMarkable().getMarkers().isEmpty()) {
final IMarker marker = getMarkable().getMarkers().iterator().next();
getMarkable().removeMarker(marker);
clearCachedValues();
notifyMarkersChanged(marker);
}
if (oldEnabled != isEnabled()) {
propertyChangeSupport.firePropertyChange(ITreeNode2.PROPERTY_ENABLED, oldEnabled, isEnabled());
}
if (oldVisible != isVisible()) {
propertyChangeSupport.firePropertyChange(ITreeNode2.PROPERTY_VISIBLE, oldVisible, isVisible());
}
}
/**
* @since 3.0
*/
public boolean removeMarker(final IMarker marker) {
final boolean oldEnabled = isEnabled();
final boolean oldVisible = isVisible();
final boolean removedMarker = getMarkable().removeMarker(marker);
if (!removedMarker) {
return false;
}
clearCachedValues();
if (oldEnabled != isEnabled()) {
propertyChangeSupport.firePropertyChange(ITreeNode2.PROPERTY_ENABLED, oldEnabled, isEnabled());
}
if (oldVisible != isVisible()) {
propertyChangeSupport.firePropertyChange(ITreeNode2.PROPERTY_VISIBLE, oldVisible, isVisible());
}
notifyMarkersChanged(marker);
if ((marker instanceof DisabledMarker) || (marker instanceof HiddenMarker)) {
for (final C child : getChildren()) {
child.removeMarker(marker);
}
}
return true;
}
@SuppressWarnings("unchecked")
private void notifyMarkersChanged(final IMarker marker) {
for (final L next : getListeners()) {
next.markerChanged((S) this, marker);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.markerChanged(this, marker);
}
}
public Object getContext(final String key) {
if (context == null) {
return null;
}
return context.get(key);
}
public void setContext(final String key, final Object value) {
if (context == null) {
context = new HashMap<String, Object>();
}
checkForNavigationArgumentUpdate(key, value);
context.put(key, value);
}
private void checkForNavigationArgumentUpdate(final String key, final Object value) {
if (NavigationArgument.CONTEXTKEY_ARGUMENT.equals(key) && getNavigationNodeController() != null) {
final NavigationArgument oldArgument = (NavigationArgument) getContext(NavigationArgument.CONTEXTKEY_ARGUMENT);
if (oldArgument == null || !oldArgument.equals(value)) {
getNavigationNodeController().navigationArgumentChanged((NavigationArgument) value);
}
}
}
/**
* @since 2.0
*/
public void removeContext(final String key) {
if (context != null) {
context.remove(key);
}
}
public void addAction(final IAction pAction) {
actions.add(pAction);
}
public Set<IAction> getActions() {
return actions;
}
public Set<IAction> getAllActions() {
final Set<IAction> allActions = new HashSet<IAction>();
allActions.addAll(getActions());
if (getParent() != null) {
allActions.addAll(getParent().getAllActions());
}
return allActions;
}
public void removeAction(final IAction pAction) {
actions.remove(pAction);
}
/**
* {@inheritDoc}
* <p>
* Changes the state and notifies listeners.
*
* @param context
* is not used; only <b>this</b> node is prepared
*
* @since 2.0
*/
public void prepare(final INavigationContext context) {
Assert.isTrue(isCreated() || isPrepared());
setState(State.PREPARED);
notifyPrepared();
}
public void activate(final INavigationContext context) {
setState(State.ACTIVATED);
notifyActivated();
}
public void onBeforeActivate(final INavigationContext context) {
notifyBeforeActivated();
}
public void onAfterActivate(final INavigationContext context) {
notifyAfterActivated();
}
public void deactivate(final INavigationContext context) {
setState(State.DEACTIVATED);
notifyDeactivated();
}
public void onBeforeDeactivate(final INavigationContext context) {
notifyBeforeDeactivated();
}
public void onAfterDeactivate(final INavigationContext context) {
notifyAfterDeactivated();
}
private void setState(final State pState) {
if (pState != state) {
final State oldState = state;
state = pState;
notifyStateChanged(oldState, state);
}
}
@SuppressWarnings("unchecked")
private void notifyStateChanged(final State oldState, final State newState) {
for (final L next : getListeners()) {
next.stateChanged((S) this, oldState, newState);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.stateChanged(this, oldState, newState);
}
}
public State getState() {
return state;
}
public boolean isActivated() {
return state == State.ACTIVATED;
}
/**
* @since 2.0
*/
public boolean isPrepared() {
return state == State.PREPARED;
}
public boolean isCreated() {
return state == State.CREATED;
}
public boolean isDeactivated() {
return state == State.DEACTIVATED;
}
public boolean isSelected() {
return selected;
}
public void setSelected(final boolean selected) {
if (selected != this.selected) {
this.selected = selected;
notifySelectedChanged();
}
}
public boolean allowsDispose(final INavigationContext context) {
final INavigationNodeController pres = getNavigationNodeController();
if (pres != null) {
return pres.allowsDispose(this, context);
} else {
return true;
}
}
/**
* @since 3.0
*/
public void dispose() {
getNavigationProcessor().dispose(this);
}
@SuppressWarnings("unchecked")
private void notifyDisposed() {
for (final L next : getListeners()) {
next.disposed((S) this);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.disposed(this);
}
}
@SuppressWarnings("unchecked")
private void notifyBeforeDisposed() {
for (final L next : getListeners()) {
next.beforeDisposed((S) this);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.beforeDisposed(this);
}
}
@SuppressWarnings("unchecked")
private void notifyAfterDisposed() {
for (final L next : getListeners()) {
next.afterDisposed((S) this);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.afterDisposed(this);
}
}
public void dispose(final INavigationContext context) {
setState(State.DISPOSED);
notifyDisposed();
}
public void onBeforeDispose(final INavigationContext context) {
notifyBeforeDisposed();
}
public void onAfterDispose(final INavigationContext context) {
notifyAfterDisposed();
}
public boolean isDisposed() {
return getState() == State.DISPOSED;
}
public boolean isBlocked() {
return blocked;
}
public void setBlocked(final boolean blocked) {
final boolean changed = this.blocked != blocked;
this.blocked = blocked;
if (changed) {
notifyBlockedChanged();
}
for (final INavigationNode<?> child : getChildren()) {
child.setBlocked(blocked);
}
}
/**
* If the node (and no parent node) hasn't a {@link DisabledMarker} the node is enabled; otherwise the node is disabled.
*/
public boolean isEnabled() {
return isEnabled(this);
}
private boolean isEnabled(final NavigationNode<?, ?, ?> node) {
//cachedEnabled = null; // TURN OFF CACHE
if (node.cachedEnabled == null) {
node.cachedEnabled = node.getMarkersOfType(DisabledMarker.class).isEmpty();
}
boolean enabled = node.cachedEnabled;
if (enabled && (node.getParent() != null)) {
enabled = isEnabled((NavigationNode<?, ?, ?>) node.getParent());
}
return enabled;
}
/**
* Adds {@link DisabledMarker} if {@code enabled} is {@code false}. Removes {@link DisabledMarker} if {@code enabled} is {@code true}.
*/
public void setEnabled(final boolean enabled) {
if (disabledMarker == null) {
disabledMarker = new DisabledMarker();
}
if (enabled) {
removeMarker(disabledMarker);
} else {
addMarker(disabledMarker);
}
}
/**
* If the node (and no parent) hasn't a {@link HiddenMarker} the node is visible; otherwise the node is hidden.
*/
public boolean isVisible() {
return isVisible(this);
}
private boolean isVisible(final NavigationNode<?, ?, ?> node) {
//cachedVisible = null; // TURN OFF CACHE
if (node.cachedVisible == null) {
node.cachedVisible = node.getMarkersOfType(HiddenMarker.class).isEmpty();
}
boolean visible = node.cachedVisible;
if (visible && (node.getParent() != null)) {
visible = isVisible((NavigationNode<?, ?, ?>) node.getParent());
}
return visible;
}
/**
* Adds {@link HiddenMarker} if {@code visible} is {@code false}. Removes {@link HiddenMarker} if {@code visible} is {@code true}.
*/
public final void setVisible(final boolean visible) {
if (hiddenMarker == null) {
hiddenMarker = new HiddenMarker();
}
if (visible) {
removeMarker(hiddenMarker);
} else {
addMarker(hiddenMarker);
}
}
@SuppressWarnings("unchecked")
private void notifyBlockedChanged() {
for (final L next : getListeners()) {
next.block((S) this, isBlocked() /* && isActivated() */);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.block(this, isBlocked() /* && isActivated() */);
}
}
public int getIndexOfChild(final INavigationNode<?> child) {
return children.indexOf(child);
}
@SuppressWarnings("unchecked")
public <N extends INavigationNode<?>> N getParentOfType(final Class<N> clazz) {
if (getParent() == null) {
return null;
}
if (clazz.isAssignableFrom(getParent().getClass())) {
return (N) getParent();
} else {
return getParent().getParentOfType(clazz);
}
}
public void create(final NavigationNodeId targetId) {
getNavigationProcessor().create(this, targetId);
}
public void create(final NavigationNodeId targetId, final NavigationArgument argument) {
getNavigationProcessor().create(this, targetId, argument);
}
/**
* @since 3.0
*/
public void createAsync(final NavigationNodeId targetId, NavigationArgument argument) {
if (null == argument) {
argument = new NavigationArgument();
}
argument.setCreateNodesAsync(true);
getNavigationProcessor().create(this, targetId, argument);
}
/**
* @since 3.0
*/
public void createAsync(final NavigationNodeId targetId) {
createAsync(targetId, null);
}
public void moveTo(final NavigationNodeId targetId) {
throw new UnsupportedOperationException("Only ModuleNodes can be moved to a new target."); //$NON-NLS-1$
}
public void navigate(final NavigationNodeId targetId) {
navigate(targetId, null);
}
public void navigate(final NavigationNodeId targetId, final NavigationArgument argument) {
getNavigationProcessor().navigate(this, targetId, argument);
}
public void navigateBack() {
if (getNavigationProcessor() != null) {
getNavigationProcessor().navigateBack(this);
}
}
public void jump(final NavigationNodeId targetId) {
jump(targetId, null);
}
public void jump(final NavigationNodeId targetId, final NavigationArgument argument) {
getNavigationProcessor().jump(this, targetId, argument);
}
public void jumpBack() {
getNavigationProcessor().jumpBack(this);
}
public boolean isJumpTarget() {
return getNavigationProcessor().isJumpTarget(this);
}
public void addJumpTargetListener(final IJumpTargetListener listener) {
getNavigationProcessor().addJumpTargetListener(this, listener);
}
public void removeJumpTargetListener(final IJumpTargetListener listener) {
getNavigationProcessor().removeJumpTargetListener(this, listener);
}
public void historyBack() {
if (getNavigationProcessor() != null) {
getNavigationProcessor().historyBack();
}
}
public void historyForward() {
if (getNavigationProcessor() != null) {
getNavigationProcessor().historyForward();
}
}
public NavigationNodeId getNodeId() {
return nodeId;
}
public void setNodeId(final NavigationNodeId nodeId) {
if (!isNodeIdChange) {
isNodeIdChange = true;
notifyNodeIdChange(nodeId);
isNodeIdChange = false;
}
this.nodeId = nodeId;
}
/**
* Creates the UI filterable.
*
* @return a UI filterable
*/
protected IUIFilterable createFilterable() {
return new UIFilterable();
}
private IUIFilterable getFilterable() {
return filterable;
}
public void addFilter(final IUIFilter filter) {
getFilterable().addFilter(filter);
notifyFilterAdded(filter);
}
public void removeFilter(final IUIFilter filter) {
getFilterable().removeFilter(filter);
notifyFilterRemoved(filter);
}
public void removeFilter(final String filterID) {
final Collection<? extends IUIFilter> filters = getFilters();
final List<IUIFilter> toRemove = new ArrayList<IUIFilter>();
for (final IUIFilter filter : filters) {
if (filter.getFilterID() != null && filter.getFilterID().equals(filterID)) {
toRemove.add(filter);
}
}
for (final IUIFilter filter : toRemove) {
removeFilter(filter);
}
}
public void removeAllFilters() {
final Collection<? extends IUIFilter> filters = new ArrayList<IUIFilter>(getFilters());
getFilterable().removeAllFilters();
for (final IUIFilter filter : filters) {
notifyFilterRemoved(filter);
}
}
public Collection<? extends IUIFilter> getFilters() {
return getFilterable().getFilters();
}
/**
* Notifies every interested listener that the filters have changed.
*/
@SuppressWarnings("unchecked")
private void notifyFilterAdded(final IUIFilter filter) {
for (final L next : getListeners()) {
next.filterAdded((S) this, filter);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.filterAdded(this, filter);
}
}
/**
* Notifies every interested listener that the filters have changed.
*/
@SuppressWarnings("unchecked")
private void notifyFilterRemoved(final IUIFilter filter) {
for (final L next : getListeners()) {
next.filterRemoved((S) this, filter);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.filterRemoved(this, filter);
}
}
@SuppressWarnings("unchecked")
private void notifyNodeIdChange(final NavigationNodeId newId) {
final NavigationNodeId oldId = getNodeId();
for (final L next : getListeners()) {
next.nodeIdChange((S) this, oldId, newId);
}
for (final ISimpleNavigationNodeListener next : getSimpleListeners()) {
next.nodeIdChange(this, oldId, newId);
}
}
public NavigationArgument getNavigationArgument() {
final NavigationArgument navigationArgument = (NavigationArgument) getContext(NavigationArgument.CONTEXTKEY_ARGUMENT);
if (navigationArgument != null) {
return navigationArgument;
}
if (getParent() != null) {
return getParent().getNavigationArgument();
}
return navigationArgument;
}
/**
* {@inheritDoc}
*
* @since 4.0
*/
public String getToolTipText() {
return toolTipText;
}
/**
* {@inheritDoc}
*
* @since 4.0
*/
public void setToolTipText(final String text) {
final String oldText = toolTipText;
toolTipText = text;
propertyChangeSupport.firePropertyChange(INavigationNode.PROPERTY_TOOLTIPTEXT, oldText, toolTipText);
}
}