001/* 002 * jPOS Project [http://jpos.org] 003 * Copyright (C) 2000-2026 jPOS Software SRL 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jpos.q2; 020 021import java.io.IOException; 022import java.util.ArrayList; 023import java.util.List; 024import java.util.regex.Matcher; 025import java.util.regex.Pattern; 026 027import org.jline.terminal.Terminal; 028import org.jpos.iso.ISOUtil; 029 030/** Dispatches CLI command lines to the appropriate {@link CLICommand} implementation. */ 031public class CLICommandInterface { 032 CLIContext ctx; 033 List<String> prefixes = new ArrayList<String>(); 034 035 /** 036 * Returns the list of registered command prefixes. 037 * @return command prefix list 038 */ 039 public List<String> getPrefixes() { 040 return prefixes; 041 } 042 043 /** 044 * Creates a CLICommandInterface for the given context. 045 * @param ctx the CLI context 046 */ 047 public CLICommandInterface(CLIContext ctx) { 048 this.ctx = ctx; 049 } 050 051 /** 052 * Registers a command prefix. 053 * @param prefix the prefix to add 054 */ 055 public void addPrefix(String prefix) { 056 prefixes.add(prefix); 057 } 058 059 /** 060 * Executes the given command line. 061 * @param line the full command line 062 * @throws IOException on I/O failure 063 */ 064 public void execCommand(String line) throws IOException { 065 String args[] = parseCommand(line); 066 if (args.length == 0) { 067 return; 068 } 069 String verbatimCmd = args[0]; 070 String command = args[0].toUpperCase(); 071 String className = command; 072 073 for (String prefix : prefixes) { 074 if (!command.contains(".")) { 075 className = prefix + command; 076 } 077 try { 078 Object cmd = getCommand(className); 079 if (cmd != null) { 080 try { 081 args[0] = ISOUtil.unPadLeft(line, ' '); // full line 082 if (cmd instanceof CLISubSystem) { 083 CLISubSystem ss = (CLISubSystem) cmd; 084 ctx.getCLI().setPrompt(ss.getPrompt(ctx, args), ss.getCompletionPrefixes(ctx, args)); 085 } 086 if (cmd instanceof CLICommand) { 087 ((CLICommand) cmd).exec(ctx, args); 088 } else if (cmd instanceof Command) { 089 Terminal t = ctx.getReader().getTerminal(); 090 ((Command) cmd).exec (t.input(), t.output(), t.output(), args); 091 } 092 return; 093 } catch (Exception ex) { 094 ctx.printThrowable(ex); 095 } 096 } 097 } catch (ClassNotFoundException ignored) { 098 // NOPMD 099 } catch (Exception ex) { 100 ctx.printThrowable(ex); 101 break; 102 } 103 } 104 ctx.println("Invalid command '" + verbatimCmd + "'"); 105 } 106 107 private Object getCommand(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException { 108 final ClassLoader cl = Thread.currentThread().getContextClassLoader(); 109 return cl.loadClass(className).newInstance(); 110 } 111 112 /** 113 * Parses a command line into tokens. 114 * @param line the full command line 115 * @return tokens: [prefix, command, args...] 116 * @throws IOException on I/O failure 117 */ 118 public String[] parseCommand(String line) throws IOException { 119 if (line == null) { 120 return new String[0]; 121 } 122 123 List<String> matchList = new ArrayList<String>(); 124 Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'"); 125 Matcher regexMatcher = regex.matcher(line); 126 while (regexMatcher.find()) { 127 if (regexMatcher.group(1) != null) { 128 // Add double-quoted string without the quotes 129 matchList.add(regexMatcher.group(1)); 130 } else if (regexMatcher.group(2) != null) { 131 // Add single-quoted string without the quotes 132 matchList.add(regexMatcher.group(2)); 133 } else { 134 // Add unquoted word 135 matchList.add(regexMatcher.group()); 136 } 137 } 138 String[] args = new String[matchList.size()]; 139 matchList.toArray(args); 140 return args; 141 } 142}