~ubuntu-branches/ubuntu/raring/sunpinyin/raring

« back to all changes in this revision

Viewing changes to src/ime-core/imi_plugin.cpp

  • Committer: Package Import Robot
  • Author(s): YunQiang Su
  • Date: 2012-03-30 15:31:55 UTC
  • mfrom: (1.1.3) (1.2.7 sid)
  • Revision ID: package-import@ubuntu.com-20120330153155-qgls77sogzgtg9zp
Tags: 2.0.3+git20120222-1
* Team upload: git snapshot 20120222.
   - fix breaks if LDFLAGS in environment contains
       multiple words (Closese #646001).
   - rm patches merged to upstream:
       append-os-environ-toenv.patch
       fix-ftbfs-on-sh.patch
       remove-10-candidate-words-limitation.patch
   - refresh disable-lm-dict-compile.patch.
* Bump stardard version to 3.9.3: no modify needed.
* add libsunpinyin3-dbg and python-sunpinyin packages.
* debian/compat to 9, multiarch it.
* rewrite debian/rules with dh 7 format.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved.
 
5
 *
 
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
 
15
 * following notice:
 
16
 *
 
17
 * NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
 
18
 * (CDDL)
 
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.
 
24
 *
 
25
 * Contributor(s):
 
26
 *
 
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.
 
36
 */
 
37
 
 
38
#include <Python.h>
 
39
#include <signal.h>
 
40
#include <sstream>
 
41
 
 
42
#include "portability.h"
 
43
#include "imi_plugin.h"
 
44
 
 
45
CIMIPlugin::CIMIPlugin(TPluginTypeEnum pluginType)
 
46
    : m_pluginType(pluginType)
 
47
{}
 
48
 
 
49
CIMIPlugin::~CIMIPlugin()
 
50
{}
 
51
 
 
52
class CIMIPythonPlugin : public CIMIPlugin
 
53
{
 
54
public:
 
55
    CIMIPythonPlugin(std::string filename);
 
56
    virtual ~CIMIPythonPlugin();
 
57
 
 
58
    virtual std::string getName() { return m_name; }
 
59
    virtual std::string getAuthor() { return m_author; }
 
60
    virtual std::string getDescription() { return m_description; }
 
61
 
 
62
    virtual TPluginCandidates provide_candidates(const TPluginPreedit& str,
 
63
                                                 int* waitTime);
 
64
    virtual TPluginCandidate  translate_candidate(const TPluginCandidate& candi,
 
65
                                                  int* waitTime);
 
66
private:
 
67
    PyObject* m_module;
 
68
    PyObject* m_provide_method;
 
69
    PyObject* m_trans_method;
 
70
 
 
71
    std::string m_name;
 
72
    std::string m_author;
 
73
    std::string m_description;
 
74
};
 
75
 
 
76
CIMIPythonPlugin::CIMIPythonPlugin(std::string filename)
 
77
    : CIMIPlugin(CIMI_PLUGIN_PYTHON), m_module(NULL), m_provide_method(NULL),
 
78
      m_trans_method(NULL)
 
79
{
 
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;
 
87
 
 
88
    m_module = PyImport_ImportModule(module_name.c_str());
 
89
    if (m_module == NULL) {
 
90
        goto error;
 
91
    }
 
92
    dict = PyModule_GetDict(m_module);
 
93
    if (dict == NULL) {
 
94
        goto error;
 
95
    }
 
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__");
 
101
 
 
102
    if (name != NULL && PyString_Check(name)) {
 
103
        m_name = PyString_AsString(name);
 
104
    }
 
105
    if (author != NULL && PyString_Check(author)) {
 
106
        m_author = PyString_AsString(author);
 
107
    }
 
108
    if (description != NULL && PyString_Check(description)) {
 
109
        m_description = PyString_AsString(description);
 
110
    }
 
111
    return;
 
112
error:
 
113
    manager.setLastError("Error when loading Python module");
 
114
    return;
 
115
}
 
116
 
 
117
CIMIPythonPlugin::~CIMIPythonPlugin()
 
118
{
 
119
    Py_XDECREF(m_module);
 
120
}
 
121
 
 
122
static PyObject*
 
123
Py_Call1(PyObject* method, PyObject* obj)
 
124
{
 
125
    PyObject* args = PyTuple_Pack(1, obj);
 
126
    PyObject* ret = PyObject_CallObject(method, args);
 
127
    Py_XDECREF(args);
 
128
    if (ret == NULL) {
 
129
        PyErr_PrintEx(2);
 
130
    }
 
131
    return ret;
 
132
}
 
133
 
 
134
static const size_t TWCharBufferSize = 2048;
 
135
 
 
136
static wstring
 
137
PyUnicode_AsWString(PyObject* obj)
 
138
{
 
139
    TWCHAR* wide_str_buf = new TWCHAR[TWCharBufferSize];
 
140
    wstring res;
 
141
    memset(wide_str_buf, 0, sizeof(TWCHAR) * TWCharBufferSize);
 
142
 
 
143
    Py_ssize_t size = PyUnicode_AsWideChar((PyUnicodeObject*) obj,
 
144
                                           (wchar_t*) wide_str_buf,
 
145
                                           TWCharBufferSize);
 
146
    if (size > 0) {
 
147
        res = wstring(wide_str_buf);
 
148
    }
 
149
    delete [] wide_str_buf;
 
150
    return res;
 
151
}
 
152
 
 
153
static void
 
154
ExtractSequence(TPluginCandidates& result, PyObject* py_seq)
 
155
{
 
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)) {
 
160
            continue;
 
161
        }
 
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)) {
 
166
            continue;
 
167
        }
 
