/******************************************************************************* * 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.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.riena.core.util.VariableManagerUtil; import org.eclipse.riena.navigation.AbstractNavigationAssembler; import org.eclipse.riena.navigation.ApplicationModelFailure; import org.eclipse.riena.navigation.IAssemblerProvider; import org.eclipse.riena.navigation.IGenericNavigationAssembler; import org.eclipse.riena.navigation.IModuleGroupNode; import org.eclipse.riena.navigation.IModuleNode; import org.eclipse.riena.navigation.INavigationNode; import org.eclipse.riena.navigation.ISubApplicationNode; import org.eclipse.riena.navigation.ISubModuleNode; import org.eclipse.riena.navigation.NavigationArgument; import org.eclipse.riena.navigation.NavigationNodeId; import org.eclipse.riena.navigation.extension.IModuleGroupNode2Extension; import org.eclipse.riena.navigation.extension.IModuleNode2Extension; import org.eclipse.riena.navigation.extension.INode2Extension; import org.eclipse.riena.navigation.extension.ISubApplicationNode2Extension; import org.eclipse.riena.navigation.extension.ISubModuleNode2Extension; import org.eclipse.riena.navigation.extension.Node2Extension; /** * This assembler builds navigation nodes according to the definition stored in * extensions. */ public class GenericNavigationAssembler extends AbstractNavigationAssembler implements IGenericNavigationAssembler { /** dynamic variable referencing navigation node id */ public static final String VAR_NAVIGATION_NODEID = "riena.navigation.nodeid"; //$NON-NLS-1$ /** dynamic variable referencing navigation node id */ public static final String VAR_NAVIGATION_NODECONTEXT = "riena.navigation.nodecontext"; //$NON-NLS-1$ /** dynamic variable referencing navigation parameter */ public static final String VAR_NAVIGATION_PARAMETER = "riena.navigation.parameter"; //$NON-NLS-1$ /** * @since 2.0 */ protected Set<String> acceptedTargetIds = null; // the IAssemblyProvider that can be used to resolve assembly references private IAssemblerProvider assemblerProvider; /** * @since 2.0 */ protected final void initializeAcceptedTargetIds() { if (getAssembly() != null) { // resolve sub-application if it exists final ISubApplicationNode2Extension[] subApplications = getAssembly().getSubApplications(); for (final ISubApplicationNode2Extension subApplication : subApplications) { resolveTargetIds(subApplication); } // resolve module group if it exists final IModuleGroupNode2Extension[] groups = getAssembly().getModuleGroups(); for (final IModuleGroupNode2Extension group : groups) { resolveTargetIds(group); } // otherwise try module final IModuleNode2Extension[] modules = getAssembly().getModules(); for (final IModuleNode2Extension module : modules) { resolveTargetIds(module); } // last resort is sub-module final ISubModuleNode2Extension[] subModules = getAssembly().getSubModules(); for (final ISubModuleNode2Extension subModule : subModules) { resolveTargetIds(subModule); } } } private void resolveTargetIds(final ISubApplicationNode2Extension subapplicationDefinition) { updateAcceptedTargetIds(subapplicationDefinition.getNodeId()); if (subapplicationDefinition.getChildNodes() != null) { for (final IModuleGroupNode2Extension groupDefinition : subapplicationDefinition.getChildNodes()) { resolveTargetIds(groupDefinition); } } } private void resolveTargetIds(final IModuleGroupNode2Extension groupDefinition) { updateAcceptedTargetIds(groupDefinition.getNodeId()); if (groupDefinition.getChildNodes() != null) { for (final IModuleNode2Extension moduleDefinition : groupDefinition.getChildNodes()) { resolveTargetIds(moduleDefinition); } } } private void resolveTargetIds(final IModuleNode2Extension moduleDefinition) { updateAcceptedTargetIds(moduleDefinition.getNodeId()); if (moduleDefinition.getChildNodes() != null) { for (final ISubModuleNode2Extension submoduleDefinition : moduleDefinition.getChildNodes()) { resolveTargetIds(submoduleDefinition); } } } private void resolveTargetIds(final ISubModuleNode2Extension submoduleDefinition) { updateAcceptedTargetIds(submoduleDefinition.getNodeId()); if (submoduleDefinition.getChildNodes() != null) { for (final ISubModuleNode2Extension nestedDefinition : submoduleDefinition.getChildNodes()) { resolveTargetIds(nestedDefinition); } } } /** * {@inheritDoc} * * @since 2.0 */ public INavigationNode<?>[] buildNode(final NavigationNodeId targetId, final NavigationArgument navigationArgument) { if (getAssembly() != null) { final Map<String, Object> context = new HashMap<String, Object>(); // build module group if it exists final ISubApplicationNode2Extension[] subApplications = getAssembly().getSubApplications(); if ((subApplications != null) && (subApplications.length > 0)) { final INavigationNode<?>[] nodes = new INavigationNode<?>[subApplications.length]; for (int i = 0; i < subApplications.length; i++) { nodes[i] = build(subApplications[i], targetId, navigationArgument, context); } return nodes; } // build module group if it exists final IModuleGroupNode2Extension[] groups = getAssembly().getModuleGroups(); if ((groups != null) && (groups.length > 0)) { final INavigationNode<?>[] nodes = new INavigationNode<?>[groups.length]; for (int i = 0; i < groups.length; i++) { nodes[i] = build(groups[i], targetId, navigationArgument, context); } return nodes; } // otherwise try module final IModuleNode2Extension[] modules = getAssembly().getModules(); if ((modules != null) && (modules.length > 0)) { final INavigationNode<?>[] nodes = new INavigationNode<?>[modules.length]; for (int i = 0; i < modules.length; i++) { nodes[i] = build(modules[i], targetId, navigationArgument, context); } return nodes; } // last resort is submodule final ISubModuleNode2Extension[] subModules = getAssembly().getSubModules(); if ((subModules != null) && (subModules.length > 0)) { final INavigationNode<?>[] nodes = new INavigationNode<?>[subModules.length]; for (int i = 0; i < subModules.length; i++) { nodes[i] = build(subModules[i], targetId, navigationArgument, context); } return nodes; } } throw new ExtensionPointFailure( "'subapplication', 'modulegroup', 'module' or 'submodule' element expected. ID=" + targetId.getTypeId()); //$NON-NLS-1$ } /** * Builds the node of a sub-application and all its children. * * @param subApplicationDefinition * definition of a sub-application * @param targetId * The ID of the node to create. * @param navigationArgument * Optional argument passed on from the navigate(..) method. May * be null. * @param context * @return created sub-application node * @since 2.0 */ protected ISubApplicationNode build(final ISubApplicationNode2Extension subApplicationDefinition, final NavigationNodeId targetId, final NavigationArgument navigationArgument, final Map<String, Object> context) { Assert.isNotNull(subApplicationDefinition, "Error building sub-application. Sub�-application cannot be null"); //$NON-NLS-1$ Assert.isNotNull(subApplicationDefinition.getPerspectiveId(), "Error building sub-application. Attribute 'perspectiveId' cannot be null for sub-application = " //$NON-NLS-1$ + subApplicationDefinition.getPerspectiveId()); // a module group can only contain modules final ISubApplicationNode subapplication = new SubApplicationNode(createNavigationNodeIdFromTemplate(targetId, subApplicationDefinition, navigationArgument), subApplicationDefinition.getName()); subapplication.setIcon(subApplicationDefinition.getIcon()); updateContext(subapplication, navigationArgument); if (subApplicationDefinition.getChildNodes() != null) { for (final IModuleGroupNode2Extension child : subApplicationDefinition.getChildNodes()) { subapplication.addChild(build(child, targetId, navigationArgument, copy(context))); } } return subapplication; } /** * Builds the node of a module group and all its children. * * @param groupDefinition * definition of a module group * @param targetId * The ID of the node to create. * @param navigationArgument * Optional argument passed on from the navigate(..) method. May * be null. * @param context * @return created module group node * @since 2.0 */ protected IModuleGroupNode build(final IModuleGroupNode2Extension groupDefinition, final NavigationNodeId targetId, final NavigationArgument navigationArgument, final Map<String, Object> context) { // a module group can only contain modules final IModuleGroupNode moduleGroup = new ModuleGroupNode(createNavigationNodeIdFromTemplate(targetId, groupDefinition, navigationArgument)); moduleGroup.setLabel(groupDefinition.getName()); moduleGroup.setIcon(groupDefinition.getIcon()); updateContext(moduleGroup, navigationArgument); if (groupDefinition.getChildNodes() != null) { for (final IModuleNode2Extension child : groupDefinition.getChildNodes()) { moduleGroup.addChild(build(child, targetId, navigationArgument, copy(context))); } } return moduleGroup; } /** * Builds the node of a module and all its children. * * @param groupDefinition * definition of a module * @param targetId * The ID of the node to create. * @param navigationArgument * Optional argument passed on from the navigate(..) method. May * be null. * @param context * @return created module node * @since 2.0 */ protected IModuleNode build(final IModuleNode2Extension moduleDefinition, final NavigationNodeId targetId, final NavigationArgument navigationArgument, final Map<String, Object> context) { IModuleNode module = null; final Map<String, Object> mapping = createMapping(targetId, navigationArgument); try { startVariableResolver(mapping); // create module node with label (and icon) String label = moduleDefinition.getName(); if (moduleDefinition instanceof Node2Extension) { // it's only necessary for converted assembly nodes // (not converted nodes are proxies) label = resolveVariables(label); } module = new ModuleNode(createNavigationNodeIdFromTemplate(targetId, moduleDefinition, navigationArgument), label); module.setIcon(moduleDefinition.getIcon()); module.setClosable(moduleDefinition.isClosable()); updateContext(module, navigationArgument); if (moduleDefinition.getChildNodes() != null) { for (final ISubModuleNode2Extension child : moduleDefinition.getChildNodes()) { module.addChild(build(child, targetId, navigationArgument, copy(context))); } } } finally { cleanupVariableResolver(); } return module; } /** * Builds the node of a sub-module and all its children. * * @param subModuleDefinition * definition of a sub-module * @param targetId * The ID of the node to create. * @param navigationArgument * Optional argument passed on from the navigate(..) method. May * be null. * @param context * @return created sub-module node * @since 2.0 */ protected ISubModuleNode build(final ISubModuleNode2Extension subModuleDefinition, final NavigationNodeId targetId, final NavigationArgument navigationArgument, final Map<String, Object> context) { ISubModuleNode submodule = null; final Map<String, Object> mapping = createMapping(targetId, navigationArgument); mapping.put(VAR_NAVIGATION_NODECONTEXT, context); try { startVariableResolver(mapping); String label = subModuleDefinition.getName(); if (subModuleDefinition instanceof Node2Extension) { // it's only necessary for converted assembly nodes // (not converted nodes are proxies) label = resolveVariables(label); } // create submodule node with label (and icon) submodule = new SubModuleNode(createNavigationNodeIdFromTemplate(targetId, subModuleDefinition, navigationArgument), label); submodule.setIcon(subModuleDefinition.getIcon()); submodule.setVisible(subModuleDefinition.isVisible()); submodule.setExpanded(subModuleDefinition.isExpanded()); submodule.setClosable(subModuleDefinition.isClosable()); submodule.setSelectable(subModuleDefinition.isSelectable()); updateContext(submodule, navigationArgument); if (subModuleDefinition.getChildNodes() != null) { for (final ISubModuleNode2Extension child : subModuleDefinition.getChildNodes()) { submodule.addChild(build(child, targetId, navigationArgument, copy(context))); } } } finally { cleanupVariableResolver(); } return submodule; } /** * Creates an ID of a navigation node with the given informations. * * @param template * @param nodeExtension * definition of node * @param navigationArgument * Optional argument passed on from the navigate(..) method. May * be null. (<i>not used in this implementation.</i>) * @return create ID of a navigation node * @since 2.0 */ protected NavigationNodeId createNavigationNodeIdFromTemplate(final NavigationNodeId template, final INode2Extension nodeExtension, final NavigationArgument navigationArgument) { final String typeId = nodeExtension.getNodeId(); final String instanceId = template.getInstanceId(); return new NavigationNodeId(typeId, instanceId); } /** * {@inheritDoc} */ public void setAssemblerProvider(final IAssemblerProvider assemblyProvider) { this.assemblerProvider = assemblyProvider; } /** * Returns an assembly provider that may be used by the resolve assembly * references. * * @param assemblyProvider */ public IAssemblerProvider getAssemblerProvider() { return assemblerProvider; } /** * @see org.eclipse.riena.navigation.INavigationAssembler#acceptsTargetId(java * .lang.String) */ public boolean acceptsToBuildNode(final NavigationNodeId nodeId, final NavigationArgument argument) { return getAcceptedTargetIds().contains(nodeId.getTypeId()); } /** * @see org.eclipse.riena.navigation.INavigationAssembler#getAcceptedNodeIds() */ public Collection<String> getAcceptedTargetIds() { if (acceptedTargetIds == null) { acceptedTargetIds = new HashSet<String>(); initializeAcceptedTargetIds(); acceptedTargetIds = Collections.unmodifiableSet(acceptedTargetIds); } return new HashSet<String>(acceptedTargetIds); } /** * @since 2.0 */ protected void updateAcceptedTargetIds(final String typeId) { if (typeId != null) { acceptedTargetIds.add(typeId); } } protected Map<String, Object> copy(final Map<String, Object> context) { return new HashMap<String, Object>(context); } protected void updateContext(final INavigationNode<?> node, final NavigationArgument navigationArgument) { // this logic is now in GenericNavigationAssembler and available for Generic assemblers and handwritten ones.... // if (navigationArgument != null) { // node.setContext(NavigationArgument.CONTEXT_KEY_PARAMETER, navigationArgument.getParameter()); // } } protected Map<String, Object> createMapping(final NavigationNodeId targetId, final NavigationArgument navigationArgument) { final Map<String, Object> mapping = new HashMap<String, Object>(); mapping.put(VAR_NAVIGATION_NODEID, targetId); if (navigationArgument != null) { mapping.put(VAR_NAVIGATION_PARAMETER, navigationArgument.getParameter()); } return mapping; } protected void startVariableResolver(final Map<String, Object> mapping) { ThreadLocalMapResolver.configure(mapping); } protected void cleanupVariableResolver() { ThreadLocalMapResolver.cleanup(); } protected String resolveVariables(final String string) { try { return VariableManagerUtil.substitute(string); } catch (final CoreException e) { throw new ApplicationModelFailure("Resolving variables in '" + string + "' failed", e); //$NON-NLS-1$ //$NON-NLS-2$ } } }