/* * JBoss, Home of Professional Open Source. * Copyright 2012, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.gatein.api.navigation; import java.util.Locale; import java.util.Map; import org.exoplatform.portal.mop.Described; import org.exoplatform.portal.mop.Described.State; import org.exoplatform.portal.mop.SiteKey; import org.exoplatform.portal.mop.description.DescriptionService; import org.exoplatform.portal.mop.navigation.NavigationContext; import org.exoplatform.portal.mop.navigation.NavigationService; import org.exoplatform.portal.mop.navigation.NavigationState; import org.exoplatform.portal.mop.navigation.NodeChangeListener; import org.exoplatform.portal.mop.navigation.NodeContext; import org.exoplatform.portal.mop.navigation.Scope; import org.exoplatform.services.resources.ResourceBundleManager; import org.gatein.api.ApiException; import org.gatein.api.EntityNotFoundException; import org.gatein.api.PortalRequest; import org.gatein.api.Util; import org.gatein.api.internal.Parameters; import org.gatein.api.site.Site; import org.gatein.api.site.SiteId; /** * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:nscavell@redhat.com">Nick Scavelli</a> */ public class NavigationImpl implements Navigation { private final NavigationService navigationService; private final NavigationContext navCtx; private final DescriptionService descriptionService; private final ResourceBundleManager bundleManager; private final SiteId siteId; private final ApiNodeModel model; private Navigation18NResolver i18nResolver; public NavigationImpl(SiteId siteId, NavigationService navigationService, NavigationContext navCtx, DescriptionService descriptionService, ResourceBundleManager bundleManager) { this.siteId = siteId; this.navigationService = navigationService; this.navCtx = navCtx; this.descriptionService = descriptionService; this.bundleManager = bundleManager; this.model = new ApiNodeModel(this); } // Used for unit testing NavigationImpl(SiteId siteId) { this.siteId = siteId; this.navigationService = null; this.navCtx = null; this.descriptionService = null; this.bundleManager = null; this.model = null; } @Override public boolean removeNode(NodePath path) { Parameters.requireNonNull(path, "path"); Node parent = getNode(path.parent(), Nodes.visitChildren()); if (parent == null || !parent.removeChild(path.getLastSegment())) { return false; } saveNode(parent); return true; } @Override public Node getNode(String... nodePath) { return getNode(NodePath.path(nodePath)); } @Override public Node getNode(NodePath nodePath) { return getNode(nodePath, Nodes.visitNone()); } @Override public Node getNode(NodePath nodePath, NodeVisitor visitor) { Parameters.requireNonNull(nodePath, "nodePath"); Parameters.requireNonNull(visitor, "visitor"); NodeContext<ApiNode> ctx = getNodeContext(nodePath, visitor); return (ctx == null) ? null : ctx.getNode(); } @Override public int getPriority() { return navCtx.getState().getPriority(); } @Override public SiteId getSiteId() { return siteId; } @Override public Node getRootNode(NodeVisitor visitor) { NodeContext<ApiNode> ctx = loadNodeContext(visitor); return (ctx == null) ? null : ctx.getNode(); } @Override public void refreshNode(Node node) { refreshNode(node, Nodes.visitNone()); } @Override public void refreshNode(Node node, NodeVisitor visitor) { Parameters.requireNonNull(node, "node"); Parameters.requireNonNull(visitor, "visitor"); NodeContext<ApiNode> ctx = ((ApiNode) node).getContext(); rebaseNodeContext(ctx, new NodeVisitorScope(visitor), null); Node r = node; while (!r.isRoot()) r = r.getParent(); clearCached(r); } private void clearCached(Node node) { ((ApiNode) node).clearCached(); if (node.isChildrenLoaded()) { for (Node c : node) { clearCached(c); } } } @Override public void saveNode(Node node) { Parameters.requireNonNull(node, "node"); NodeContext<ApiNode> ctx = ((ApiNode) node).getContext(); saveNodeContext(ctx, null); saveDisplayNames(ctx); } @Override public void setPriority(int priority) { navCtx.setState(new NavigationState(priority)); save(navCtx); } Map<Locale, Described.State> loadDescriptions(String id) { try { return descriptionService.getDescriptions(id); } catch (Throwable t) { throw new ApiException("Failed to retrieve descriptions", t); } } String resolve(NodeContext<ApiNode> ctx) { if (i18nResolver == null) { PortalRequest request = PortalRequest.getInstance(); Site site; if (request.getSiteId().equals(siteId)) { site = request.getSite(); } else { // look it up site = request.getPortal().getSite(siteId); } if (site == null) { throw new ApiException("Could not resolve display name because site " + siteId + " could not be found."); } i18nResolver = new Navigation18NResolver(descriptionService, bundleManager, site.getLocale(), siteId); } return i18nResolver.resolveName(ctx.getState().getLabel(), ctx.getId(), ctx.getName()); } NodeContext<ApiNode> getNodeContext(NodePath nodePath, NodeVisitor visitor) { NodeContext<ApiNode> ctx = loadNodeContext(Nodes.visitNodes(nodePath, visitor)); for (String name : nodePath) { ctx = ctx.get(name); if (ctx == null) return null; } return ctx; } private NodeContext<ApiNode> loadNodeContext(NodeVisitor visitor) { return loadNodeContext(new NodeVisitorScope(visitor), null); } private NodeContext<ApiNode> loadNodeContext(Scope scope, NodeChangeListener<NodeContext<ApiNode>> listener) { try { return navigationService.loadNode(model, navCtx, scope, listener); } catch (Throwable e) { throw new ApiException("Failed to load node", e); } } void rebaseNodeContext(NodeContext<ApiNode> ctx, Scope scope, NodeChangeListener<NodeContext<ApiNode>> listener) { try { navigationService.rebaseNode(ctx, scope, listener); } catch (Throwable e) { throw new ApiException("Failed to refresh node", e); } } private void saveNodeContext(NodeContext<ApiNode> ctx, NodeChangeListener<NodeContext<ApiNode>> listener) { try { navigationService.saveNode(ctx, listener); } catch (Throwable e) { throw new ApiException("Failed to save node", e); } } private void save(NavigationContext ctx) { try { navigationService.saveNavigation(ctx); } catch (Throwable e) { throw new ApiException("Failed to save navigation", e); } } private void saveDisplayNames(NodeContext<ApiNode> ctx) { ApiNode node = ctx.getNode(); if (node != null && node.isDisplayNameChanged()) { if (!node.getDisplayNames().isLocalized()) { Map<Locale, Described.State> descriptions = loadDescriptions(ctx.getId()); if (descriptions != null) { setDescriptions(ctx.getId(), null); } } else { Map<Locale, State> descriptions = ObjectFactory.createDescriptions(node.getDisplayNames()); setDescriptions(ctx.getId(), descriptions); } } for (NodeContext<ApiNode> c = ctx.getFirst(); c != null; c = c.getNext()) { saveDisplayNames(c); } } private void setDescriptions(String id, Map<Locale, Described.State> descriptions) { try { descriptionService.setDescriptions(id, descriptions); } catch (Throwable t) { throw new ApiException("Failed to set descriptions", t); } } }