/* * Copyright (c) 2009-present the original author or authors. * * 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 com.planet57.gshell.help; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; import java.util.function.Predicate; import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import com.google.common.annotations.VisibleForTesting; import com.google.inject.Key; import com.planet57.gshell.alias.AliasRegistry; import com.planet57.gshell.command.resolver.CommandResolver; import com.planet57.gshell.command.resolver.Node; import com.planet57.gshell.event.EventManager; import com.planet57.gshell.internal.BeanContainer; import org.eclipse.sisu.BeanEntry; import org.eclipse.sisu.Mediator; import org.sonatype.goodies.lifecycle.LifecycleSupport; import static com.google.common.base.Preconditions.checkNotNull; /** * Default {@link HelpPageManager}. * * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> * @since 2.5 */ @Named @Singleton public class HelpPageManagerImpl extends LifecycleSupport implements HelpPageManager { private final BeanContainer container; private final EventManager events; private final AliasRegistry aliases; private final CommandResolver resolver; private final HelpContentLoader loader; private final Map<String, MetaHelpPage> metaPages = new LinkedHashMap<>(); private boolean discoveryEnabled = true; @Inject public HelpPageManagerImpl(final BeanContainer container, final EventManager events, final AliasRegistry aliases, final CommandResolver resolver, final HelpContentLoader loader) { this.container = checkNotNull(container); this.events = checkNotNull(events); this.aliases = checkNotNull(aliases); this.resolver = checkNotNull(resolver); this.loader = checkNotNull(loader); } /** * Exposed to allow help-page discovery to be disabled for testing. */ @VisibleForTesting public void setDiscoveryEnabled(final boolean discoveryEnabled) { log.debug("Discovery enabled: {}", discoveryEnabled); this.discoveryEnabled = discoveryEnabled; } @Override protected void doStart() throws Exception { if (discoveryEnabled) { log.debug("Watching for meta-help-pages"); container.watch(Key.get(MetaHelpPage.class), new MetaHelpPageMediator(), this); } } private static class MetaHelpPageMediator implements Mediator<Named, MetaHelpPage, HelpPageManagerImpl> { @Override public void add(final BeanEntry<Named, MetaHelpPage> entry, final HelpPageManagerImpl watcher) throws Exception { watcher.addMetaPageInternal(entry.getValue());; } @Override public void remove(final BeanEntry<Named, MetaHelpPage> entry, final HelpPageManagerImpl watcher) throws Exception { watcher.addMetaPage(entry.getValue()); } } @Override public HelpPage getPage(final String name) { checkNotNull(name); ensureStarted(); if (aliases.containsAlias(name)) { try { return new AliasHelpPage(name, aliases.getAlias(name)); } catch (AliasRegistry.NoSuchAliasException e) { throw new Error(e); } } Node node = resolver.resolve(name); if (node != null) { return HelpPageUtil.pageFor(node, loader); } if (metaPages.containsKey(name)) { return metaPages.get(name); } return null; } @Override public Collection<HelpPage> getPages(final Predicate<HelpPage> query) { checkNotNull(query); ensureStarted(); return getPages().stream().filter(query).collect(Collectors.toList()); } @Override public Collection<HelpPage> getPages() { ensureStarted(); Map<String, HelpPage> pages = new TreeMap<>(); // Add aliases aliases.getAliases().forEach((key, value) -> pages.put(key, new AliasHelpPage(key, value))); // Add commands for (Node parent : resolver.searchPath()) { if (parent.isGroup()) { for (Node child : parent.children()) { if (!pages.containsKey(child.getName())) { pages.put(child.getName(), HelpPageUtil.pageFor(child, loader)); } } } else if (!pages.containsKey(parent.getName())) { pages.put(parent.getName(), HelpPageUtil.pageFor(parent, loader)); } } // Add meta-pages getMetaPages().stream() .filter(page -> !pages.containsKey(page.getName())) .forEach(page -> pages.put(page.getName(), page)); return pages.values(); } @Override public void addMetaPage(final MetaHelpPage page) { checkNotNull(page); ensureStarted(); addMetaPageInternal(page); } @Override public void removeMetaPage(final MetaHelpPage page) { checkNotNull(page); ensureStarted(); log.debug("Removing meta-page: {}", page); metaPages.remove(page.getName()); events.publish(new MetaHelpPageRemovedEvent(page)); } /** * Exposed for discovery; which is before lifecycle has been started. */ private void addMetaPageInternal(final MetaHelpPage page) { log.debug("Adding meta-page: {}", page); metaPages.put(page.getName(), page); events.publish(new MetaHelpPageAddedEvent(page)); } @Override public Collection<MetaHelpPage> getMetaPages() { ensureStarted(); return metaPages.values(); } }