Ticket #180: cjkv.patch

File cjkv.patch, 36.7 KB (added by Pavel Strashkin, 15 years ago)

patch to add CJKV tokenizer supporting

  • xapian-core-1.0.16

    diff -bBrupN xapian-core-1.0.16.old/Makefile.am xapian-core-1.0.16/Makefile.am
    old new lib_LTLIBRARIES = libxapian.la  
    104104
    105105libxapian_la_SOURCES =
    106106
    107 libxapian_la_LDFLAGS = $(XAPIAN_LDFLAGS) -no-undefined -version-info $(LIBRARY_VERSION_INFO)
     107libxapian_la_LDFLAGS = $(XAPIAN_LDFLAGS) -no-undefined -version-info $(LIBRARY_VERSION_INFO) $(GLIB2_LIBS)
    108108
    109109if !MAINTAINER_NO_DOCS
    110110dist_man_MANS = xapian-config.1
  • xapian-core-1.0.16

    diff -bBrupN xapian-core-1.0.16.old/acinclude.m4 xapian-core-1.0.16/acinclude.m4
    old new  
    11dnl acinclude.m4
    22m4_include(m4/rjb_find_stlport.m4)
    33m4_include(m4/type_socklen_t.m4)
     4m4_include(m4/pkg.m4)
  • xapian-core-1.0.16

    diff -bBrupN xapian-core-1.0.16.old/configure.ac xapian-core-1.0.16/configure.ac
    old new if test yes = "$use_stlport"; then  
    10791079fi
    10801080AC_SUBST(STLPORT_CXXFLAGS)
    10811081
     1082dnl Check for glib-2.0 files what need to compile CJKV
     1083PKG_CHECK_MODULES([GLIB2], [glib-2.0])
     1084AM_CXXFLAGS="$GLIB2_CFLAGS $AM_CXXFLAGS"
     1085LIBS="$GLIB2_LIBS $LIBS"
     1086
    10821087AC_SUBST(AM_CXXFLAGS)
    10831088
    10841089dnl Restore CXXFLAGS to those the user specified or autoconf defaulted to.
    dnl There are no files generated by AC_O  
    11191124dnl and we need to ensure they exist so that the rest of configure or make
    11201125dnl won't fail because they don't exist when srcdir != builddir.
    11211126if test yes = "$vpath_build" ; then
    1122   for dir in include include/xapian languages queryparser ; do
     1127  for dir in include include/xapian include/xapian/cjkv languages queryparser ; do
    11231128    test -d "$dir" || mkdir "$dir"
    11241129  done
    11251130fi
  • include/Makefile.mk

    diff -bBrupN xapian-core-1.0.16.old/include/Makefile.mk xapian-core-1.0.16/include/Makefile.mk
    old new xapianinclude_HEADERS =\  
    2929        include/xapian/types.h\
    3030        include/xapian/unicode.h\
    3131        include/xapian/valueiterator.h\
    32         include/xapian/visibility.h
     32        include/xapian/visibility.h\
     33        include/xapian/cjkv/CJKVTokenizer.h
    3334
    3435nodist_xapianinclude_HEADERS =\
    3536        include/xapian/version.h
  • include/xapian/cjkv/CJKVTokenizer.h

    diff -bBrupN xapian-core-1.0.16.old/include/xapian/cjkv/CJKVTokenizer.h xapian-core-1.0.16/include/xapian/cjkv/CJKVTokenizer.h
    old new  
     1/*
     2 *  Copyright 2007-2008 林永忠 Yung-Chung Lin
     3 *  Copyright 2008-2009 Fabrice Colin
     4 *
     5 *  This library is free software; you can redistribute it and/or
     6 *  modify it under the terms of the GNU Lesser General Public
     7 *  License as published by the Free Software Foundation; either
     8 *  version 2 of the License, or (at your option) any later version.
     9 *
     10 *  This library is distributed in the hope that it will be useful,
     11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13 *  Lesser General Public License for more details.
     14 *
     15 *  You should have received a copy of the GNU Lesser General Public
     16 *  License along with this library; if not, write to the Free Software
     17 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     18 */
     19
     20#ifndef _DIJON_CJKVTOKENIZER_H
     21#define _DIJON_CJKVTOKENIZER_H
     22
     23#include <string>
     24#include <vector>
     25#ifdef HAVE_UNICODE_H
     26#include <unicode.h>
     27#else
     28#include <glib/gunicode.h>
     29#define unicode_char_t gunichar
     30#endif
     31
     32#ifndef DIJON_CJKV_EXPORT
     33#if defined __GNUC__ && (__GNUC__ >= 4)
     34  #define DIJON_CJKV_EXPORT __attribute__ ((visibility("default")))
     35#else
     36  #define DIJON_CJKV_EXPORT
     37#endif
     38#endif
     39
     40namespace Dijon
     41{
     42        class DIJON_CJKV_EXPORT CJKVTokenizer
     43        {
     44                public:
     45                        CJKVTokenizer();
     46                        ~CJKVTokenizer();
     47
     48                        class TokensHandler
     49                        {
     50                                public:
     51                                        TokensHandler() {}
     52                                        virtual ~TokensHandler() {}
     53
     54                                        virtual bool handle_token(const std::string &tok, bool is_cjkv) = 0;
     55                        };
     56
     57                        void set_ngram_size(unsigned int ngram_size);
     58
     59                        unsigned int get_ngram_size(void) const;
     60
     61                        void set_max_token_count(unsigned int max_token_count);
     62
     63                        unsigned int get_max_token_count(void) const;
     64
     65                        void set_max_text_size(unsigned int max_text_size);
     66
     67                        unsigned int get_max_text_size(void) const;
     68
     69                        void tokenize(const std::string &str,
     70                                std::vector<std::string> &token_list);
     71
     72                        void tokenize(const std::string &str,
     73                                TokensHandler &handler,
     74                                bool break_ascii_only_on_space = false);
     75
     76                        void split(const std::string &str,
     77                                std::vector<std::string> &token_list);
     78
     79                        void split(const std::string &str,
     80                                std::vector<unicode_char_t> &token_list);
     81
     82                        void segment(const std::string &str,
     83                                std::vector<std::string> &token_segment);
     84
     85                        bool has_cjkv(const std::string &str);
     86
     87                        bool has_cjkv_only(const std::string &str);
     88
     89                protected:
     90                        unsigned int m_nGramSize;
     91                        unsigned int m_maxTokenCount;
     92                        unsigned int m_maxTextSize;
     93
     94        };
     95};
     96
     97#endif // _DIJON_CJKVTOKENIZER_H
  • xapian-core-1.0.16

    diff -bBrupN xapian-core-1.0.16.old/m4/pkg.m4 xapian-core-1.0.16/m4/pkg.m4
    old new  
     1# pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*-
     2#
     3# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
     4#
     5# This program is free software; you can redistribute it and/or modify
     6# it under the terms of the GNU General Public License as published by
     7# the Free Software Foundation; either version 2 of the License, or
     8# (at your option) any later version.
     9#
     10# This program is distributed in the hope that it will be useful, but
     11# WITHOUT ANY WARRANTY; without even the implied warranty of
     12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13# General Public License for more details.
     14#
     15# You should have received a copy of the GNU General Public License
     16# along with this program; if not, write to the Free Software
     17# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     18#
     19# As a special exception to the GNU General Public License, if you
     20# distribute this file as part of a program that contains a
     21# configuration script generated by Autoconf, you may include it under
     22# the same distribution terms that you use for the rest of that program.
     23
     24# PKG_PROG_PKG_CONFIG([MIN-VERSION])
     25# ----------------------------------
     26AC_DEFUN([PKG_PROG_PKG_CONFIG],
     27[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
     28m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
     29AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl
     30if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
     31        AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
     32fi
     33if test -n "$PKG_CONFIG"; then
     34        _pkg_min_version=m4_default([$1], [0.9.0])
     35        AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
     36        if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
     37                AC_MSG_RESULT([yes])
     38        else
     39                AC_MSG_RESULT([no])
     40                PKG_CONFIG=""
     41        fi
     42
     43fi[]dnl
     44])# PKG_PROG_PKG_CONFIG
     45
     46# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
     47#
     48# Check to see whether a particular set of modules exists.  Similar
     49# to PKG_CHECK_MODULES(), but does not set variables or print errors.
     50#
     51#
     52# Similar to PKG_CHECK_MODULES, make sure that the first instance of
     53# this or PKG_CHECK_MODULES is called, or make sure to call
     54# PKG_CHECK_EXISTS manually
     55# --------------------------------------------------------------
     56AC_DEFUN([PKG_CHECK_EXISTS],
     57[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
     58if test -n "$PKG_CONFIG" && \
     59    AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
     60  m4_ifval([$2], [$2], [:])
     61m4_ifvaln([$3], [else
     62  $3])dnl
     63fi])
     64
     65
     66# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
     67# ---------------------------------------------
     68m4_define([_PKG_CONFIG],
     69[if test -n "$PKG_CONFIG"; then
     70    if test -n "$$1"; then
     71        pkg_cv_[]$1="$$1"
     72    else
     73        PKG_CHECK_EXISTS([$3],
     74                         [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
     75                         [pkg_failed=yes])
     76    fi
     77else
     78        pkg_failed=untried
     79fi[]dnl
     80])# _PKG_CONFIG
     81
     82# _PKG_SHORT_ERRORS_SUPPORTED
     83# -----------------------------
     84AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
     85[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
     86if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
     87        _pkg_short_errors_supported=yes
     88else
     89        _pkg_short_errors_supported=no
     90fi[]dnl
     91])# _PKG_SHORT_ERRORS_SUPPORTED
     92
     93
     94# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
     95# [ACTION-IF-NOT-FOUND])
     96#
     97#
     98# Note that if there is a possibility the first call to
     99# PKG_CHECK_MODULES might not happen, you should be sure to include an
     100# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
     101#
     102#
     103# --------------------------------------------------------------
     104AC_DEFUN([PKG_CHECK_MODULES],
     105[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
     106AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
     107AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
     108
     109pkg_failed=no
     110AC_MSG_CHECKING([for $1])
     111
     112_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
     113_PKG_CONFIG([$1][_LIBS], [libs], [$2])
     114
     115m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
     116and $1[]_LIBS to avoid the need to call pkg-config.
     117See the pkg-config man page for more details.])
     118
     119if test $pkg_failed = yes; then
     120        _PKG_SHORT_ERRORS_SUPPORTED
     121        if test $_pkg_short_errors_supported = yes; then
     122                $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"`
     123        else
     124                $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"`
     125        fi
     126        # Put the nasty error message in config.log where it belongs
     127        echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
     128
     129        ifelse([$4], , [AC_MSG_ERROR(dnl
     130[Package requirements ($2) were not met:
     131
     132$$1_PKG_ERRORS
     133
     134Consider adjusting the PKG_CONFIG_PATH environment variable if you
     135installed software in a non-standard prefix.
     136
     137_PKG_TEXT
     138])],
     139                [$4])
     140elif test $pkg_failed = untried; then
     141        ifelse([$4], , [AC_MSG_FAILURE(dnl
     142[The pkg-config script could not be found or is too old.  Make sure it
     143is in your PATH or set the PKG_CONFIG environment variable to the full
     144path to pkg-config.
     145
     146_PKG_TEXT
     147
     148To get pkg-config, see <http://www.freedesktop.org/software/pkgconfig>.])],
     149                [$4])
     150else
     151        $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
     152        $1[]_LIBS=$pkg_cv_[]$1[]_LIBS
     153        AC_MSG_RESULT([yes])
     154        ifelse([$3], , :, [$3])
     155fi[]dnl
     156])# PKG_CHECK_MODULES
  • queryparser/CJKVTokenizer.cc

    diff -bBrupN xapian-core-1.0.16.old/queryparser/CJKVTokenizer.cc xapian-core-1.0.16/queryparser/CJKVTokenizer.cc
    old new  
     1/*
     2 *  Copyright 2007-2008 林永忠 Yung-Chung Lin
     3 *  Copyright 2008-2009 Fabrice Colin
     4 *
     5 *  This library is free software; you can redistribute it and/or
     6 *  modify it under the terms of the GNU Lesser General Public
     7 *  License as published by the Free Software Foundation; either
     8 *  version 2 of the License, or (at your option) any later version.
     9 *
     10 *  This library is distributed in the hope that it will be useful,
     11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13 *  Lesser General Public License for more details.
     14 *
     15 *  You should have received a copy of the GNU Lesser General Public
     16 *  License along with this library; if not, write to the Free Software
     17 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     18 */
     19
     20#include <ctype.h>
     21#include <string.h>
     22#include <iostream>
     23
     24#include <xapian/cjkv/CJKVTokenizer.h>
     25
     26#ifndef HAVE_UNICODE_H
     27static void unicode_init(void)
     28{
     29}
     30
     31static char *unicode_get_utf8(const char *p, unicode_char_t *result)
     32{
     33        *result = g_utf8_get_char(p);
     34
     35        return (*result == (unicode_char_t)-1) ? NULL : g_utf8_next_char(p);
     36}
     37
     38static int unicode_strlen(const char *p, int max)
     39{
     40        return (int)g_utf8_strlen(p, (gssize)max);
     41}
     42
     43static int unicode_ispunct(unicode_char_t c)
     44{
     45        if (g_unichar_ispunct(c))
     46        {
     47                return 1;
     48        }
     49
     50        return 0;
     51}
     52
     53static int unicode_isspace(unicode_char_t c)
     54{
     55        if (g_unichar_isspace(c))
     56        {
     57                return 1;
     58        }
     59
     60        return 0;
     61}
     62#endif
     63
     64// 2E80..2EFF; CJK Radicals Supplement
     65// 3000..303F; CJK Symbols and Punctuation
     66// 3040..309F; Hiragana
     67// 30A0..30FF; Katakana
     68// 3100..312F; Bopomofo
     69// 3130..318F; Hangul Compatibility Jamo
     70// 3190..319F; Kanbun
     71// 31A0..31BF; Bopomofo Extended
     72// 31C0..31EF; CJK Strokes
     73// 31F0..31FF; Katakana Phonetic Extensions
     74// 3200..32FF; Enclosed CJK Letters and Months
     75// 3300..33FF; CJK Compatibility
     76// 3400..4DBF; CJK Unified Ideographs Extension A
     77// 4DC0..4DFF; Yijing Hexagram Symbols
     78// 4E00..9FFF; CJK Unified Ideographs
     79// A700..A71F; Modifier Tone Letters
     80// AC00..D7AF; Hangul Syllables
     81// F900..FAFF; CJK Compatibility Ideographs
     82// FE30..FE4F; CJK Compatibility Forms
     83// FF00..FFEF; Halfwidth and Fullwidth Forms
     84// 20000..2A6DF; CJK Unified Ideographs Extension B
     85// 2F800..2FA1F; CJK Compatibility Ideographs Supplement
     86#define UTF8_IS_CJKV(p)                                                  \
     87    (((p) >= 0x2E80 && (p) <= 0x2EFF)                                   \
     88     || ((p) >= 0x3000 && (p) <= 0x303F)                                \
     89     || ((p) >= 0x3040 && (p) <= 0x309F)                                \
     90     || ((p) >= 0x30A0 && (p) <= 0x30FF)                                \
     91     || ((p) >= 0x3100 && (p) <= 0x312F)                                \
     92     || ((p) >= 0x3130 && (p) <= 0x318F)                                \
     93     || ((p) >= 0x3190 && (p) <= 0x319F)                                \
     94     || ((p) >= 0x31A0 && (p) <= 0x31BF)                                \
     95     || ((p) >= 0x31C0 && (p) <= 0x31EF)                                \
     96     || ((p) >= 0x31F0 && (p) <= 0x31FF)                                \
     97     || ((p) >= 0x3200 && (p) <= 0x32FF)                                \
     98     || ((p) >= 0x3300 && (p) <= 0x33FF)                                \
     99     || ((p) >= 0x3400 && (p) <= 0x4DBF)                                \
     100     || ((p) >= 0x4DC0 && (p) <= 0x4DFF)                                \
     101     || ((p) >= 0x4E00 && (p) <= 0x9FFF)                                \
     102     || ((p) >= 0xA700 && (p) <= 0xA71F)                                \
     103     || ((p) >= 0xAC00 && (p) <= 0xD7AF)                                \
     104     || ((p) >= 0xF900 && (p) <= 0xFAFF)                                \
     105     || ((p) >= 0xFE30 && (p) <= 0xFE4F)                                \
     106     || ((p) >= 0xFF00 && (p) <= 0xFFEF)                                \
     107     || ((p) >= 0x20000 && (p) <= 0x2A6DF)                              \
     108     || ((p) >= 0x2F800 && (p) <= 0x2FA1F)                              \
     109     || ((p) >= 0x2F800 && (p) <= 0x2FA1F))
     110
     111using namespace std;
     112using namespace Dijon;
     113
     114static void _split_string(string str, const string &delim,
     115        vector<string> &list)
     116{
     117        list.clear();
     118
     119        string::size_type cut_at = 0;
     120        while ((cut_at = str.find_first_of(delim)) != str.npos)
     121        {
     122                if (cut_at > 0)
     123                {
     124                        list.push_back(str.substr(0,cut_at));
     125                }
     126                str = str.substr(cut_at+1);
     127        }
     128
     129        if (str.length() > 0)
     130        {
     131                list.push_back(str);
     132        }
     133}
     134
     135static inline unsigned char *_unicode_to_char(unicode_char_t &uchar,
     136        unsigned char *p)
     137{
     138        if (p == NULL)
     139        {
     140                return NULL;
     141        }
     142
     143        memset(p, 0, sizeof(unicode_char_t) + 1);
     144        if (unicode_isspace(uchar) ||
     145                unicode_ispunct(uchar))
     146        {
     147                p[0] = ' ';
     148        }
     149        else if (uchar < 0x80)
     150        {
     151                p[0] = uchar;
     152        }
     153        else if (uchar < 0x800)
     154        {
     155                p[0] = (0xC0 | uchar >> 6);
     156                p[1] = (0x80 | uchar & 0x3F);
     157        }
     158        else if (uchar < 0x10000)
     159        {
     160                p[0] = (0xE0 | uchar >> 12);
     161                p[1] = (0x80 | uchar >> 6 & 0x3F);
     162                p[2] = (0x80 | uchar & 0x3F);
     163        }
     164        else if (uchar < 0x200000)
     165        {
     166                p[0] = (0xF0 | uchar >> 18);
     167                p[1] = (0x80 | uchar >> 12 & 0x3F);
     168                p[2] = (0x80 | uchar >> 6 & 0x3F);
     169                p[3] = (0x80 | uchar & 0x3F);
     170        }
     171
     172        return p;
     173}
     174
     175class VectorTokensHandler : public CJKVTokenizer::TokensHandler
     176{
     177        public:
     178                VectorTokensHandler(vector<string> &token_list) :
     179                        CJKVTokenizer::TokensHandler(),
     180                        m_token_list(token_list)
     181                {
     182                }
     183
     184                virtual ~VectorTokensHandler()
     185                {
     186                }
     187
     188                virtual bool handle_token(const string &tok, bool is_cjkv)
     189                {
     190                        m_token_list.push_back(tok);
     191                        return true;
     192                }
     193
     194        protected:
     195                vector<string> &m_token_list;
     196
     197};
     198
     199CJKVTokenizer::CJKVTokenizer() :
     200        m_nGramSize(2),
     201        m_maxTokenCount(0),
     202        m_maxTextSize(5242880)
     203{
     204        unicode_init();
     205}
     206
     207CJKVTokenizer::~CJKVTokenizer()
     208{
     209}
     210
     211void CJKVTokenizer::set_ngram_size(unsigned int ngram_size)
     212{
     213        m_nGramSize = ngram_size;
     214}
     215
     216unsigned int CJKVTokenizer::get_ngram_size(void) const
     217{
     218        return m_nGramSize;
     219}
     220
     221void CJKVTokenizer::set_max_token_count(unsigned int max_token_count)
     222{
     223        m_maxTokenCount = max_token_count;
     224}
     225
     226unsigned int CJKVTokenizer::get_max_token_count(void) const
     227{
     228        return m_maxTokenCount;
     229}
     230
     231void CJKVTokenizer::set_max_text_size(unsigned int max_text_size)
     232{
     233        m_maxTextSize = max_text_size;
     234}
     235
     236unsigned int CJKVTokenizer::get_max_text_size(void) const
     237{
     238        return m_maxTextSize;
     239}
     240
     241void CJKVTokenizer::tokenize(const string &str, vector<string> &token_list)
     242{
     243        VectorTokensHandler handler(token_list);
     244
     245        tokenize(str, handler);
     246}
     247
     248void CJKVTokenizer::tokenize(const string &str, TokensHandler &handler,
     249        bool break_ascii_only_on_space)
     250{
     251        string token_str;
     252        vector<string> temp_token_list;
     253        vector<unicode_char_t> temp_uchar_list;
     254        unsigned int tokens_count = 0;
     255
     256        split(str, temp_token_list);
     257        split(str, temp_uchar_list);
     258
     259        for (unsigned int i = 0; i < temp_token_list.size();)
     260        {
     261                if ((m_maxTokenCount > 0) &&
     262                        (tokens_count >= m_maxTokenCount))
     263                {
     264                        break;
     265                }
     266                token_str.resize(0);
     267                if (UTF8_IS_CJKV(temp_uchar_list[i]))
     268                {
     269                        for (unsigned int j = i; j < i + m_nGramSize; j++)
     270                        {
     271                                if ((m_maxTokenCount > 0) &&
     272                                        (tokens_count >= m_maxTokenCount))
     273                                {
     274                                        break;
     275                                }
     276                                if (j == temp_token_list.size())
     277                                {
     278                                        break;
     279                                }
     280                                if (UTF8_IS_CJKV(temp_uchar_list[j]))
     281                                {
     282                                        token_str += temp_token_list[j];
     283                                        if (handler.handle_token(token_str, true) == true)
     284                                        {
     285                                                ++tokens_count;
     286                                        }
     287                                }
     288                        }
     289                        i++;
     290                }
     291                else
     292                {
     293                        unsigned int j = i;
     294
     295                        while (j < temp_token_list.size())
     296                        {
     297                                unsigned char *p = (unsigned char*) temp_token_list[j].c_str();
     298                                bool break_ascii = false;
     299
     300                                if (isascii((int)p[0]) != 0)
     301                                {
     302                                        if (break_ascii_only_on_space == true)
     303                                        {
     304                                                if (isspace((int)p[0]) != 0)
     305                                                {
     306                                                        break_ascii = true;
     307                                                }
     308                                        }
     309                                        else if (isalnum((int)p[0]) == 0)
     310                                        {
     311                                                break_ascii = true;
     312                                        }
     313                                }
     314
     315                                if (break_ascii == true)
     316                                {
     317                                        j++;
     318                                        break;
     319                                }
     320                                else if (UTF8_IS_CJKV(temp_uchar_list[j]))
     321                                {
     322                                        break;
     323                                }
     324
     325                                token_str += temp_token_list[j];
     326                                j++;
     327                        }
     328                        i = j;
     329                        if ((m_maxTokenCount > 0) &&
     330                                (tokens_count >= m_maxTokenCount))
     331                        {
     332                                break;
     333                        }
     334                        if (token_str.length() > 0)
     335                        {
     336                                if (handler.handle_token(token_str, false) == true)
     337                                {
     338                                        ++tokens_count;
     339                                }
     340                        }
     341                }
     342        }
     343}
     344
     345void CJKVTokenizer::split(const string &str, vector<string> &token_list)
     346{
     347        unicode_char_t uchar;
     348        const char *str_ptr = str.c_str();
     349        int str_utf8_len = unicode_strlen(str_ptr, str.length());
     350        unsigned char p[sizeof(unicode_char_t) + 1];
     351
     352        for (int i = 0; i < str_utf8_len; i++)
     353        {
     354                str_ptr = unicode_get_utf8(str_ptr, &uchar);
     355                if (str_ptr == NULL)
     356                {
     357                        break;
     358                }
     359
     360                if (i >= m_maxTextSize)
     361                {
     362                        break;
     363                }
     364
     365                token_list.push_back((const char*)_unicode_to_char(uchar, p));
     366        }
     367}
     368
     369void CJKVTokenizer::split(const string &str, vector<unicode_char_t> &token_list)
     370{
     371        unicode_char_t uchar;
     372        const char *str_ptr = str.c_str();
     373        int str_utf8_len = unicode_strlen(str_ptr, str.length());
     374
     375        for (int i = 0; i < str_utf8_len; i++)
     376        {
     377                str_ptr = unicode_get_utf8(str_ptr, &uchar);
     378                if (str_ptr == NULL)
     379                {
     380                        break;
     381                }
     382
     383                if (i >= m_maxTextSize)
     384                {
     385                        break;
     386                }
     387
     388                token_list.push_back(uchar);
     389        }
     390}
     391
     392void CJKVTokenizer::segment(const string &str, vector<string> &token_segment)
     393{
     394        vector<string> token_list;
     395        string onlySpacesStr(str);
     396
     397        for (string::iterator it = onlySpacesStr.begin(); it != onlySpacesStr.end(); ++it)
     398        {
     399                if (isspace((int)*it) != 0)
     400                {
     401                        *it = ' ';
     402                }
     403        }
     404
     405        _split_string(onlySpacesStr, " ", token_segment);
     406}
     407
     408bool CJKVTokenizer::has_cjkv(const string &str)
     409{
     410        vector<unicode_char_t> temp_uchar_list;
     411
     412        split(str, temp_uchar_list);
     413
     414        for (unsigned int i = 0; i < temp_uchar_list.size(); i++)
     415        {
     416                if (UTF8_IS_CJKV(temp_uchar_list[i]))
     417                {
     418                        return true;
     419                }
     420        }
     421        return false;
     422}
     423
     424bool CJKVTokenizer::has_cjkv_only(const string &str)
     425{
     426        vector<unicode_char_t> temp_uchar_list;
     427
     428        split(str, temp_uchar_list);
     429
     430        for (unsigned int i = 0; i < temp_uchar_list.size(); i++)
     431        {
     432                if (!(UTF8_IS_CJKV(temp_uchar_list[i])))
     433                {
     434                        unsigned char p[sizeof(unicode_char_t) + 1];
     435
     436                        _unicode_to_char(temp_uchar_list[i], p);
     437                        if (isspace((int)p[0]) == 0)
     438                        {
     439                                return false;
     440                        }
     441                }
     442        }
     443        return true;
     444}
     445
  • queryparser/Makefile.mk

    diff -bBrupN xapian-core-1.0.16.old/queryparser/Makefile.mk xapian-core-1.0.16/queryparser/Makefile.mk
    old new libxapian_la_SOURCES +=\  
    6060        queryparser/queryparser.cc\
    6161        queryparser/queryparser_internal.cc\
    6262        queryparser/termgenerator.cc\
    63         queryparser/termgenerator_internal.cc
     63        queryparser/termgenerator_internal.cc\
     64        queryparser/CJKVTokenizer.cc
  • queryparser/queryparser_internal.cc

    diff -bBrupN xapian-core-1.0.16.old/queryparser/queryparser_internal.cc xapian-core-1.0.16/queryparser/queryparser_internal.cc
    old new  
    3131#include "queryparser_internal.h"
    3232#include <xapian/error.h>
    3333#include <xapian/unicode.h>
     34#include <xapian/cjkv/CJKVTokenizer.h>
    3435#include "stringutils.h"
    3536
    3637// Include the list of token values lemon generates.
    QueryParser::Internal::parse_term(Utf8It  
    554555    return term;
    555556}
    556557
     558class QueryModifier: public Dijon::CJKVTokenizer::TokensHandler
     559{
     560public:
     561    typedef enum {
     562        NONE     = 0,
     563        BRACKETS = 1
     564    } CJKVWrap;
     565
     566    QueryModifier(const string &query, unsigned int nGramSize):
     567        m_query(query),
     568        m_pos(0),
     569        m_wrap(BRACKETS),
     570        m_wrapped(false),
     571        m_nGramCount(0),
     572        m_nGramSize(nGramSize),
     573        m_tokensCount(0),
     574        m_hasCJKV(false),
     575        m_hasNonCJKV(false)
     576    {
     577    }
     578
     579    virtual ~QueryModifier()
     580    {
     581    }
     582
     583    virtual bool handle_token(const string &tok, bool is_cjkv)
     584    {
     585        if (tok.empty()) {
     586            return false;
     587        }
     588
     589        // Where is this token in the original query ?
     590        string::size_type tokPos = m_query.find(tok, m_pos);
     591        ++m_tokensCount;
     592
     593        // Is this CJKV ?
     594        if (!is_cjkv) {
     595            char lastChar = tok[tok.length() - 1];
     596
     597            if (tokPos == string::npos) {
     598                // This should have been found
     599                return false;
     600            }
     601
     602            if (m_nGramCount > 0) {
     603                wrapClose();
     604
     605                m_nGramCount = 0;
     606                m_pos = tokPos;
     607            }
     608
     609            m_currentFilter.clear();
     610            if (lastChar == '"') {
     611                // It's a quoted string
     612                m_wrap = NONE;
     613            } else if (lastChar == ':') {
     614                // It's a filter
     615                m_wrap = NONE;
     616                m_currentFilter = tok;
     617            } else {
     618                m_wrap = BRACKETS;
     619            }
     620
     621            if (m_currentFilter.empty()) {
     622                m_hasNonCJKV = true;
     623            }
     624
     625            // Return right away
     626            return true;
     627        }
     628
     629        // First n-gram ?
     630        if (m_nGramCount == 0) {
     631            if (tokPos == string::npos) {
     632                // That's definitely not right
     633                return false;
     634            }
     635
     636            // Append non-CJKV text that precedes and start wrapping CJKV tokens
     637            if (tokPos > m_pos) {
     638                m_modifiedQuery += " " + m_query.substr(m_pos, tokPos - m_pos);
     639            }
     640            m_pos += tok.length();
     641
     642            wrapOpen();
     643        } else {
     644            m_modifiedQuery += " ";
     645            if (!m_currentFilter.empty()) {
     646                m_modifiedQuery += m_currentFilter;
     647            }
     648        }
     649        m_modifiedQuery += tok;
     650
     651        if (tokPos != string::npos) {
     652            m_pos = tokPos + tok.length();
     653        }
     654
     655        ++m_nGramCount;
     656        m_hasCJKV = true;
     657
     658        return true;
     659    }
     660
     661    unsigned int get_tokens_count(void) const
     662    {
     663        return m_tokensCount;
     664    }
     665
     666    string get_modified_query(bool &pureCJKV)
     667    {
     668        // Anything left ?
     669        if (m_pos < m_query.length() - 1) {
     670            m_modifiedQuery += " " + m_query.substr(m_pos);
     671        }
     672        wrapClose();
     673
     674        if (m_hasCJKV && !m_hasNonCJKV) {
     675            pureCJKV = true;
     676        } else {
     677            pureCJKV = false;
     678        }
     679
     680        return m_modifiedQuery;
     681    }
     682
     683protected:
     684    string m_query;
     685    string m_modifiedQuery;
     686    string::size_type m_pos;
     687    CJKVWrap m_wrap;
     688    bool m_wrapped;
     689    string m_currentFilter;
     690    unsigned int m_nGramCount;
     691    unsigned int m_nGramSize;
     692    unsigned int m_tokensCount;
     693    bool m_hasCJKV;
     694    bool m_hasNonCJKV;
     695
     696    void wrapOpen(void)
     697    {
     698        switch (m_wrap) {
     699            case BRACKETS:
     700                m_modifiedQuery += " (";
     701                break;
     702            case NONE:
     703            default:
     704                break;
     705        }
     706        m_wrapped = true;
     707    }
     708
     709    void wrapClose(void)
     710    {
     711        if (!m_wrapped) {
     712            return;
     713        }
     714
     715        // Finish wrapping CJKV tokens
     716        switch (m_wrap) {
     717            case BRACKETS:
     718                m_modifiedQuery += ')';
     719                break;
     720            case NONE:
     721            default:
     722                break;
     723        }
     724        m_wrapped = false;
     725    }
     726}; // class QueryModifier: public Dijon::CJKVTokenizer::TokensHandler
     727
    557728Query
    558 QueryParser::Internal::parse_query(const string &qs, unsigned flags,
     729QueryParser::Internal::parse_query(const string &iqs, unsigned flags,
    559730                                   const string &default_prefix)
    560731{
     732    Dijon::CJKVTokenizer tokenizer;
     733    string qs(iqs);
     734
     735    // Modifying the query is necessary if it's CJKV
     736    if (tokenizer.has_cjkv(qs)) {
     737        QueryModifier handler(qs, tokenizer.get_ngram_size());
     738        tokenizer.tokenize(qs, handler, true);
     739
     740        // We can disable stemming and spelling correction for pure CJKV queries
     741        bool pureCJKV = false;
     742        qs = handler.get_modified_query(pureCJKV);
     743    }
     744
    561745    yyParser * pParser = ParseAlloc();
    562746
    563747    // Set value_ranges if we may have to handle value ranges in the query.
  • queryparser/termgenerator_internal.cc

    diff -bBrupN xapian-core-1.0.16.old/queryparser/termgenerator_internal.cc xapian-core-1.0.16/queryparser/termgenerator_internal.cc
    old new  
    2525#include <xapian/document.h>
    2626#include <xapian/queryparser.h>
    2727#include <xapian/unicode.h>
     28#include <xapian/cjkv/CJKVTokenizer.h>
    2829
    2930#include "stringutils.h"
    3031
    using namespace std;  
    3435
    3536namespace Xapian {
    3637
     38// FIXME: add API for this:
     39#define STOPWORDS_NONE 0
     40#define STOPWORDS_IGNORE 1
     41#define STOPWORDS_INDEX_UNSTEMMED_ONLY 2
     42
    3743// Put a limit on the size of terms to help prevent the index being bloated
    3844// by useless junk terms.
    3945static const unsigned int MAX_PROB_TERM_LENGTH = 64;
    should_stem(const std::string & term)  
    6470    return ((SHOULD_STEM_MASK >> Unicode::get_category(*u)) & 1);
    6571}
    6672
     73inline bool
     74should_index(const std::string &term, int stop_mode, const Stopper *stopper)
     75{
     76    if (term.size() > MAX_PROB_TERM_LENGTH) {
     77        return false;
     78    }
     79
     80    if (stop_mode == STOPWORDS_IGNORE && stopper && (*stopper)(term)) {
     81        return false;
     82    }
     83
     84    return true;
     85}
     86
    6787inline unsigned check_infix(unsigned ch) {
    6888    if (ch == '\'' || ch == '&' || ch == 0xb7 || ch == 0x5f4 || ch == 0x2027) {
    6989        // Unicode includes all these except '&' in it's word boundary rules,
    inline unsigned check_suffix(unsigned ch  
    108128    return 0;
    109129}
    110130
    111 // FIXME: add API for this:
    112 #define STOPWORDS_NONE 0
    113 #define STOPWORDS_IGNORE 1
    114 #define STOPWORDS_INDEX_UNSTEMMED_ONLY 2
     131class TokensIndexer: public Dijon::CJKVTokenizer::TokensHandler
     132{
     133public:
     134    TokensIndexer(
     135        TermGenerator::Internal &generator,
     136        const string &prefix,
     137        termcount &weight,
     138        bool with_positions,
     139        int stop_mode,
     140        unsigned int gram_size
     141    ):
     142        m_generator(generator),
     143        m_prefix(prefix),
     144        m_weight(weight),
     145        m_with_positions(with_positions),
     146        m_stop_mode(stop_mode),
     147        m_gram_size(gram_size),
     148        m_gram_count(0),
     149        m_hasCJKV(false)
     150    {
     151    }
     152
     153    virtual ~TokensIndexer()
     154    {
     155    }
     156
     157    virtual bool handle_token(const string &token, bool is_cjkv)
     158    {
     159        termcount termpos_inc = 0;
     160        if (is_cjkv) {
     161            if ((m_gram_count + 1) % m_gram_size == 0) {
     162                termpos_inc = 1;
     163            }
     164        } else {
     165            termpos_inc = 1;
     166        }
     167
     168        string term(to_lower(token));
     169        if (m_generator.index_term(m_prefix, term, m_weight, m_stop_mode, m_with_positions, termpos_inc) == false) {
     170            return false;
     171        }
     172
     173        if (is_cjkv) {
     174            m_gram_count++;
     175            m_hasCJKV = true;
     176        } else {
     177            m_gram_count = 0;
     178        }
     179
     180        return true;
     181    }
     182
     183protected:
     184    string to_lower(const string &str)
     185    {
     186        string out(str);
     187        for (string::iterator i = out.begin(); i != out.end(); ++i) {
     188            if (U_isupper(*i)) {
     189                *i = Unicode::tolower(*i);
     190            }
     191        }
     192        return out;
     193    }
     194
     195    TermGenerator::Internal &m_generator;
     196    const string &m_prefix;
     197    termcount &m_weight;
     198    bool m_with_positions;
     199    int m_stop_mode;
     200    unsigned int m_gram_size;
     201    unsigned int m_gram_count;
     202    bool m_hasCJKV;
     203};
    115204
    116205void
    117206TermGenerator::Internal::index_text(Utf8Iterator itor, termcount weight,
    118207                                    const string & prefix, bool with_positions)
    119208{
    120     int stop_mode = STOPWORDS_INDEX_UNSTEMMED_ONLY;
    121 
    122     if (!stopper) stop_mode = STOPWORDS_NONE;
     209    // set default stop words mode depend on
     210    // stopper handler availability
     211    int stop_mode = stopper ? STOPWORDS_INDEX_UNSTEMMED_ONLY : STOPWORDS_NONE;
     212
     213    const char *raw = itor.raw();
     214    if (raw) {
     215        Dijon::CJKVTokenizer tokenizer;
     216        string text(raw);
     217
     218        // is there CJKV chars? then we should use CJKVTokenizer
     219        // instead of builtin TermGenerator
     220        if (tokenizer.has_cjkv(text)) {
     221            TokensIndexer handler(*this, prefix, weight, with_positions, stop_mode, tokenizer.get_ngram_size());
     222            tokenizer.tokenize(text, handler);
     223            return;
     224        }
     225    }
    123226
    124227    while (true) {
    125228        // Advance to the start of the next term.
    126229        unsigned ch;
    127230        while (true) {
    128             if (itor == Utf8Iterator()) return;
     231            if (itor == Utf8Iterator()) {
     232                return;
     233            }
     234
    129235            ch = check_wordchar(*itor);
    130             if (ch) break;
     236            if (ch) {
     237                break;
     238            }
     239
    131240            ++itor;
    132241        }
    133242
    TermGenerator::Internal::index_text(Utf8  
    158269            do {
    159270                Unicode::append_utf8(term, ch);
    160271                prevch = ch;
    161                 if (++itor == Utf8Iterator()) goto endofterm;
     272                if (++itor == Utf8Iterator()) {
     273                    goto endofterm;
     274                }
    162275                ch = check_wordchar(*itor);
    163276            } while (ch);
    164277
    165278            Utf8Iterator next(itor);
    166279            ++next;
    167             if (next == Utf8Iterator()) break;
     280            if (next == Utf8Iterator()) {
     281                break;
     282            }
     283
    168284            unsigned nextch = check_wordchar(*next);
    169             if (!nextch) break;
     285            if (!nextch) {
     286                break;
     287            }
     288
    170289            unsigned infix_ch = *itor;
    171290            if (is_digit(prevch) && is_digit(*next)) {
    172291                infix_ch = check_infix_digit(infix_ch);
    TermGenerator::Internal::index_text(Utf8  
    174293                // Handle things like '&' in AT&T, apostrophes, etc.
    175294                infix_ch = check_infix(infix_ch);
    176295            }
    177             if (!infix_ch) break;
     296
     297            if (!infix_ch) {
     298                break;
     299            }
     300
    178301            Unicode::append_utf8(term, infix_ch);
    179302            ch = nextch;
    180303            itor = next;
    TermGenerator::Internal::index_text(Utf8  
    189312                    break;
    190313                }
    191314                Unicode::append_utf8(term, ch);
    192                 if (++itor == Utf8Iterator()) goto endofterm;
     315                if (++itor == Utf8Iterator()) {
     316                    goto endofterm;
     317                }
    193318            }
    194319        }
    195320
    196321endofterm:
    197         if (term.size() > MAX_PROB_TERM_LENGTH) continue;
     322        index_term(prefix, term, weight, stop_mode, with_positions);
     323    } // while(true)
     324}
    198325
    199         if (stop_mode == STOPWORDS_IGNORE && (*stopper)(term)) continue;
     326bool
     327TermGenerator::Internal::index_term(const string &prefix, const string &term, termcount &weight, int stop_mode, bool with_positions, termcount termpos_inc)
     328{
     329    if (should_index(term, stop_mode, stopper) == false) {
     330        return false;
     331    }
    200332
    201333        if (with_positions) {
    202             doc.add_posting(prefix + term, ++termpos, weight);
     334        termpos += termpos_inc;
     335        doc.add_posting(prefix + term, termpos, weight);
    203336        } else {
    204337            doc.add_term(prefix + term, weight);
    205338        }
    206         if ((flags & FLAG_SPELLING) && prefix.empty()) db.add_spelling(term);
    207339
    208         if (!stemmer.internal.get()) continue;
     340    if ((flags & FLAG_SPELLING) && prefix.empty()) {
     341        db.add_spelling(term);
     342    }
    209343
    210         if (stop_mode == STOPWORDS_INDEX_UNSTEMMED_ONLY && (*stopper)(term))
    211             continue;
     344    if (!stemmer.internal.get()) {
     345        return true;
     346    }
     347
     348    if (stop_mode == STOPWORDS_INDEX_UNSTEMMED_ONLY && stopper && (*stopper)(term)) {
     349        return true;
     350    }
    212351
    213352        // Note, this uses the lowercased term, but that's OK as we only
    214353        // want to avoid stemming terms starting with a digit.
    215         if (!should_stem(term)) continue;
     354    if (should_stem(term) == false) {
     355        return true;
     356    }
    216357
    217358        // Add stemmed form without positional information.
    218359        string stem("Z");
    219360        stem += prefix;
    220361        stem += stemmer(term);
    221362        doc.add_term(stem, weight);
    222     }
    223 }
    224363
     364    return true;
    225365}
     366
     367} // namespace Xapian
  • queryparser/termgenerator_internal.h

    diff -bBrupN xapian-core-1.0.16.old/queryparser/termgenerator_internal.h xapian-core-1.0.16/queryparser/termgenerator_internal.h
    old new  
    2121#ifndef XAPIAN_INCLUDED_TERMGENERATOR_INTERNAL_H
    2222#define XAPIAN_INCLUDED_TERMGENERATOR_INTERNAL_H
    2323
     24#include <string>
     25
    2426#include <xapian/base.h>
    2527#include <xapian/database.h>
    2628#include <xapian/document.h>
     
    3032namespace Xapian {
    3133
    3234class Stopper;
     35using std::string;
    3336
    3437class TermGenerator::Internal : public Xapian::Internal::RefCntBase {
    3538    friend class TermGenerator;
    class TermGenerator::Internal : public X  
    4346  public:
    4447    Internal() : stopper(NULL), termpos(0),
    4548        flags(TermGenerator::flags(0)) { }
    46     void index_text(Utf8Iterator itor,
    47                     termcount weight,
    48                     const std::string & prefix,
    49                     bool with_positions);
     49    void index_text(Utf8Iterator itor, termcount weight, const std::string & prefix, bool with_positions);
     50    bool index_term(const string &prefix, const string &term, termcount &weight, int stop_mode, bool with_positions, termcount termpos_inc=1);
    5051};
    5152
    5253}
  • xapian-core-1.0.16

    diff -bBrupN xapian-core-1.0.16.old/xapian-config.in xapian-core-1.0.16/xapian-config.in
    old new while [ 0 != "$#" ] ; do  
    187187        cxxflags=
    188188        [ -n "@ANSI_CXXFLAGS@" ] && cxxflags="@ANSI_CXXFLAGS@ "
    189189        [ -n "@STLPORT_CXXFLAGS@" ] && cxxflags="${cxxflags}@STLPORT_CXXFLAGS@ "
     190        [ -n "@GLIB2_CFLAGS@" ] && cxxflags="${cxxflags}@GLIB2_CFLAGS@ "
    190191        echo "$cxxflags$I"
    191192        ;;
    192193