1 /*
2  * Copyright (c) 2011-2014, Intel Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this
9  * list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation and/or
13  * other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  * may be used to endorse or promote products derived from this software without
17  * specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 #pragma once
31 
32 #include <vector>
33 #include "RemoteCommandHandler.h"
34 
35 template <class CCommandParser>
36 class TRemoteCommandHandlerTemplate : public IRemoteCommandHandler
37 {
38 public:
39     /** Remote command parser execution return status */
40     enum CommandStatus
41     {
42         EDone,      /**< Command succeded, return "Done" */
43         ESucceeded, /**< Command succeeded */
44         EFailed,    /**< Command failed */
45         EShowUsage  /**< Command failed, show usage */
46     };
47 
48     /** Type of the remote command callbacks
49      *
50      * @param[in] remoteCommand contains the arguments of the received command.
51      * @param[out] strResult a string containing the result of the command.
52      *
53      * @return the command execution status, @see CommandStatus
54      */
55     typedef CommandStatus (CCommandParser::*RemoteCommandParser)(
56         const IRemoteCommand &remoteCommand, std::string &strResult);
57 
58 private:
59     // Parser descriptions
60     class CRemoteCommandParserItem
61     {
62     public:
CRemoteCommandParserItem(const std::string & strCommandName,RemoteCommandParser pfnParser,size_t minArgumentCount,const std::string & strHelp,const std::string & strDescription)63         CRemoteCommandParserItem(const std::string &strCommandName, RemoteCommandParser pfnParser,
64                                  size_t minArgumentCount, const std::string &strHelp,
65                                  const std::string &strDescription)
66             : _strCommandName(strCommandName), _pfnParser(pfnParser),
67               _minArgumentCount(minArgumentCount), _strHelp(strHelp),
68               _strDescription(strDescription)
69         {
70         }
71 
getCommandName()72         const std::string &getCommandName() const { return _strCommandName; }
73 
getDescription()74         const std::string &getDescription() const { return _strDescription; }
75 
76         // Usage
usage()77         std::string usage() const { return _strCommandName + " " + _strHelp; }
78 
parse(CCommandParser * pCommandParser,const IRemoteCommand & remoteCommand,std::string & strResult)79         bool parse(CCommandParser *pCommandParser, const IRemoteCommand &remoteCommand,
80                    std::string &strResult) const
81         {
82             // Check enough arguments supplied
83             if (remoteCommand.getArgumentCount() < _minArgumentCount) {
84 
85                 strResult = std::string("Not enough arguments supplied\nUsage:\n") + usage();
86 
87                 return false;
88             }
89 
90             switch ((pCommandParser->*_pfnParser)(remoteCommand, strResult)) {
91             case EDone:
92                 strResult = "Done";
93             // Fall through intentionally
94             case ESucceeded:
95                 return true;
96             case EShowUsage:
97                 strResult = usage();
98             // Fall through intentionally
99             case EFailed:
100                 return false;
101             }
102 
103             return false;
104         }
105 
106     private:
107         std::string _strCommandName;
108         RemoteCommandParser _pfnParser;
109         size_t _minArgumentCount;
110         std::string _strHelp;
111         std::string _strDescription;
112     };
113 
114 public:
TRemoteCommandHandlerTemplate(CCommandParser * pCommandParser)115     TRemoteCommandHandlerTemplate(CCommandParser *pCommandParser)
116         : _pCommandParser(pCommandParser), _maxCommandUsageLength(0)
117     {
118         // Help Command
119         addCommandParser("help", NULL, 0, "", "Show commands description and usage");
120     }
~TRemoteCommandHandlerTemplate()121     ~TRemoteCommandHandlerTemplate()
122     {
123         // FIXME use unique_ptr
124         for (auto *parser : _remoteCommandParserVector) {
125 
126             delete parser;
127         }
128     }
129 
130     // Parsers
addCommandParser(const std::string & strCommandName,RemoteCommandParser pfnParser,size_t minArgumentCount,const std::string & strHelp,const std::string & strDescription)131     bool addCommandParser(const std::string &strCommandName, RemoteCommandParser pfnParser,
132                           size_t minArgumentCount, const std::string &strHelp,
133                           const std::string &strDescription)
134     {
135         if (findCommandParserItem(strCommandName)) {
136 
137             // Already exists
138             return false;
139         }
140 
141         // Add command
142         _remoteCommandParserVector.push_back(new CRemoteCommandParserItem(
143             strCommandName, pfnParser, minArgumentCount, strHelp, strDescription));
144 
145         return true;
146     }
147 
148 private:
149     // Command processing
remoteCommandProcess(const IRemoteCommand & remoteCommand,std::string & strResult)150     bool remoteCommandProcess(const IRemoteCommand &remoteCommand, std::string &strResult)
151     {
152         // Dispatch
153         const CRemoteCommandParserItem *pRemoteCommandParserItem =
154             findCommandParserItem(remoteCommand.getCommand());
155 
156         if (!pRemoteCommandParserItem) {
157 
158             // Not found
159             strResult = "Command not found!\nUse \"help\" to show available commands";
160 
161             return false;
162         }
163 
164         if (remoteCommand.getCommand() == "help") {
165 
166             helpCommandProcess(strResult);
167 
168             return true;
169         }
170 
171         return pRemoteCommandParserItem->parse(_pCommandParser, remoteCommand, strResult);
172     }
173 
174     // Max command usage length, use for formatting
initMaxCommandUsageLength()175     void initMaxCommandUsageLength()
176     {
177         if (!_maxCommandUsageLength) {
178             // Show usages
179             for (const auto *pRemoteCommandParserItem : _remoteCommandParserVector) {
180 
181                 size_t remoteCommandUsageLength = pRemoteCommandParserItem->usage().length();
182 
183                 if (remoteCommandUsageLength > _maxCommandUsageLength) {
184 
185                     _maxCommandUsageLength = remoteCommandUsageLength;
186                 }
187             }
188         }
189     }
190 
191     /////////////////// Remote command parsers
192     /// Help
helpCommandProcess(std::string & strResult)193     void helpCommandProcess(std::string &strResult)
194     {
195         initMaxCommandUsageLength();
196 
197         // Show usages
198         for (const auto *pRemoteCommandParserItem : _remoteCommandParserVector) {
199 
200             std::string strUsage = pRemoteCommandParserItem->usage();
201 
202             // Align
203             size_t spacesToAdd = _maxCommandUsageLength + 5 - strUsage.length();
204 
205             strResult += strUsage + std::string(spacesToAdd, ' ') + "=> " +
206                          pRemoteCommandParserItem->getDescription() + '\n';
207         }
208     }
209 
findCommandParserItem(const std::string & strCommandName)210     const CRemoteCommandParserItem *findCommandParserItem(const std::string &strCommandName) const
211     {
212         for (const auto *pRemoteCommandParserItem : _remoteCommandParserVector) {
213 
214             if (pRemoteCommandParserItem->getCommandName() == strCommandName) {
215 
216                 return pRemoteCommandParserItem;
217             }
218         }
219         return NULL;
220     }
221 
222 private:
223     CCommandParser *_pCommandParser;
224     std::vector<CRemoteCommandParserItem *> _remoteCommandParserVector;
225     size_t _maxCommandUsageLength;
226 };
227