2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved.
6 * The contents of this file are subject to the terms of either the GNU Lesser
7 * General Public License Version 2.1 only ("LGPL") or the Common Development and
8 * Distribution License ("CDDL")(collectively, the "License"). You may not use this
9 * file except in compliance with the License. You can obtain a copy of the CDDL at
10 * http://www.opensource.org/licenses/cddl1.php and a copy of the LGPLv2.1 at
11 * http://www.opensource.org/licenses/lgpl-license.php. See the License for the
12 * specific language governing permissions and limitations under the License. When
13 * distributing the software, include this License Header Notice in each file and
14 * include the full text of the License in the License file as well as the
17 * NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
19 * For Covered Software in this distribution, this License shall be governed by the
20 * laws of the State of California (excluding conflict-of-law provisions).
21 * Any litigation relating to this License shall be subject to the jurisdiction of
22 * the Federal Courts of the Northern District of California and the state courts
23 * of the State of California, with venue lying in Santa Clara County, California.
27 * If you wish your version of this file to be governed by only the CDDL or only
28 * the LGPL Version 2.1, indicate your decision by adding "[Contributor]" elects to
29 * include this software in this distribution under the [CDDL or LGPL Version 2.1]
30 * license." If you don't indicate a single choice of license, a recipient has the
31 * option to distribute your version of this file under either the CDDL or the LGPL
32 * Version 2.1, or to extend the choice of license to its licensees as provided
33 * above. However, if you add LGPL Version 2.1 code and therefore, elected the LGPL
34 * Version 2 license, then the option applies only if the new code is made subject
35 * to such option by the copyright holder.
42 #include "portability.h"
43 #include "imi_plugin.h"
45 CIMIPlugin::CIMIPlugin(TPluginTypeEnum pluginType)
46 : m_pluginType(pluginType)
49 CIMIPlugin::~CIMIPlugin()
52 class CIMIPythonPlugin : public CIMIPlugin
55 CIMIPythonPlugin(std::string filename);
56 virtual ~CIMIPythonPlugin();
58 virtual std::string getName() { return m_name; }
59 virtual std::string getAuthor() { return m_author; }
60 virtual std::string getDescription() { return m_description; }
62 virtual TPluginCandidates provide_candidates(const TPluginPreedit& str,
64 virtual TPluginCandidate translate_candidate(const TPluginCandidate& candi,
68 PyObject* m_provide_method;
69 PyObject* m_trans_method;
73 std::string m_description;
76 CIMIPythonPlugin::CIMIPythonPlugin(std::string filename)
77 : CIMIPlugin(CIMI_PLUGIN_PYTHON), m_module(NULL), m_provide_method(NULL),
80 // filename always ends with .py
81 std::string module_name = filename.substr(0, filename.length() - 3);
82 CIMIPluginManager& manager = AIMIPluginManager::instance();
83 PyObject* dict = NULL;
84 PyObject* name = NULL;
85 PyObject* author = NULL;
86 PyObject* description = NULL;
88 m_module = PyImport_ImportModule(module_name.c_str());
89 if (m_module == NULL) {
92 dict = PyModule_GetDict(m_module);
96 m_provide_method = PyDict_GetItemString(dict, "provide_candidates");
97 m_trans_method = PyDict_GetItemString(dict, "translate_candidate");
98 name = PyDict_GetItemString(dict, "__NAME__");
99 author = PyDict_GetItemString(dict, "__AUTHOR__");
100 description = PyDict_GetItemString(dict, "__DESCRIPTION__");
102 if (name != NULL && PyString_Check(name)) {
103 m_name = PyString_AsString(name);
105 if (author != NULL && PyString_Check(author)) {
106 m_author = PyString_AsString(author);
108 if (description != NULL && PyString_Check(description)) {
109 m_description = PyString_AsString(description);
113 manager.setLastError("Error when loading Python module");
117 CIMIPythonPlugin::~CIMIPythonPlugin()
119 Py_XDECREF(m_module);
123 Py_Call1(PyObject* method, PyObject* obj)
125 PyObject* args = PyTuple_Pack(1, obj);
126 PyObject* ret = PyObject_CallObject(method, args);
134 static const size_t TWCharBufferSize = 2048;
137 PyUnicode_AsWString(PyObject* obj)
139 TWCHAR* wide_str_buf = new TWCHAR[TWCharBufferSize];
141 memset(wide_str_buf, 0, sizeof(TWCHAR) * TWCharBufferSize);
143 Py_ssize_t size = PyUnicode_AsWideChar((PyUnicodeObject*) obj,
144 (wchar_t*) wide_str_buf,
147 res = wstring(wide_str_buf);
149 delete [] wide_str_buf;
154 ExtractSequence(TPluginCandidates& result, PyObject* py_seq)
156 Py_ssize_t len = PySequence_Length(py_seq);
157 for (Py_ssize_t i = 0; i < len; i++) {
158 PyObject* tuple_item_obj = PySequence_GetItem(py_seq, i);
159 if (!PyTuple_Check(tuple_item_obj)) {
162 PyObject* rank_obj = PyTuple_GetItem(tuple_item_obj, 0);
163 PyObject* candi_obj = PyTuple_GetItem(tuple_item_obj, 1);
164 if (rank_obj == NULL || !PyInt_Check(rank_obj) || candi_obj == NULL
165 || !PyUnicode_Check(candi_obj)) {
169 result.push_back(TPluginCandidateItem((int) PyInt_AsLong(rank_obj),
170 PyUnicode_AsWString(candi_obj)));
175 CIMIPythonPlugin::provide_candidates(const TPluginPreedit& str,
178 TPluginCandidates res;
181 if (m_provide_method == NULL) {
186 PyObject* str_obj = PyUnicode_FromWideChar((wchar_t*) str.c_str(),
188 PyObject* ret_obj = Py_Call1(m_provide_method, str_obj);
190 if (ret_obj == NULL) {
192 } else if (PyInt_Check(ret_obj)) {
193 *waitTime = (int) PyInt_AsLong(ret_obj);
194 } else if (PyTuple_Check(ret_obj) && PyTuple_Size(ret_obj) == 2) {
195 PyObject* time_obj = PyTuple_GetItem(ret_obj, 0);
196 PyObject* seq_obj = PyTuple_GetItem(ret_obj, 1);
197 if (PyInt_Check(time_obj) && PyList_Check(seq_obj)) {
198 *waitTime = (int) PyInt_AsLong(time_obj);
199 ExtractSequence(res, seq_obj);
201 } else if (PyList_Check(ret_obj)) {
202 // extract all items inside this sequence.
203 ExtractSequence(res, ret_obj);
211 CIMIPythonPlugin::translate_candidate(const TPluginCandidate& candi,
214 TPluginCandidate res;
217 if (m_trans_method == NULL) {
222 PyObject* str_obj = PyUnicode_FromWideChar((wchar_t*) candi.c_str(),
224 PyObject* ret_obj = Py_Call1(m_trans_method, str_obj);
225 if (ret_obj == NULL) {
227 } else if (PyInt_Check(ret_obj)) {
228 *waitTime = (int) PyInt_AsLong(ret_obj);
229 } else if (PyUnicode_Check(ret_obj)) {
230 res = TPluginCandidate(PyUnicode_AsWString(ret_obj));
240 if (Py_IsInitialized())
242 std::stringstream eval_str;
244 // append plugin module path to default load path
246 signal(SIGINT, SIG_DFL);
248 PyRun_SimpleString("import sys");
249 eval_str << "sys.path.append(r'" << getenv("HOME")
250 << "/.sunpinyin/plugins/" << "')";
251 PyRun_SimpleString(eval_str.str().c_str());
254 #define PLUGIN_LIST_FILE "/.sunpinyin/plugins.list";
255 #define PLUGIN_NAME_LEN 128
257 CIMIPluginManager::CIMIPluginManager()
258 : m_hasError(false), m_waitTime(0)
263 CIMIPluginManager::~CIMIPluginManager()
265 for (size_t i = 0; i < m_plugins.size(); i++) {
271 CIMIPluginManager::initializePlugins()
273 // load configuration file which list all needed plugins
274 std::string plugin_list_path = getenv("HOME");
275 plugin_list_path += PLUGIN_LIST_FILE;
276 FILE* fp = fopen(plugin_list_path.c_str(), "r");
281 char plugin_name[PLUGIN_NAME_LEN];
282 memset(plugin_name, 0, PLUGIN_NAME_LEN);
283 fgets(plugin_name, PLUGIN_NAME_LEN, fp);
284 if (strlen(plugin_name) == 0) {
287 if (strlen(plugin_name) == 1) {
290 plugin_name[strlen(plugin_name) - 1] = 0; // remove the \n at the end
291 if (loadPlugin(plugin_name) == NULL) {
292 fprintf(stderr, "Error! Cannot load plugin %s\n", plugin_name);
299 CIMIPluginManager::detectPluginType(std::string filename)
301 if (filename.length() >= 3
302 && filename.substr(filename.length() - 3) == ".py") {
303 return CIMI_PLUGIN_PYTHON;
305 return CIMI_PLUGIN_UNKNOWN;
310 CIMIPluginManager::loadPlugin(std::string filename)
312 TPluginTypeEnum type = detectPluginType(filename);
313 CIMIPlugin* plugin = createPlugin(filename, type);
314 std::stringstream error;
316 if (plugin == NULL) {
319 if (hasLastError()) {
324 for (size_t i = 0; i < m_plugins.size(); i++) {
325 if (m_plugins[i]->getName() == plugin->getName()) {
326 error << "Plugin " << plugin->getName() << " has already loaded!";
327 setLastError(error.str());
328 delete plugin; // Reject duplicate plugins
332 m_plugins.push_back(plugin);
337 CIMIPluginManager::createPlugin(std::string filename,
338 TPluginTypeEnum pluginType)
340 std::stringstream error;
343 switch (pluginType) {
344 case CIMI_PLUGIN_PYTHON:
345 return new CIMIPythonPlugin(filename);
346 case CIMI_PLUGIN_UNKNOWN:
348 error << "Cannot detect type for " << filename;
349 setLastError(error.str());
355 CIMIPluginManager::setLastError(std::string desc)
362 CIMIPluginManager::clearLastError()
369 CIMIPluginManager::markWaitTime(int waitTime)
374 if (m_waitTime == 0) {
375 m_waitTime = waitTime;
376 } else if (waitTime < m_waitTime) {
377 m_waitTime = waitTime;