168
 
 
169
        result.push_back(TPluginCandidateItem((int) PyInt_AsLong(rank_obj),
 
170
                                              PyUnicode_AsWString(candi_obj)));
 
171
    }
 
172
}
 
173
 
 
174
TPluginCandidates
 
175
CIMIPythonPlugin::provide_candidates(const TPluginPreedit& str,
 
176
                                     int* waitTime)
 
177
{
 
178
    TPluginCandidates res;
 
179
    *waitTime = 0;
 
180
 
 
181
    if (m_provide_method == NULL) {
 
182
        *waitTime = -1;
 
183
        return res;
 
184
    }
 
185
 
 
186
    PyObject* str_obj = PyUnicode_FromWideChar((wchar_t*) str.c_str(),
 
187
                                               str.size());
 
188
    PyObject* ret_obj = Py_Call1(m_provide_method, str_obj);
 
189
 
 
190
    if (ret_obj == NULL) {
 
191
        *waitTime = -1;
 
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);
 
200
        }
 
201
    } else if (PyList_Check(ret_obj)) {
 
202
        // extract all items inside this sequence.
 
203
        ExtractSequence(res, ret_obj);
 
204
    }
 
205
    Py_XDECREF(str_obj);
 
206
    Py_XDECREF(ret_obj);
 
207
    return res;
 
208
}
 
209
 
 
210
TPluginCandidate
 
211
CIMIPythonPlugin::translate_candidate(const TPluginCandidate& candi,
 
212
                                      int* waitTime)
 
213
{
 
214
    TPluginCandidate res;
 
215
    *waitTime = 0;
 
216
 
 
217
    if (m_trans_method == NULL) {
 
218
        *waitTime = -1;
 
219
        return res;
 
220
    }
 
221
 
 
222
    PyObject* str_obj = PyUnicode_FromWideChar((wchar_t*) candi.c_str(),
 
223
                                               candi.size());
 
224
    PyObject* ret_obj = Py_Call1(m_trans_method, str_obj);
 
225
    if (ret_obj == NULL) {
 
226
        *waitTime = -1;
 
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));
 
231
    }
 
232
    Py_XDECREF(str_obj);
 
233
    Py_XDECREF(ret_obj);
 
234
    return res;
 
235
}
 
236
 
 
237
static void
 
238
InitializePython()
 
