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
35template <class CCommandParser>
36class TRemoteCommandHandlerTemplate : public IRemoteCommandHandler
37{
38public:
39    /** Remote command parser execution return status */
40    enum CommandStatus {
41        EDone, /**< Command succeded, return "Done" */
42        ESucceeded, /**< Command succeeded */
43        EFailed, /**< Command failed */
44        EShowUsage /**< Command failed, show usage */
45    };
46
47    /** Type of the remote command callbacks
48     *
49     * @param[in] remoteCommand contains the arguments of the received command.
50     * @param[out] strResult a string containing the result of the command.
51     *
52     * @return the command execution status, @see CommandStatus
53     */
54    typedef CommandStatus (CCommandParser::*RemoteCommandParser)(const IRemoteCommand& remoteCommand, std::string& strResult);
55
56private:
57    // Parser descriptions
58    class CRemoteCommandParserItem
59    {
60    public:
61        CRemoteCommandParserItem(const std::string& strCommandName,
62                                 RemoteCommandParser pfnParser,
63                                 uint32_t uiMinArgumentCount,
64                                 const std::string& strHelp,
65                                 const std::string& strDescription)
66            : _strCommandName(strCommandName),
67              _pfnParser(pfnParser),
68              _uiMinArgumentCount(uiMinArgumentCount),
69              _strHelp(strHelp),
70              _strDescription(strDescription) {}
71
72        const std::string& getCommandName() const
73        {
74            return _strCommandName;
75        }
76
77        const std::string& getDescription() const
78        {
79            return _strDescription;
80        }
81
82        // Usage
83        std::string usage() const
84        {
85            return _strCommandName + " " + _strHelp;
86        }
87
88        bool parse(CCommandParser* pCommandParser, const IRemoteCommand& remoteCommand, std::string& strResult) const
89        {
90            // Check enough arguments supplied
91            if (remoteCommand.getArgumentCount() < _uiMinArgumentCount) {
92
93                strResult = std::string("Not enough arguments supplied\nUsage:\n") + usage();
94
95                return false;
96            }
97
98            switch ((pCommandParser->*_pfnParser)(remoteCommand, strResult)) {
99            case EDone:
100                strResult = "Done";
101                // Fall through intentionally
102            case ESucceeded:
103                return true;
104            case EShowUsage:
105                strResult = usage();
106                // Fall through intentionally
107            case EFailed:
108                return false;
109            }
110
111            return false;
112        }
113
114    private:
115        std::string _strCommandName;
116        RemoteCommandParser _pfnParser;
117        uint32_t _uiMinArgumentCount;
118        std::string _strHelp;
119        std::string _strDescription;
120    };
121
122public:
123    TRemoteCommandHandlerTemplate(CCommandParser* pCommandParser) : _pCommandParser(pCommandParser), _uiMaxCommandUsageLength(0)
124    {
125        // Help Command
126        addCommandParser("help", NULL, 0, "", "Show commands description and usage");
127    }
128    ~TRemoteCommandHandlerTemplate()
129    {
130        uint32_t uiIndex;
131
132        for (uiIndex = 0; uiIndex < _remoteCommandParserVector.size(); uiIndex++) {
133
134            delete _remoteCommandParserVector[uiIndex];
135        }
136    }
137
138    // Parsers
139    bool addCommandParser(const std::string& strCommandName,
140        RemoteCommandParser pfnParser,
141        uint32_t uiMinArgumentCount,
142        const std::string& strHelp,
143        const std::string& strDescription)
144    {
145        if (findCommandParserItem(strCommandName)) {
146
147            // Already exists
148            return false;
149        }
150
151        // Add command
152        _remoteCommandParserVector.push_back(new CRemoteCommandParserItem(strCommandName, pfnParser, uiMinArgumentCount, strHelp, strDescription));
153
154        return true;
155    }
156
157private:
158    // Command processing
159    bool remoteCommandProcess(const IRemoteCommand& remoteCommand, std::string& strResult)
160    {
161        // Dispatch
162        const CRemoteCommandParserItem* pRemoteCommandParserItem = findCommandParserItem(remoteCommand.getCommand());
163
164        if (!pRemoteCommandParserItem) {
165
166            // Not found
167            strResult = "Command not found!\nUse \"help\" to show available commands";
168
169            return false;
170        }
171
172        if (remoteCommand.getCommand() == "help") {
173
174            helpCommandProcess(strResult);
175
176            return true;
177        }
178
179        return pRemoteCommandParserItem->parse(_pCommandParser, remoteCommand, strResult);
180    }
181
182    // Max command usage length, use for formatting
183    void initMaxCommandUsageLength()
184    {
185        if (!_uiMaxCommandUsageLength) {
186            // Show usages
187            uint32_t uiIndex;
188
189            for (uiIndex = 0; uiIndex < _remoteCommandParserVector.size(); uiIndex++) {
190
191                const CRemoteCommandParserItem* pRemoteCommandParserItem = _remoteCommandParserVector[uiIndex];
192
193                uint32_t uiRemoteCommandUsageLength = (uint32_t)pRemoteCommandParserItem->usage().length();
194
195                if (uiRemoteCommandUsageLength > _uiMaxCommandUsageLength) {
196
197                    _uiMaxCommandUsageLength = uiRemoteCommandUsageLength;
198                }
199            }
200        }
201    }
202
203    /////////////////// Remote command parsers
204    /// Help
205    void helpCommandProcess(std::string& strResult)
206    {
207        initMaxCommandUsageLength();
208
209        strResult = "\n";
210
211        // Show usages
212        uint32_t uiIndex;
213
214        for (uiIndex = 0; uiIndex < _remoteCommandParserVector.size(); uiIndex++) {
215
216            const CRemoteCommandParserItem* pRemoteCommandParserItem = _remoteCommandParserVector[uiIndex];
217
218            std::string strUsage = pRemoteCommandParserItem->usage();
219
220            strResult += strUsage;
221
222            // Align
223            uint32_t uiToSpacesAdd = _uiMaxCommandUsageLength + 5 - (uint32_t)strUsage.length();
224
225            while (uiToSpacesAdd--) {
226
227                strResult += " ";
228            }
229
230            strResult += std::string("=> ") + std::string(pRemoteCommandParserItem->getDescription()) + "\n";
231        }
232    }
233
234    const CRemoteCommandParserItem* findCommandParserItem(const std::string& strCommandName) const
235    {
236        uint32_t uiIndex;
237
238        for (uiIndex = 0; uiIndex < _remoteCommandParserVector.size(); uiIndex++) {
239
240            const CRemoteCommandParserItem* pRemoteCommandParserItem = _remoteCommandParserVector[uiIndex];
241
242            if (pRemoteCommandParserItem->getCommandName() == strCommandName) {
243
244                return pRemoteCommandParserItem;
245            }
246        }
247        return NULL;
248    }
249
250private:
251    CCommandParser* _pCommandParser;
252    std::vector<CRemoteCommandParserItem*> _remoteCommandParserVector;
253    uint32_t _uiMaxCommandUsageLength;
254};
255
256