package act.cli; /*- * #%L * ACT Framework * %% * Copyright (C) 2014 - 2017 ActFramework * %% * 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. * #L% */ import act.Act; import act.app.App; import act.app.AppServiceBase; import act.cli.builtin.Exit; import act.cli.builtin.Help; import act.cli.builtin.IterateCursor; import act.cli.meta.CommandMethodMetaInfo; import act.cli.meta.CommanderClassMetaInfo; import act.handler.CliHandler; import act.handler.builtin.cli.CliHandlerProxy; import org.osgl.Osgl; import org.osgl.logging.LogManager; import org.osgl.logging.Logger; import org.osgl.util.C; import org.osgl.util.E; import org.osgl.util.S; import java.util.List; import java.util.Map; /** * Dispatch console command to CLI command handler */ public class CliDispatcher extends AppServiceBase<CliDispatcher> { private static Logger logger = LogManager.get(CliDispatcher.class); private static final String NAME_PART_SEPARATOR = "[\\.\\-_]+"; private Map<String, CliHandler> registry = C.newMap(); private Map<String, String> shortCuts = C.newMap(); private Map<String, List<CliHandler>> ambiguousShortCuts = C.newMap(); private Map<CliHandler, List<String>> nameMap = C.newMap(); private Map<CliHandler, List<String>> shortCutMap = C.newMap(); public CliDispatcher(App app) { super(app); registerBuiltInHandlers(); } public CliDispatcher registerCommandHandler(String command, CommandMethodMetaInfo methodMetaInfo, CommanderClassMetaInfo classMetaInfo) { String sa[] = command.split(CommanderClassMetaInfo.NAME_SEPARATOR); for (String s : sa) { if (registry.containsKey(s)) { throw E.invalidConfiguration("Command %s already registered", command); } addToRegistry(s, new CliHandlerProxy(classMetaInfo, methodMetaInfo, app())); logger.debug("Command registered: %s", s); } return this; } public boolean registered(String command) { return registry.containsKey(command); } public CliHandler handler(String command) { String command0 = command; command = shortCuts.get(command); if (null == command) { command = command0; } CliHandler handler = registry.get(command); if (null == handler && !command.startsWith("act.")) { handler = registry.get("act." + command); } Act.Mode mode = Act.mode(); if (null != handler && handler.appliedIn(mode)) { return handler; } return null; } /** * Returns a list of system commands in alphabetic order * * @return the system command list */ public List<String> systemCommands() { return commands(true, false); } /** * Returns a list of application commands in alphabetic order * * @return the application command list */ public List<String> applicationCommands() { return commands(false, true); } /** * Returns all commands in alphabetic order * * @return the list of commands */ public List<String> commands(boolean sys, boolean app) { C.List<String> list = C.newList(); Act.Mode mode = Act.mode(); boolean all = !sys && !app; for (String s : registry.keySet()) { boolean isSysCmd = s.startsWith("act."); if (isSysCmd && !sys && !all) { continue; } if (!isSysCmd && !app && !all) { continue; } CliHandler h = registry.get(s); if (h.appliedIn(mode)) { list.add(s); } } return list.sorted(new Osgl.Comparator<String>() { @Override public int compare(String o1, String o2) { boolean b1 = (o1.startsWith("act.")); boolean b2 = (o2.startsWith("act.")); if (b1 & !b2) { return -1; } if (!b1 & b2) { return 1; } return o1.compareTo(o2); } }); } public List<String> names(CliHandler handler) { return nameMap.get(handler); } public List<String> shortCuts(CliHandler handler) { return shortCutMap.get(handler); } @Override protected void releaseResources() { registry.clear(); } private void addToRegistry(String name, CliHandler handler) { registry.put(name, handler); Help.updateMaxWidth(name.length()); updateNameIndex(name, handler); registerShortCut(name, handler); } private void updateNameIndex(String name, CliHandler handler) { List<String> nameList = nameMap.get(handler); if (null == nameList) { nameList = C.newList(); nameMap.put(handler, nameList); } nameList.add(name); } private void registerShortCut(String name, CliHandler handler) { List<String> shortCutNames = shortCutMap.get(handler); if (null == shortCutNames) { shortCutNames = C.newList(); shortCutMap.put(handler, shortCutNames); } for (int i = 0; i < 5; ++i) { String shortCut = shortCut(name, i); if (null == shortCut) { continue; } if (ambiguousShortCuts.containsKey(shortCut)) { List<CliHandler> list = ambiguousShortCuts.get(shortCut); list.add(handler); } else if (shortCuts.containsKey(shortCut)) { shortCuts.remove(shortCut); List<CliHandler> list = C.newList(); ambiguousShortCuts.put(shortCut, list); for (List<String> ls : shortCutMap.values()) { ls.remove(shortCut); } } else { shortCuts.put(shortCut, name); shortCutNames.add(shortCut); } } } /** * level: * * 0 - "foo.bar.zee" -> ".fbz" * 1 - "foo.bar.zee" -> "f.b.z" * 2 - "foo.bar.zee" -> "f.b.zee" * 3 - "foo.bar.zee" -> "f.bar.z" * 4 - "foo.bar.zee" -> "fo.ba.ze" */ private static String shortCut(String name, int level) { String sa[] = name.split(NAME_PART_SEPARATOR); if (sa.length < 2) { return null; } S.Buffer sb = S.buffer(); switch (level) { case 0: sb.append("."); for (String s : sa) { sb.append(s.charAt(0)); } return sb.toString(); case 1: for (String s : sa) { sb.append(s.charAt(0)).append("."); } sb.deleteCharAt(sb.length() - 1); return sb.toString(); case 2: for (int i = 0; i < sa.length - 1; ++i) { sb.append(sa[i].charAt(0)).append("."); } sb.append(sa[sa.length - 1]); return sb.toString(); case 3: for (int i = 0; i < sa.length - 2; ++i) { sb.append(sa[i].charAt(0)).append("."); } sb.append(sa[sa.length - 2]).append("."); sb.append(sa[sa.length - 1].charAt(0)); return sb.toString(); case 4: for (String s : sa) { sb.append(s.charAt(0)); if (s.length() > 1) { sb.append(s.charAt(1)); } sb.append("."); } sb.deleteCharAt(sb.length() - 1); return sb.toString(); default: throw E.unsupport(); } } private void registerBuiltInHandlers() { addToRegistry("act.exit", Exit.INSTANCE); addToRegistry("act.quit", Exit.INSTANCE); addToRegistry("act.bye", Exit.INSTANCE); addToRegistry("act.help", Help.INSTANCE); addToRegistry("act.it", IterateCursor.INSTANCE); } }