/* * Copyright (C) 2015 Red Hat, Inc. and/or its affiliates. * * 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 org.jboss.errai.ui.nav.client.local.spi; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import org.jboss.errai.common.client.util.CreationalCallback; import org.jboss.errai.ioc.client.container.IOC; import org.jboss.errai.ioc.client.container.async.AsyncBeanManager; import org.jboss.errai.ui.nav.client.local.PageRole; import org.jboss.errai.ui.nav.client.local.TransitionTo; import org.jboss.errai.ui.nav.client.local.UniquePageRole; import org.jboss.errai.ui.nav.client.local.api.MissingPageRoleException; import org.jboss.errai.ui.nav.client.local.api.PageNotFoundException; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import com.google.gwt.user.client.ui.IsWidget; /** * The NavigationGraph is responsible for creating or retrieving instances of Page and PageTransition objects. It is * also the central repository for structural information about the interpage navigation in the app (this information is * defined in a decentralized way, by classes that implement {@link PageNode} and contain injected {@link TransitionTo} * fields. * <p> * The concrete implementation of this class is usually generated at compile-time by scanning for page classes. It is * expected to fill in the {@link #pagesByName} map in its constructor. * * @author Jonathan Fuerth <jfuerth@gmail.com> */ public abstract class NavigationGraph { protected final AsyncBeanManager bm = IOC.getAsyncBeanManager(); /** * Maps page names to the classes that implement them. The subclass's constructor is responsible for populating this * map. */ protected final Map<String, PageNode<?>> pagesByName = new HashMap<String, PageNode<?>>(); protected final Multimap<Class<? extends PageRole>, PageNode<?>> pagesByRole = ArrayListMultimap .create(); /** * Returns an instance of the given page type. If the page is an ApplicationScoped bean, the singleton instance of the * page will be returned; otherwise (for Dependent-scoped beans) a new instance will be returned. * * @param name * The page name, as defined by the implementation of page. * @return The appropriate instance of the page. */ public <C> PageNode<C> getPage(String name) { @SuppressWarnings("unchecked") PageNode<C> page = (PageNode<C>) pagesByName.get(name); if (page == null) { throw new PageNotFoundException("Page not found: \"" + name + "\""); } return page; } /** * Returns an instance of the given page type. If the page is an ApplicationScoped bean, the singleton instance of the * page will be returned; otherwise (for Dependent-scoped beans) a new instance will be returned. * * @param type * The Class object for the bean that implements the page. * @return The appropriate instance of the page. */ public <C> PageNode<C> getPage(Class<C> type) { // TODO this could be made more efficient if we had a pagesByWidgetType map for (Entry<String, PageNode<?>> e : pagesByName.entrySet()) { if (e.getValue().contentType().equals(type)) { @SuppressWarnings({ "unchecked" }) PageNode<C> page = (PageNode<C>) e.getValue(); return page; } } throw new PageNotFoundException("No page with a widget type of " + type.getName() + " exists"); } /** * Returns all pages that have the specified role. In the add page annotation one can specify multiple roles for a * page. {@link #getPage(Class)} {@link PageRole} * * @param role * the role used to lookup the pages * @return all pages that have the role set. */ public Collection<PageNode<?>> getPagesByRole(Class<? extends PageRole> role) { return pagesByRole.get(role); } public PageNode getPageByRole(Class<? extends UniquePageRole> role) { final Collection<PageNode<?>> pageNodes = pagesByRole.get(role); if (pageNodes.size() == 1) { return pageNodes.iterator().next(); } else if (pageNodes.size() < 1) { throw new MissingPageRoleException(role); } else { throw new IllegalStateException("Role '" + role + "' is not unique multiple pages: " + pageNodes + " found"); } } /** * Returns true if and only if there are no pages in this nagivation graph. */ public boolean isEmpty() { return pagesByName.isEmpty(); } protected static final class PageNodeCreationalCallback<W extends IsWidget> implements CreationalCallback<PageNode<W>> { @Override public void callback(PageNode<W> beanInstance) { } } /** * @return Returns a collection of all {@link PageNode PageNodes} in the navigation graph. */ public Collection<PageNode<?>> getAllPages() { Collection<PageNode<?>> values = pagesByName.values(); return Collections.unmodifiableCollection(new HashSet<PageNode<?>>(values)); } }