/*******************************************************************************
* 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;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.riena.navigation.model.NavigationModelFailure;
/**
* A NodePositioner object is responsible to add a NavigationNode as a child to another NavigationNode at a specified index. NodePositioner is for example used
* by the SimpleNavigationNodeProvider to add a subtree to a parent node. You can add a NodePositioner to a NavigationArgument. Then, whenever new nodes are
* created the NodePositioner will handle the addition of the new subtree to the root node. Usually it should not be necessary to subclass NodePositioner.
*/
public abstract class NodePositioner {
public final static String POSITIONING_ORDINALITY_KEY = NodePositioner.class.getName() + "positioning-ordinality-key"; //$NON-NLS-1$
protected Map<String, Object> context = new HashMap<String, Object>();
enum Mode {
FIXED, ORDINAL
}
/**
* Adds a node to another at index 0.
*/
public final static NodePositioner ADD_BEGINNING = new NodePositioner() {
private final NodePositioner delegate = indexed(0);
@Override
public void addChildToParent(final INavigationNode parent, final INavigationNode child) {
delegate.addChildToParent(parent, child);
}
};
/**
* Adds a node to another as the last child.
*/
public final static NodePositioner ADD_END = new NodePositioner() {
@Override
public void addChildToParent(final INavigationNode parent, final INavigationNode child) {
assertMode(parent, Mode.FIXED);
parent.addChild(child);
}
};
/**
* Adds a node to another as a child at the specified index.
*/
public final static NodePositioner indexed(final int index) {
return new NodePositioner() {
@Override
public void addChildToParent(final INavigationNode parent, final INavigationNode child) {
assertMode(parent, Mode.FIXED);
if (index < 0) {
throw new NavigationModelFailure("Cannot add child " + child + " to parent " + parent //$NON-NLS-1$//$NON-NLS-2$
+ " at index " + index + ". Index must be >= 0"); //$NON-NLS-1$//$NON-NLS-2$
}
// Fallback for out of bound index
if (index >= parent.getChildren().size()) {
ADD_END.addChildToParent(parent, child);
return;
}
parent.addChild(index, child);
}
};
}
/**
* Adds a node to another as a child with the specified ordinality(relative index). Note that you may not add different children with fixed index and
* ordinality to the same parent node.
*/
public final static NodePositioner ordinal(final int ordinality) {
return new NodePositioner() {
@Override
public void addChildToParent(final INavigationNode parent, final INavigationNode child) {
assertMode(parent, Mode.ORDINAL);
child.setContext(POSITIONING_ORDINALITY_KEY, ordinality);
if (parent.getChildren().size() == 0) {
parent.addChild(child);
return;
}
final List<INavigationNode<?>> oldChildren = parent.getChildren();
int ix = 0;
for (final INavigationNode<?> n : oldChildren) {
final Integer curOrdinality = (Integer) n.getContext(POSITIONING_ORDINALITY_KEY);
if (curOrdinality == null) {
throw new IllegalArgumentException("The node '" + n.getNodeId() + "' has no ordinality index. " //$NON-NLS-1$ //$NON-NLS-2$
+ "You can not combine ordinal with fixed children for the same parent."); //$NON-NLS-1$
}
if (ordinality < curOrdinality) {
break;
}
ix++;
}
if (ix == oldChildren.size()) {
parent.addChild(child);
} else {
parent.addChild(ix, child);
}
}
};
}
private static void assertMode(final INavigationNode node, final Mode expected) {
if (node.getChildren().size() == 0) {
return;
}
final INavigationNode firstChild = (INavigationNode) node.getChildren().get(0);
final Mode currentMode = firstChild.getContext(POSITIONING_ORDINALITY_KEY) != null ? Mode.ORDINAL : Mode.FIXED;
if (!currentMode.equals(expected)) {
throw new NavigationModelFailure("Node " + node + " cannot be added with NodePositioningMode " + expected //$NON-NLS-1$ //$NON-NLS-2$
+ " as the current mode is " + currentMode); //$NON-NLS-1$
}
}
/**
* Adds the child node to the parent node.
*
* @param parent
* the parent node
* @param child
* the child node
*/
public abstract void addChildToParent(INavigationNode parent, INavigationNode child);
}