/*******************************************************************************
* 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.internal.navigation.ui.swt.handlers;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.riena.navigation.IApplicationNode;
import org.eclipse.riena.navigation.IModuleGroupNode;
import org.eclipse.riena.navigation.IModuleNode;
import org.eclipse.riena.navigation.INavigationNode;
import org.eclipse.riena.navigation.ISubApplicationNode;
/**
* Abstract handler for navigation related actions.
* <p>
* Extend and implement
* {@link #execute(org.eclipse.core.commands.ExecutionEvent)} to use.
*/
abstract class AbstractNavigationHandler extends AbstractHandler {
/**
* Starting with an {@link IApplicationNode}, find the active
* sub-application and return all {@link IModuleNode}s in it.
*
* @param application
* a non-null IApplicationNode
* @return an array of {@link IModuleNode}s; never null; may be empty.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
protected final IModuleNode[] collectModules(final IApplicationNode application) {
final List<IModuleNode> modules = new ArrayList<IModuleNode>();
final INavigationNode<?> subApplication = findActive((List) application.getChildren());
if (subApplication instanceof ISubApplicationNode) {
final List<IModuleGroupNode> groups = ((ISubApplicationNode) subApplication).getChildren();
for (final IModuleGroupNode moduleGroup : groups) {
modules.addAll(moduleGroup.getChildren());
}
}
return modules.toArray(new IModuleNode[modules.size()]);
}
/**
* Return the first 'active' nodes from the list of nodes.
*
* @param nodes
* a list of INavigationNode elements; never null
*
* @see INavigationNode#isActivated()
*/
protected final INavigationNode<?> findActive(final List<INavigationNode<?>> nodes) {
INavigationNode<?> result = null;
final Iterator<INavigationNode<?>> iter = nodes.iterator();
while (result == null && iter.hasNext()) {
final INavigationNode<?> candidate = iter.next();
if (candidate.isActivated()) {
result = candidate;
}
}
return result;
}
/**
* Not API; public for testing only.
* <p>
* Do a depth-first-search of the navigation tree, starting with the
* IApplicationNode and return the first 'active' IModuleGroupNode; or null
* if none found.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public final IModuleGroupNode findModuleGroup(final IApplicationNode application) {
IModuleGroupNode result = null;
final INavigationNode<?> subApplication = findActive((List) application.getChildren());
if (subApplication != null) {
final INavigationNode<?> moduleGroup = findActive((List) subApplication.getChildren());
if (moduleGroup instanceof IModuleGroupNode) {
result = (IModuleGroupNode) moduleGroup;
}
}
return result;
}
/**
* Not API; public for testing only.
* <p>
* Find the currently 'selected' node and return it's successor. If the
* 'selected' node is the last node of the array, return the first node. If
* no successor can be determined (i.e.e only one node, several selected
* nodes) return null.
* <p>
* The notion of 'selected' depends on the implementation of the
* {@link #isSelected(INavigationNode)} method.
*/
public final INavigationNode<?> findNextNode(final INavigationNode<?>[] nodes) {
INavigationNode<?> result = null;
int selectedCount = 0;
for (final INavigationNode<?> node : nodes) {
if (isSelected(node)) {
selectedCount++;
} else {
if (selectedCount == 1 && result == null) { // previous else implies !selected
result = node;
}
}
}
if (selectedCount == 1 && result == null && !isSelected(nodes[0])) { // wrap around
result = nodes[0];
}
return selectedCount == 1 ? result : null;
}
/**
* Not API; public for testing only.
* <p>
* Find the currently 'selected' node and return it's predecessor. When
* wrapping is on and the 'selected' node is the first node of the array,
* return the last node. If no predecessir can be determined (i.e. only one
* node, several selected nodes) return null.
* <p>
* The notion of 'selected' depends on the implementation of the
* {@link #isSelected(INavigationNode)} method.
*/
public final INavigationNode<?> findPreviousNode(final INavigationNode<?>[] nodes, final boolean wrap) {
INavigationNode<?> result = null;
int selectedCount = 0;
for (int i = nodes.length - 1; i >= 0; i--) {
final INavigationNode<?> node = nodes[i];
if (isSelected(node)) {
selectedCount++;
} else {
if (selectedCount == 1 && result == null) { // previous else implies !selected
result = node;
}
}
}
final int lastApp = nodes.length - 1;
if (wrap && selectedCount == 1 && result == null && !isSelected(nodes[lastApp])) {
result = nodes[lastApp];
}
return selectedCount == 1 ? result : null;
}
// helping methods
//////////////////
/**
* How to determine if the given <tt>node</tt> is selected. Used by
* {@link #findNextNode(INavigationNode[])} and
* {@link #findPreviousNode(INavigationNode[])}.
* <p>
* This implementation will check if node is selected AND it's parent is
* selected.
* <p>
* Subclasses may override.
*/
protected boolean isSelected(final INavigationNode<?> node) {
return node.isSelected() && node.getParent().isSelected();
}
}