239
{
 
240
    if (Py_IsInitialized())
 
241
        return;
 
242
    std::stringstream eval_str;
 
243
 
 
244
    // append plugin module path to default load path
 
245
    Py_Initialize();
 
246
    signal(SIGINT, SIG_DFL);
 
247
 
 
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());
 
252
}
 
253
 
 
254
#define PLUGIN_LIST_FILE "/.sunpinyin/plugins.list";
 
255
#define PLUGIN_NAME_LEN 128
 
256
 
 
257
CIMIPluginManager::CIMIPluginManager()
 
258
    : m_hasError(false), m_waitTime(0)
 
259
{
 
260
    InitializePython();
 
261
}
 
262
 
 
263
CIMIPluginManager::~CIMIPluginManager()
 
264
{
 
265
    for (size_t i = 0;  i < m_plugins.size(); i++) {
 
266
        delete m_plugins[i];
 
267
    }
 
268
}
 
269
 
 
270
void
 
271
CIMIPluginManager::initializePlugins()
 
272
{
 
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");
 
277
    if (!fp) {
 
278
        return;
 
279
    }
 
280
    while (true) {
 
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) {
 
285
            break;
 
286
        }
 
287
        if (strlen(plugin_name) == 1) {
 
288
            continue;
 
289
        }
 
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);
 
293
        }
 
294
    }
 
295
    fclose(fp);
 
296
}
 
297
 
 
298
TPluginTypeEnum
 
299
CIMIPluginManager::detectPluginType(std::string filename)
 
300
{
 
301
    if (filename.length() >= 3
 
302
        && filename.substr(filename.length() - 3) == ".py") {
 
303
        return CIMI_PLUGIN_PYTHON;
 
304
    } else {
 
305
        return CIMI_PLUGIN_UNKNOWN;
 
306
    }
 
307
}
 
308
 
 
309
CIMIPlugin*
 
310
CIMIPluginManager::loadPlugin(std::string filename)
 
311
{
 
312
    TPluginTypeEnum type = detectPluginType(filename);
 
313
    CIMIPlugin* plugin = createPlugin(filename, type);
 
314
    std::stringstream error;
 
315
 
 
316
    if (plugin == NULL) {
 
317
        return NULL;
 
318
    }
 
319
    if (hasLastError()) {
 
320
        delete plugin;
 
321
        return NULL;
 
322
    }
 
323
 
 
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
 
329
            return NULL;
 
330
        }
 
331
    }
 
332
    m_plugins.push_back(plugin);
 
333
    return plugin;
 
334
}
 
335
 
 
336
CIMIPlugin*
 
337
CIMIPluginManager::createPlugin(std::string filename,
 
338
                                TPluginTypeEnum pluginType)
 
339
{
 
340
    std::stringstream error;
 
341
    clearLastError();
 
342
 
 
343
    switch (pluginType) {
 
344
    case CIMI_PLUGIN_PYTHON:
 
345
        return new CIMIPythonPlugin(filename);
 
346
    case CIMI_PLUGIN_UNKNOWN:
 
347
    default:
 
348
        error << "Cannot detect type for " << filename;
 
349
        setLastError(error.str());
 
350
        return NULL;
 
351
    }
 
352
}
 
353
 
 
354
void
 
355
CIMIPluginManager::setLastError(std::string desc)
 
356
{
 
357
    m_hasError = true;
 
358
    m_lastError = desc;
 
359
}
 
360
 
 
361
void
 
362
CIMIPluginManager::clearLastError()
 
363
{
 
364
    m_hasError = false;
 
365
    m_lastError = "";
 
366
}
 
367
 
 
368
void
 
369
CIMIPluginManager::markWaitTime(int waitTime)
 
370
{
 
371
    if (waitTime <= 0)
 
372
        return;
 
373
 
 
374
    if (m_waitTime == 0) {
 
375
        m_waitTime = waitTime;
 
376
    } else if (waitTime < m_waitTime) {
 
377
        m_waitTime = waitTime;
 
378
    }
 
379
}