rev 8942 - in kde-extras/strigi/trunk/debian: . patches
Fathi Boudra
fabo at alioth.debian.org
Sun Jan 13 11:15:45 UTC 2008
Author: fabo
Date: 2008-01-13 11:15:45 +0000 (Sun, 13 Jan 2008)
New Revision: 8942
Added:
kde-extras/strigi/trunk/debian/patches/01_strigi_branch_r760102.diff
Removed:
kde-extras/strigi/trunk/debian/cmake.mk
kde-extras/strigi/trunk/debian/patches/01_strigi_branch_r729946.diff
Modified:
kde-extras/strigi/trunk/debian/changelog
kde-extras/strigi/trunk/debian/control
kde-extras/strigi/trunk/debian/libstreamanalyzer0.install
kde-extras/strigi/trunk/debian/patches/series
kde-extras/strigi/trunk/debian/rules
Log:
* Update branch pull patch to r760102.
* Remove our cmake.mk. Merged in CDBS package.
* Clean up build dependencies.
* Update my e-mail address.
* Bump Standard-Version to 3.7.3.
* Use Homepage field.
* Update installed files.
* Build with --no-undefined and --as-needed linker flags.
* Remove libstreams-dev dependency on libstdc++-dev. (Closes: #454995)
* Add strict versioned shlibs. (Closes: #460296)
* Add CMAKE_CXX_COMPILER:FILEPATH=g++ to workaround g++ path not properly
set.
Modified: kde-extras/strigi/trunk/debian/changelog
===================================================================
--- kde-extras/strigi/trunk/debian/changelog 2008-01-13 01:38:36 UTC (rev 8941)
+++ kde-extras/strigi/trunk/debian/changelog 2008-01-13 11:15:45 UTC (rev 8942)
@@ -1,3 +1,20 @@
+strigi (0.5.7-2) unstable; urgency=low
+
+ * Update branch pull patch to r760102.
+ * Remove our cmake.mk. Merged in CDBS package.
+ * Clean up build dependencies.
+ * Update my e-mail address.
+ * Bump Standard-Version to 3.7.3.
+ * Use Homepage field.
+ * Update installed files.
+ * Build with --no-undefined and --as-needed linker flags.
+ * Remove libstreams-dev dependency on libstdc++-dev. (Closes: #454995)
+ * Add strict versioned shlibs. (Closes: #460296)
+ * Add CMAKE_CXX_COMPILER:FILEPATH=g++ to workaround g++ path not properly
+ set.
+
+ -- Fathi Boudra <fabo at debian.org> Sat, 12 Jan 2008 22:57:56 +0100
+
strigi (0.5.7-1) unstable; urgency=low
* New upstream release.
Deleted: kde-extras/strigi/trunk/debian/cmake.mk
Modified: kde-extras/strigi/trunk/debian/control
===================================================================
--- kde-extras/strigi/trunk/debian/control 2008-01-13 01:38:36 UTC (rev 8941)
+++ kde-extras/strigi/trunk/debian/control 2008-01-13 11:15:45 UTC (rev 8942)
@@ -3,11 +3,11 @@
Priority: optional
Maintainer: Debian KDE Extras Team <pkg-kde-extras at lists.alioth.debian.org>
Uploaders: Fathi Boudra <fabo at debian.org>, Mark Purcell <msp at debian.org>
-Build-Depends: cdbs, debhelper (>= 5), cmake, quilt, bison,
- libattr1-dev, libbz2-dev, libclucene-dev, libcppunit-dev, libdbus-1-dev,
- libexiv2-dev, libqt4-dev, libxml2-dev, zlib1g-dev, libxml2-utils,
- python-support, python
-Standards-Version: 3.7.2
+Build-Depends: cdbs, debhelper (>= 5), cmake, quilt, libbz2-dev,
+ libclucene-dev, libdbus-1-dev, libexiv2-dev, libqt4-dev, libxml2-dev,
+ zlib1g-dev, libxml2-utils, python-support, python
+Standards-Version: 3.7.3
+Homepage: http://strigi.sourceforge.net
Package: strigi-daemon
Architecture: any
@@ -37,8 +37,6 @@
duplicates)
.
This package contains the Strigi daemon
- .
- Homepage: http://strigi.sourceforge.net
Package: strigi-client
Architecture: any
@@ -50,8 +48,6 @@
This package is part of Strigi Desktop Search, it contains the Qt4 client.
.
See the 'strigi-daemon' package for more informations.
- .
- Homepage: http://strigi.sourceforge.net
Package: strigi-utils
Architecture: any
@@ -75,8 +71,6 @@
parsing the output.
.
See the 'strigi-daemon' package for more informations.
- .
- Homepage: http://strigi.sourceforge.net
Package: libstreams0
Architecture: any
@@ -88,20 +82,16 @@
writing clients using libstreams.
.
See the 'strigi-daemon' package for more informations.
- .
- Homepage: http://strigi.sourceforge.net
Package: libstreams-dev
Architecture: any
Section: libdevel
-Depends: libstreams0 (= ${binary:Version}), libstdc++-dev
+Depends: libstreams0 (= ${binary:Version})
Description: development files for libstreams
This package is part of Strigi Desktop Search, it contains the Strigi
development files for libstreams.
.
See the 'strigi-daemon' package for more informations.
- .
- Homepage: http://strigi.sourceforge.net
Package: libstreamanalyzer0
Architecture: any
@@ -115,8 +105,6 @@
writing clients using libstreamanalyzer.
.
See the 'strigi-daemon' package for more informations.
- .
- Homepage: http://strigi.sourceforge.net
Package: libstreamanalyzer-dev
Architecture: any
@@ -129,8 +117,6 @@
development files for libstreamanalyzer.
.
See the 'strigi-daemon' package for more informations.
- .
- Homepage: http://strigi.sourceforge.net
Package: libsearchclient0
Architecture: any
@@ -142,8 +128,6 @@
writing clients.
.
See the 'strigi-daemon' package for more informations.
- .
- Homepage: http://strigi.sourceforge.net
Package: libsearchclient-dev
Architecture: any
@@ -154,8 +138,6 @@
development files for libsearchclient.
.
See the 'strigi-daemon' package for more informations.
- .
- Homepage: http://strigi.sourceforge.net
Package: libstrigihtmlgui0
Architecture: any
@@ -166,8 +148,6 @@
writing html clients.
.
See the 'strigi-daemon' package for more informations.
- .
- Homepage: http://strigi.sourceforge.net
Package: libstrigihtmlgui-dev
Architecture: any
@@ -178,8 +158,6 @@
development files for libstrigihtmlgui.
.
See the 'strigi-daemon' package for more informations.
- .
- Homepage: http://strigi.sourceforge.net
Package: libstrigiqtdbusclient0
Architecture: any
@@ -190,8 +168,6 @@
writing Qt D-Bus clients for strigi.
.
See the 'strigi-daemon' package for more informations.
- .
- Homepage: http://strigi.sourceforge.net
Package: libstrigiqtdbusclient-dev
Architecture: any
@@ -202,8 +178,6 @@
development files for libstrigiqtdbusclient.
.
See the 'strigi-daemon' package for more informations.
- .
- Homepage: http://strigi.sourceforge.net
Package: deskbar-plugins-strigi
Architecture: any
@@ -211,8 +185,6 @@
Depends: ${python:Depends}, strigi-daemon, deskbar-applet
Description: Deskbar plugin to search files with strigi
This package is part of Strigi Desktop Search, it contains a plugin
- for the Gnome Deskbar applet to search for files using Strigi.
+ for the GNOME Deskbar applet to search for files using Strigi.
.
See the 'strigi-daemon' package for more informations.
- .
- Homepage: http://strigi.sourceforge.net
Modified: kde-extras/strigi/trunk/debian/libstreamanalyzer0.install
===================================================================
--- kde-extras/strigi/trunk/debian/libstreamanalyzer0.install 2008-01-13 01:38:36 UTC (rev 8941)
+++ kde-extras/strigi/trunk/debian/libstreamanalyzer0.install 2008-01-13 11:15:45 UTC (rev 8942)
@@ -1,6 +1,7 @@
usr/lib/libstreamanalyzer.so.*
usr/lib/strigi/strigiindex_*
-usr/lib/strigi/strigiea_*
usr/lib/strigi/strigila_*
usr/lib/strigi/strigita_*
+usr/share/strigi/fieldproperties/strigi.rdfs
+usr/share/strigi/fieldproperties/xesam-convenience.rdfs
usr/share/strigi/fieldproperties/xesam.rdfs
Deleted: kde-extras/strigi/trunk/debian/patches/01_strigi_branch_r729946.diff
Added: kde-extras/strigi/trunk/debian/patches/01_strigi_branch_r760102.diff
===================================================================
--- kde-extras/strigi/trunk/debian/patches/01_strigi_branch_r760102.diff (rev 0)
+++ kde-extras/strigi/trunk/debian/patches/01_strigi_branch_r760102.diff 2008-01-13 11:15:45 UTC (rev 8942)
@@ -0,0 +1,10197 @@
+--- /dev/null
++++ b/cmake/FindFAM.cmake
+@@ -0,0 +1,36 @@
++# - Try to find the fam libraries
++# Once done this will define
++#
++# FAM_FOUND - system supports fam
++# FAM_INCLUDE_DIR - the fam include directory
++# FAM_LIBRARIES - libfam library
++
++FIND_PATH(FAM_INCLUDE_DIR fam.h PATHS /usr/include /usr/local/include )
++FIND_LIBRARY(FAM_LIBRARIES NAMES fam )
++
++FIND_LIBRARY(GAMIN_LIBRARIES NAMES gamin gamin-1 PATH /usr/lib /usr/local/lib)
++
++IF (NOT GAMIN_LIBRARIES AND FAM_LIBRARIES)
++ message (STATUS "Please use Gamin instead of FAM if possible")
++ENDIF (NOT GAMIN_LIBRARIES AND FAM_LIBRARIES)
++
++if (GAMIN_LIBRARIES)
++ message(STATUS "Found Gamin: good choice, it's better then FAM")
++endif (GAMIN_LIBRARIES)
++
++IF(FAM_INCLUDE_DIR AND FAM_LIBRARIES)
++ SET(FAM_FOUND 1)
++ if(NOT FAM_FIND_QUIETLY)
++ if (GAMIN_LIBRARIES)
++ message(STATUS "Found FAM (provided by Gamin): ${FAM_LIBRARIES}")
++ else (GAMIN_LIBRARIES)
++ message(STATUS "Found FAM: ${FAM_LIBRARIES}")
++ endif (GAMIN_LIBRARIES)
++ endif(NOT FAM_FIND_QUIETLY)
++ELSE(FAM_INCLUDE_DIR AND FAM_LIBRARIES)
++ SET(FAM_FOUND 0 CACHE BOOL "Not found FAM")
++ message(STATUS "NOT Found FAM, disabling it")
++ENDIF(FAM_INCLUDE_DIR AND FAM_LIBRARIES)
++
++MARK_AS_ADVANCED(FAM_INCLUDE_DIR FAM_LIBRARIES)
++
+--- a/cmake/MacroCheckGccVisibility.cmake
++++ b/cmake/MacroCheckGccVisibility.cmake
+@@ -32,7 +32,7 @@
+
+ if (${GccVisibility} AND GCC_IS_NEWER_THAN_4_1 AND NOT _GCC_COMPILED_WITH_BAD_ALLOCATOR)
+ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
+- set (KDE4_C_FLAGS "-fvisibility=hidden")
++ set (KDE4_C_FLAGS "${KDE4_C_FLAGS}" "-fvisibility=hidden")
+
+ if (GCC_IS_NEWER_THAN_4_2)
+ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden")
+--- /dev/null
++++ b/cmake/MacroFindOptionalDep.cmake
+@@ -0,0 +1,59 @@
++# FIND_OPTIONAL_DEP macro implements two typical optional dependency handling approaches:
++#
++# Best-effort approach(FORCE_DEPS=OFF):
++# Link to all enabled optional dependencies if found. Turn off not found ones, and keep compiling.
++# This greatly benefits hand-compiling from source if all suggested dependencies are turned on by default.
++# Newly installed software conveniently integrates with whatever environment it's compiled in.
++#
++# Strict dependencies approach(FORCE_DEPS=ON):
++# All enabled optional dependencies must be found, or compilation aborts.
++# This approach lets request and ensure specific functionality. The compilation is deterministic
++# in the sense that everything that's requested is provided or the process fails.
++# This is the preferred behaviour for automated building by package managers/distro maintainers.
++#
++# Parameters:
++# _package: the package to load
++# _found: the name of *_FOUND variable which is set by find_package() if ${_package} is found.
++# _enabled: option/variable name which along with FORCE_DEPS options controls macro behaviour:
++# ${_enabled} FORCE_DEPS Behaviour
++# OFF any ${_package} is not loaded
++# ON ON Try loading ${_package}. If package is not found, abort(fatal error).
++# ON OFF Try loading ${_package}. If package is not found, continue.
++# _description: a short description of features provided by ${_package}.
++# Used to display human-readable diagnostic messages
++
++# macro name changed from FIND_OPTIONAL_PACKAGE to FIND_OPTIONAL_DEP due to clash with a macro from KDE4
++
++# if ON, requested optional deps become required
++# if OFF, requested optional deps are linked to if found
++
++OPTION(FORCE_DEPS "Enforce strict dependencies" OFF)
++
++macro(FIND_OPTIONAL_DEP _package _enabled _found _description)
++
++ if(${_enabled})
++ if(FORCE_DEPS)
++ find_package(${_package} REQUIRED)
++ else(FORCE_DEPS)
++ find_package(${_package})
++ endif(FORCE_DEPS)
++ endif(${_enabled})
++
++ REPORT_OPTIONAL_PACKAGE_STATUS(${_package} ${_enabled} ${_found} ${_description})
++
++endmacro(FIND_OPTIONAL_DEP)
++
++
++macro(REPORT_OPTIONAL_PACKAGE_STATUS _package _enabled _found _description)
++
++ if(${_enabled})
++ if(${_found})
++ MESSAGE("** ${_package} is found. Support for ${_description} is enabled")
++ else(${_found})
++ MESSAGE("** ${_package} not found. Support for ${_description} is disabled")
++ endif(${_found})
++ else(${_enabled})
++ MESSAGE("** ${_package} is disabled. No support for ${_description}")
++ endif(${_enabled})
++
++endmacro(REPORT_OPTIONAL_PACKAGE_STATUS)
+\ No newline at end of file
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -16,6 +16,7 @@
+ # include general modules
+ include(UsePkgConfig)
+ include(MacroCheckGccVisibility)
++include(MacroFindOptionalDep)
+
+ # compile in debug mode
+ IF(NOT CMAKE_BUILD_TYPE)
+@@ -67,23 +68,26 @@
+ REMOVE_DEFINITIONS(-fPIC)
+ ENDIF(CMAKE_SYSTEM MATCHES "SunOS-5*.")
+
+-OPTION(ENABLE_INOTIFY
+- "enable inotify support (unstable)"
+- OFF)
+-
+-OPTION(ENABLE_DBUS
+- "enable dbus support"
+- ON)
+-
+-OPTION(ENABLE_NEWXESAM
+- "enable new xesam support"
+- OFF)
+-
+-OPTION(ENABLE_SQLITE "enable sqlite support" OFF)
+-
+-OPTION(ENABLE_LOG4CXX
+- "enable log4cxx support for advanced logging"
+- OFF)
++OPTION(ENABLE_INOTIFY "enable inotify support (unstable)" OFF)
++IF(NOT WIN32)
++ OPTION(ENABLE_FAM "enable FAM support (testing)" OFF)
++ENDIF(NOT WIN32)
++OPTION(ENABLE_DBUS "enable dbus support" ON)
++OPTION(ENABLE_NEWXESAM "enable new xesam support" OFF)
++OPTION(ENABLE_LOG4CXX "enable log4cxx support for advanced logging" OFF)
++OPTION(ENABLE_CPPUNIT "enable CppUnit for unit tests" ON)
++OPTION(ENABLE_QT4 "enable Qt4 GUI" ON)
++OPTION(ENABLE_EXIV2
++ "enable exiv2 support. This allows you to index EXIF/IPTC metadata." ON)
++
++# backends
++OPTION(ENABLE_CLUCENE "enable CLucene support (recommended)" ON)
++OPTION(ENABLE_HYPERESTRAIER "enable Hyper Estraier support(unreliable)" OFF)
++OPTION(ENABLE_SQLITE "enable SQLite support(unreliable)" OFF)
++
++#OPTION(ENABLE_LIBXML2 "enable libxml2 support" ON)
++OPTION(ENABLE_EXPAT "enable expat support" OFF)
++
+
+ find_package(ZLIB REQUIRED)
+ find_package(BZip2 REQUIRED)
+@@ -91,28 +95,24 @@
+ find_package(Iconv REQUIRED)
+
+ # use either expat or libxml2
+-find_package(Expat)
++FIND_OPTIONAL_DEP(Expat ENABLE_EXPAT Expat_FOUND "XML via Expat")
++# libxml seems to be required regardless of what because it's used by streamanalyzer
++#FIND_OPTIONAL_DEP(LibXml2 ENABLE_LIBXML2 LIBXML2_FOUND "XML via LibXml2")
+ find_package(LibXml2 REQUIRED)
++
+ if (NOT LIBXML2_FOUND AND NOT Expat_FOUND)
+- MESSAGE(FATAL "You need libxml2 or libexpat")
++ MESSAGE(FATAL_ERROR "You need libxml2 or libexpat")
+ endif (NOT LIBXML2_FOUND AND NOT Expat_FOUND)
+
+-find_package(CLucene)
+-if(NOT CLucene_FOUND)
+- MESSAGE("Could not find CLucene. Please install CLucene = 0.9.16a (http://clucene.sf.net)")
+-endif(NOT CLucene_FOUND)
+-
+-find_package(HyperEstraier)
+-find_package(Exiv2)
+-if(ENABLE_SQLITE)
+- find_package(SQLite)
+-endif(ENABLE_SQLITE)
++FIND_OPTIONAL_DEP(CLucene ENABLE_CLUCENE CLucene_FOUND "CLucene backend")
++FIND_OPTIONAL_DEP(HyperEstraier ENABLE_HYPERESTRAIER HyperEstraier_FOUND "HyperEstraier backend")
++FIND_OPTIONAL_DEP(SQLite ENABLE_SQLITE SQLite_FOUND "SQLite backend")
++
++FIND_OPTIONAL_DEP(Exiv2 ENABLE_EXIV2 EXIV2_FOUND "indexing of EXIF/IPTC metadata")
++
+ #find_package(XAttr)
+-set(QT_MIN_VERSION "4.2.0")
+-find_package(Qt4)
+-if (NOT QT4_FOUND)
+- MESSAGE("** Qt4 was not found. No GUI will be built.")
+-endif (NOT QT4_FOUND)
++set(QT_MIN_VERSION "4.3.0")
++FIND_OPTIONAL_DEP(Qt4 ENABLE_QT4 QT4_FOUND "QT4 GUI client")
+
+ check_include_files(strings.h HAVE_STRINGS_H) # various
+
+@@ -129,35 +129,43 @@
+ ELSE(WIN32)
+ include(UsePkgConfig)
+ PKGCONFIG(dbus-1 DBUS_INCLUDE_DIR DBUS_LIBRARY_DIR DBUS_LDFLAGS DBUS_CFLAGS)
+- if (NOT DBUS_INCLUDE_DIR)
+- MESSAGE(FATAL_ERROR "Could not find DBus")
+- endif (NOT DBUS_INCLUDE_DIR)
+
+- EXEC_PROGRAM(${PKGCONFIG_EXECUTABLE} ARGS --atleast-version=1.0 dbus-1 RETURN_VALUE _return_VALUE OUTPUT_VARIABLE _pkgconfigDevNull )
+- if(_return_VALUE STREQUAL "0")
+- message(STATUS "Found dbus-1 release >= 1.0")
+- else(_return_VALUE STREQUAL "0")
+- message(STATUS "Found dbus-1 release < 1.0 support for dbus client will be disable")
+- set(ENABLE_DBUS "OFF")
+- endif(_return_VALUE STREQUAL "0")
++ if (DBUS_INCLUDE_DIR)
++ EXEC_PROGRAM(${PKGCONFIG_EXECUTABLE} ARGS --atleast-version=1.0 dbus-1 RETURN_VALUE _return_VALUE OUTPUT_VARIABLE _pkgconfigDevNull )
++ if(_return_VALUE STREQUAL "0")
++ message(STATUS "Found dbus-1 release >= 1.0")
++ set(DBUS_FOUND "ON")
++ else(_return_VALUE STREQUAL "0")
++ message(STATUS "Found dbus-1 release < 1.0. Release >=1.0 is needed")
++ endif(_return_VALUE STREQUAL "0")
++ endif (DBUS_INCLUDE_DIR)
++
++ REPORT_OPTIONAL_PACKAGE_STATUS(DBus-1 ENABLE_DBUS DBUS_FOUND "DBus interface in Strigi daemon")
++ if(NOT DBUS_FOUND)
++ set(ENABLE_DBUS "OFF")
++ if(FORCE_DEPS)
++ MESSAGE(FATAL_ERROR "Aborting")
++ endif(FORCE_DEPS)
++ endif(NOT DBUS_FOUND)
+
+ ENDIF(WIN32)
+ ENDIF(ENABLE_DBUS)
+
+-find_program (BISON
+- bison
+- DOC "Path to bison command, used for xesam userlanguage parser generation"
+-)
+-
+-if (BISON)
+- MESSAGE (STATUS "Found bison: ${BISON}")
+-else (BISON)
+- MESSAGE ("** GNU bison not found. This affects the xesam parser.")
+-endif (BISON)
+-
+-if (ENABLE_LOG4CXX)
+- find_package (Log4cxx)
+-endif (ENABLE_LOG4CXX)
++# Don't delete bison section, but only micron seems to need this
++#find_program (BISON
++# bison
++# DOC "Path to bison command, used for xesam userlanguage parser generation")
++
++#if (BISON)
++# MESSAGE (STATUS "Found bison: ${BISON}")
++#else (BISON)
++# MESSAGE ("** GNU bison not found. This affects the xesam parser.")
++#endif (BISON)
++
++FIND_OPTIONAL_DEP(Log4cxx ENABLE_LOG4CXX LOG4CXX_FOUND "advanced logging")
++IF(NOT WIN32)
++ FIND_OPTIONAL_DEP(FAM ENABLE_FAM FAM_FOUND "efficient file change monitoring system")
++ENDIF(NOT WIN32)
+
+ #
+ # AC_CHECK_LIB(dl, dlopen, DL_LIBRARY="-ldl", DL_LIBRARY="") for cmake by
+@@ -189,13 +197,10 @@
+
+ SET (DIRS ${DIRS} src)
+
+-find_package(CppUnit)
+-if (NOT CppUnit_FOUND)
+- MESSAGE("** CppUnit was not found. Strigi unit tests will not be built.")
+-else (NOT CppUnit_FOUND)
+- MESSAGE(STATUS "CppUnit found. Strigi unit tests will be built.")
++FIND_OPTIONAL_DEP(CppUnit ENABLE_CPPUNIT CppUnit_FOUND "Strigi unit tests")
++if (CppUnit_FOUND)
+ SET (DIRS ${DIRS} tests)
+-endif (NOT CppUnit_FOUND)
++endif (CppUnit_FOUND)
+
+ ENABLE_TESTING()
+ SUBDIRS (${DIRS})
+--- a/config.h.cmake
++++ b/config.h.cmake
+@@ -139,6 +139,8 @@
+ #ifndef strigi_nanosleep
+ #define strigi_nanosleep(nanoseconds) Sleep(nanoseconds/1000000)
+ #endif
++
++ #define snprintf _snprintf
+ #endif
+
+ #include <strigi/strigiconfig.h>
+@@ -147,9 +149,9 @@
+
+ #define LIBINSTALLDIR "${LIBINSTALLDIR}"
+
+-#define SOURCEDIR "${CMAKE_SOURCE_DIR}"
++#define SOURCEDIR "${CMAKE_CURRENT_SOURCE_DIR}"
+
+-#define BINARYDIR "${CMAKE_BINARY_DIR}"
++#define BINARYDIR "${CMAKE_CURRENT_BINARY_DIR}"
+
+ #define INSTALLDIR "${CMAKE_INSTALL_PREFIX}"
+
+--- a/README
++++ b/README
+@@ -32,6 +32,7 @@
+ - Qt4 >= 4.2 (for a graphical interface)
+ - D-Bus (http://www.freedesktop.org/wiki/Software/dbus): it is optional but is tuned on by default
+ - linux kernel >= 2.6.13 (for inotify support)
++- FAM or Gamin for FAM file system monitoring support (Gamin is recommended)
+ - log4cxx >= 0.9.7 (http://logging.apache.org/log4cxx/) for advanced logging features
+
+ Upgrading:
+@@ -60,6 +61,8 @@
+ include a custom library directory
+ -DENABLE_INOTIFY:BOOL=ON
+ enable inotify support, requires kernel >= 2.6.13 with inotify support enabled
++ -DENABLE_FAM:BOOL=OFF
++ enable FAM support, requires FAM or Gamin (which is better) installed
+ -DENABLE_LOG4CXX:BOOL=ON
+ enable log4cxx support, provides advanced logging features using log4cxx lib
+ -DENABLE_DBUS:BOOL=ON
+--- a/src/CMakeLists.txt
++++ b/src/CMakeLists.txt
+@@ -32,7 +32,7 @@
+
+ if (NOT CLucene_FOUND)
+ message("** No CLucene libraries were found, so Strigi cannot use indexes.")
+- message("** It is recommended to install CLucene >= 0.9.16.")
++ message("** It is recommended to install CLucene >= 0.9.16a.")
+ message("** You will still be able to use deepfind, deepgrep and xmlindexer.")
+ endif (NOT CLucene_FOUND)
+
+--- a/src/daemon/CMakeLists.txt
++++ b/src/daemon/CMakeLists.txt
+@@ -14,6 +14,15 @@
+ MESSAGE(STATUS "inotify support enabled")
+ ENDIF(ENABLE_INOTIFY)
+
++IF(FAM_FOUND AND ENABLE_FAM)
++ add_definitions(-DHAVE_FAM)
++ IF (GAMIN_LIBRARIES)
++ MESSAGE(STATUS "FAM support enabled (using Gamin)")
++ ELSE (GAMIN_LIBRARIES)
++ MESSAGE(STATUS "FAM support enabled")
++ ENDIF (GAMIN_LIBRARIES)
++ENDIF(FAM_FOUND AND ENABLE_FAM)
++
+ IF(ENABLE_POLLING)
+ add_definitions(-DHAVE_POLLING)
+ MESSAGE(STATUS "polling support enabled")
+--- a/src/daemon/daemon.cpp
++++ b/src/daemon/daemon.cpp
+@@ -28,7 +28,9 @@
+ #include "xesamlivesearch.h"
+ #include "queue/jobqueue.h"
+
+-#if defined (HAVE_INOTIFY)
++#if defined (HAVE_FAM)
++#include "famlistener.h"
++#elif defined (HAVE_INOTIFY)
+ #include "inotifylistener.h"
+ #else
+ #include "pollinglistener.h"
+@@ -293,8 +295,9 @@
+ XesamLiveSearch xesam(index, queue);
+
+ EventListener* listener = NULL;
+-
+-#if defined (HAVE_INOTIFY)
++#if defined (HAVE_FAM)
++ listener = new FamListener (dirs);
++#elif defined (HAVE_INOTIFY)
+ // listen for requests
+ listener = new InotifyListener(dirs);
+ #else
+--- a/src/daemon/dbus/dbuscpp/makecode.pl
++++ b/src/daemon/dbus/dbuscpp/makecode.pl
+@@ -23,7 +23,7 @@
+ my $classname;
+ my $constructorargs = "";
+
+-my @lines = `cat $interfaceheader`;
++my @lines = `cat "$interfaceheader"`;
+
+ my %typemapping = (
+ "void" => "ignore",
+--- a/src/daemon/eventlistener/CMakeLists.txt
++++ b/src/daemon/eventlistener/CMakeLists.txt
+@@ -3,12 +3,22 @@
+ set (inotify_SRC inotifylistener.cpp)
+ ENDIF(ENABLE_INOTIFY)
+
+-INCLUDE_DIRECTORIES ( ${inotify_INCLUDE} ../../streams/strigi ..
+- ${strigi_BINARY_DIR}/src/streams/strigi
+- ${strigi_SOURCE_DIR}/src/streams/strigi)
++IF(FAM_FOUND AND ENABLE_FAM)
++ set (fam_INCLUDE inotify)
++ set (fam_SRC famlistener.cpp)
++ set (eventlistener_LIBS ${eventlistener_LIBS} ${FAM_LIBRARIES})
++ENDIF(FAM_FOUND AND ENABLE_FAM)
++
++INCLUDE_DIRECTORIES ( ${inotify_INCLUDE} ../../streams/strigi ..
++ ${strigi_BINARY_DIR}/src/streams/strigi
++ ${strigi_SOURCE_DIR}/src/streams/strigi)
+
+ ADD_LIBRARY(eventlistener
+- event.cpp
+- eventlistenerqueue.cpp
+- pollinglistener.cpp
+- ${inotify_SRC})
++ event.cpp
++ eventlistenerqueue.cpp
++ pollinglistener.cpp
++ fslistener.cpp
++ ${inotify_SRC}
++ ${fam_SRC})
++
++TARGET_LINK_LIBRARIES (eventlistener ${eventlistener_LIBS})
+\ No newline at end of file
+--- a/src/daemon/eventlistener/eventlistener.h
++++ b/src/daemon/eventlistener/eventlistener.h
+@@ -41,7 +41,7 @@
+ public:
+ explicit EventListener(const char* name) :StrigiThread(name) {
+ m_pEventQueue = NULL;
+- m_pindexerconfiguration = NULL;
++ m_pAnalyzerConfiguration = NULL;
+ m_pManager = NULL;
+ m_pollingInterval = 180;
+ }
+@@ -50,14 +50,13 @@
+
+ virtual bool init() { return true; }
+ virtual bool addWatch (const std::string& path) = 0;
+- virtual void addWatches (const std::set<std::string>& watches,
+- bool enableInterrupt = false) = 0;
++ virtual void addWatches (const std::set<std::string>& watches) = 0;
+ virtual void setIndexedDirectories (const std::set<std::string>& dirs) = 0;
+ void setEventListenerQueue (EventListenerQueue* eventQueue) {
+ m_pEventQueue = eventQueue;
+ }
+ void setIndexerConfiguration(Strigi::AnalyzerConfiguration* ic) {
+- m_pindexerconfiguration = ic;
++ m_pAnalyzerConfiguration = ic;
+ }
+ void setCombinedIndexManager (CombinedIndexManager* m) {
+ m_pManager = m;
+@@ -76,7 +75,7 @@
+
+ protected:
+ EventListenerQueue* m_pEventQueue;
+- Strigi::AnalyzerConfiguration* m_pindexerconfiguration;
++ Strigi::AnalyzerConfiguration* m_pAnalyzerConfiguration;
+ CombinedIndexManager* m_pManager;
+ unsigned int m_pollingInterval;//!< pause time between each polling operation
+ };
+--- a/src/daemon/eventlistener/eventlistenerqueue.cpp
++++ b/src/daemon/eventlistener/eventlistenerqueue.cpp
+@@ -292,6 +292,11 @@
+ // D | U | IMPOSSIBLE
+ // D | C | U
+
++ /*STRIGI_LOG_DEBUG ("strigi.EventListener.Queue.updateEventType",
++ "old event: " + oldEvent->toString())
++ STRIGI_LOG_DEBUG ("strigi.EventListener.Queue.updateEventType",
++ "new event: " + newEvent->toString())*/
++
+ switch (oldEvent->getType())
+ {
+ case Event::CREATED:
+@@ -300,8 +305,8 @@
+ {
+ case Event::CREATED:
+ STRIGI_LOG_INFO("strigi.EventListenerQueue.updateEventType",
++ "CREATED --> CREATED - "
+ "Maybe we've lost some filesystem event");
+-
+ // put in update state, it's the safer solution because with
+ // update event we make a delete and a create
+ oldEvent->setType( Event::UPDATED);
+@@ -328,6 +333,7 @@
+ {
+ case Event::CREATED:
+ STRIGI_LOG_INFO("strigi.EventListenerQueue.updateEventType",
++ "UPDATED --> CREATED - "
+ "Maybe we've lost some filesystem event");
+ // leave UPDATED state, it's the safer solution because with
+ // update event we make a delete and a create
+@@ -357,6 +363,7 @@
+ break;
+ case Event::UPDATED:
+ STRIGI_LOG_INFO("strigi.EventListenerQueue.updateEventType",
++ "DELETED --> UPDATED - "
+ "Maybe we've lost some filesystem event");
+
+ // put in update state, it's the safer solution because with
+@@ -365,6 +372,7 @@
+ break;
+ case Event::DELETED:
+ STRIGI_LOG_INFO("strigi.EventListenerQueue.updateEventType",
++ "DELETED -> DELETED - "
+ "Maybe we've lost some filesystem event");
+ //leave DELETED state
+ break;
+--- /dev/null
++++ b/src/daemon/eventlistener/famlistener.cpp
+@@ -0,0 +1,492 @@
++/* This file is part of Strigi Desktop Search
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++#include "famlistener.h"
++
++#include "analyzerconfiguration.h"
++#include "combinedindexmanager.h"
++#include "event.h"
++#include "eventlistenerqueue.h"
++#include "filelister.h"
++#include "indexreader.h"
++#include "../strigilogging.h"
++
++#include <cerrno>
++#include <sys/resource.h>
++#include <sys/select.h>
++#include <sys/types.h>
++#include <vector>
++
++using namespace std;
++using namespace Strigi;
++
++class MatchString {
++ string m_fixed_val;
++
++ public:
++ MatchString (string fixed_val) {m_fixed_val = fixed_val;}
++ bool operator() (pair<FAMRequest,string> val) {
++ return (m_fixed_val.compare(val.second) == 0 ? true : false);
++ }
++};
++
++bool operator< (const FAMRequest& f1, const FAMRequest& f2)
++{
++ return f1.reqnum < f2.reqnum;
++}
++
++FamEvent::FamEvent (const string& watchName, FAMEvent event)
++ : FsEvent (watchName, event.filename),
++ m_event(event),
++ m_watchName (watchName)
++{
++ bool doStat = true;
++
++ switch (event.code)
++ {
++ case FAMChanged:
++ m_type = UPDATE;
++ break;
++ case FAMMoved:
++ case FAMDeleted:
++ m_type = DELETE;
++ break;
++ case FAMCreated:
++ m_type = CREATE;
++ break;
++ default:
++ STRIGI_LOG_DEBUG ("strigi.FamEvent.FamEvent",
++ "unuseful event");
++ doStat = false;
++ break;
++ }
++
++ if (!doStat)
++ return;
++
++ struct stat status;
++
++ string path = m_watchName;
++ if (path[path.length() - 1] != '/')
++ path += "/";
++ path += event.filename;
++
++ if ( stat( path.c_str(), &status) == 0) {
++ if (S_ISDIR(status.st_mode))
++ m_regardsDir = true;
++ else
++ m_regardsDir = false;
++ }
++ else {
++ string msg;
++ msg = "unable to execute stat() on FAMEvent.filename because of: ";
++ msg += strerror (errno);
++ STRIGI_LOG_ERROR ("strigi.FamEvent", msg)
++ }
++}
++
++char* FamEvent::name()
++{
++ return m_event.filename;
++}
++
++const string FamEvent::description()
++{
++ string message;
++
++ switch (m_event.code)
++ {
++ case FAMChanged:
++ message += "FAMChanged";
++ break;
++ case FAMMoved:
++ message += "FAMMoved";
++ break;
++ case FAMDeleted:
++ message += "FAMDeleted";
++ break;
++ case FAMCreated:
++ message += "FAMCreated";
++ break;
++ case FAMExists:
++ message += "FAMExists";
++ break;
++ case FAMEndExist:
++ message += "FAMEndExist";
++ break;
++ case FAMAcknowledge:
++ message += "FAMAcknowledge";
++ break;
++ case FAMStartExecuting:
++ message += "FAMStartExecuting";
++ break;
++ case FAMStopExecuting:
++ message += "FAMStopExecuting";
++ break;
++ }
++
++ message += " event regarding ";
++ message += (m_regardsDir) ? "dir " : "file ";
++ message += m_event.filename;
++ message += " ; associated watch description: " + m_watchName;
++
++ return message;
++}
++
++bool FamEvent::regardsDir()
++{
++ return m_regardsDir;
++}
++
++class FamListener::Private
++{
++ public:
++ Private();
++
++ virtual ~Private();
++
++ bool init();
++ void stopMonitoring();
++
++ bool isEventInteresting (FsEvent * event);
++ bool isEventValid(FsEvent* event);
++
++ // event methods
++ void pendingEvent(vector<FsEvent*>& events, unsigned int& counter);
++
++ // dir methods
++ void dirRemoved (string dir);
++
++ // watches methods
++ bool addWatch (const std::string& path);
++ void addWatches (const std::set<std::string>& watches);
++ void rmWatch(FAMRequest& famRequest, std::string path);
++ void rmWatches(std::map<FAMRequest, std::string>& watchesToRemove);
++ void rmWatches(std::set<std::string>& watchesToRemove);
++ void clearWatches();
++
++ private:
++ FAMConnection m_famConnection;
++ std::map<FAMRequest, std::string> m_watches; //!< map containing all inotify watches added by FamListener. Key is watch descriptor, value is dir path
++};
++
++bool FamListener::Private::init()
++{
++ if (FAMOpen(&m_famConnection) == -1)
++ return false;
++
++ return true;
++}
++
++FamListener::Private::Private()
++{
++}
++
++FamListener::Private::~Private()
++{
++ clearWatches();
++
++ if (FAMClose (&m_famConnection) == -1)
++ STRIGI_LOG_ERROR ("strigi.FamListener",
++ "Error during FAM close procedure");
++}
++
++void FamListener::Private::pendingEvent(vector<FsEvent*>& events,
++ unsigned int& counter)
++{
++ sleep (1);
++
++ if (FAMPending(&m_famConnection)) {
++ FAMEvent event;
++ if (FAMNextEvent (&m_famConnection, &event) == -1) {
++ STRIGI_LOG_ERROR ("strigi.FamListener.pendingEvent",
++ "Fam event retrieval failed");
++ return;
++ }
++
++ if ((event.code == FAMChanged) || (event.code == FAMMoved) ||
++ (event.code == FAMDeleted) || (event.code == FAMCreated))
++ {
++ map<FAMRequest, string>::iterator match;
++ match = m_watches.find (event.fr);
++ if (match != m_watches.end()) {
++ events.push_back (new FamEvent (match->second, event));
++ counter++;
++ }
++ }
++ }
++}
++
++bool FamListener::Private::isEventValid(FsEvent* event)
++{
++ FamEvent* famEvent = dynamic_cast<FamEvent*> (event);
++
++ if (famEvent == 0)
++ return false;
++
++ map<FAMRequest, string>::iterator match = m_watches.find(famEvent->fr());
++
++ return (match != m_watches.end());
++}
++
++bool FamListener::Private::addWatch (const string& path)
++{
++ map<FAMRequest, string>::iterator iter;
++ for (iter = m_watches.begin(); iter != m_watches.end(); iter++) {
++ if ((iter->second).compare (path) == 0) // dir is already watched
++ return true;
++ }
++
++ FAMRequest famRequest;
++ if (FAMMonitorDirectory (&m_famConnection, path.c_str(), &famRequest, 0) == 0) {
++ m_watches.insert(make_pair(famRequest, path));
++ return true;
++ }
++ else
++ return false;
++}
++
++void FamListener::Private::rmWatch(FAMRequest& famRequest, string path)
++{
++ if (FAMCancelMonitor (&m_famConnection, &famRequest) == -1)
++ STRIGI_LOG_ERROR ("strigi.FamListener.Private.rmWatch",
++ string("Error removing watch associated to path: ") + path)
++ else
++ STRIGI_LOG_DEBUG ("strigi.FamListener.Private.rmWatch",
++ string("Removed watch associated to path: ") + path)
++
++ map<FAMRequest, string>::iterator match = m_watches.find(famRequest);
++
++ if (match != m_watches.end() && (path.compare(match->second) == 0))
++ m_watches.erase (match);
++ else
++ STRIGI_LOG_ERROR ("strigi.FamListener.Private.rmWatch",
++ "unable to remove internal watch reference for " + path)
++}
++
++void FamListener::Private::rmWatches(map<FAMRequest, string>& watchesToRemove)
++{
++ for (map<FAMRequest,string>::iterator it = watchesToRemove.begin();
++ it != watchesToRemove.end(); it++)
++ {
++ map<FAMRequest,string>::iterator match = m_watches.find (it->first);
++ if (match != m_watches.end()) {
++ FAMRequest famRequest = it->first;
++ rmWatch ( famRequest, it->second);
++ }
++ else
++ STRIGI_LOG_WARNING ("strigi.FamListener.Private.rmWatches",
++ "unable to remove watch associated to " + it->second);
++ }
++}
++
++void FamListener::Private::rmWatches(set<string>& watchesToRemove)
++{
++ map<FAMRequest, string> removedWatches;
++
++ // find all pairs <watch-id, watch-name> that have to be removed
++ for (set<string>::iterator it = watchesToRemove.begin();
++ it != watchesToRemove.end(); it++)
++ {
++ MatchString finder (*it);
++ map<FAMRequest, string>::iterator match;
++ match = find_if (m_watches.begin(), m_watches.end(), finder);
++
++ if (match != m_watches.end())
++ removedWatches.insert (make_pair (match->first, match->second));
++ else
++ STRIGI_LOG_WARNING ("strigi.FamListener.Private.rmWatches",
++ "unable to find the watch associated to " + *it)
++ }
++
++ rmWatches (removedWatches);
++}
++
++void FamListener::Private::clearWatches ()
++{
++ map<FAMRequest, string>::iterator iter;
++ for (iter = m_watches.begin(); iter != m_watches.end(); iter++) {
++ FAMRequest famRequest = iter->first;
++ if (FAMCancelMonitor (&m_famConnection, &famRequest) == -1)
++ STRIGI_LOG_ERROR ("strigi.FamListener.rmWatch",
++ string("Error removing watch associated to path: ") + iter->second)
++ else
++ STRIGI_LOG_DEBUG ("strigi.FamListener.rmWatch",
++ string("Removed watch associated to path: ") + iter->second)
++ }
++
++ m_watches.clear();
++}
++
++void FamListener::Private::dirRemoved (string dir)
++{
++ map<FAMRequest, string> watchesToRemove;
++
++ // remove inotify watches over no more indexed dirs
++ for (map<FAMRequest, string>::iterator mi = m_watches.begin();
++ mi != m_watches.end(); mi++)
++ {
++ if ((mi->second).find (dir,0) == 0)
++ watchesToRemove.insert (make_pair (mi->first, mi->second));
++ }
++
++ rmWatches (watchesToRemove);
++}
++
++// END FamListener::Private
++
++FamListener::FamListener(set<string>& indexedDirs)
++ :FsListener("FamListener", indexedDirs)
++{
++ p = new Private();
++}
++
++FamListener::~FamListener()
++{
++ delete p;
++}
++
++bool FamListener::init()
++{
++ if (!p->init()) {
++ STRIGI_LOG_ERROR ("strigi.FamListener.init",
++ "Error during FAM initialization");
++ return false;
++ }
++
++ m_bInitialized = true;
++
++ STRIGI_LOG_DEBUG ("strigi.FamListener.init", "successfully initialized");
++
++ return true;
++}
++
++FsEvent* FamListener:: retrieveEvent()
++{
++ if (m_events.empty())
++ return 0;
++
++ vector<FsEvent*>::iterator iter = m_events.begin();
++
++ FsEvent* event = *iter;
++ m_events.erase(iter);
++ return event;
++}
++
++bool FamListener::pendingEvent()
++{
++ unsigned int counter = 0;
++ p->pendingEvent (m_events, counter);
++
++ if (counter > 0) {
++ char buff [20];
++ snprintf(buff, 20 * sizeof (char), "%i", counter);
++ string message = "Caught ";
++ message += buff;
++ message += " FAM event(s)";
++
++ STRIGI_LOG_DEBUG ("strigi.FamListener.pendingEvent", message)
++ }
++
++ return !m_events.empty();
++}
++
++bool FamListener::isEventInteresting (FsEvent* event)
++{
++ FamEvent* famEvent = dynamic_cast<FamEvent*> (event);
++
++ if (famEvent == 0)
++ return false;
++
++ // ignore directories starting with '.'
++ if ((famEvent->regardsDir()) &&
++ (strlen (famEvent->name()) > 0) && (famEvent->name())[0] == '.')
++ return false;
++
++ if (m_pAnalyzerConfiguration == NULL) {
++ STRIGI_LOG_WARNING ("strigi.FamListener.isEventInteresting",
++ "AnalyzerConfiguration == NULL")
++ return true;
++ }
++
++ string path = famEvent->watchName();
++ if (path[path.length() - 1] != '/')
++ path += "/";
++ path += famEvent->name();
++
++ if (famEvent->regardsDir())
++ return m_pAnalyzerConfiguration->indexDir( path.c_str(),
++ famEvent->name());
++ else
++ return m_pAnalyzerConfiguration->indexFile( path.c_str(),
++ famEvent->name());
++}
++
++bool FamListener::isEventValid(FsEvent* event)
++{
++ return p->isEventValid ( event);
++}
++
++bool FamListener::addWatch (const string& path)
++{
++ if (p->addWatch (path)) {
++ STRIGI_LOG_INFO ("strigi.FamListener.addWatch",
++ "added watch for " + path);
++ return true;
++ }
++ else {
++ STRIGI_LOG_ERROR ("strigi.FamListener.addWatch",
++ "Failed to watch " + path)
++ return false;
++ }
++}
++
++void FamListener::clearWatches ()
++{
++ p->clearWatches();
++}
++
++void FamListener::dirRemoved (string dir, vector<Event*>& events)
++{
++ map<FAMRequest, string> watchesToRemove;
++
++ STRIGI_LOG_DEBUG ("strigi.FamListener.dirRemoved", dir +
++ " is no longer watched, removing all indexed files contained")
++
++ // we've to de-index all files contained into the deleted/moved directory
++ if (m_pManager)
++ {
++ // all indexed files contained into directory
++ map<string, time_t> indexedFiles;
++ m_pManager->indexReader()->getChildren(dir, indexedFiles);
++
++ // remove all entries that were contained into the removed directory
++ for (map<string, time_t>::iterator it = indexedFiles.begin();
++ it != indexedFiles.end(); it++)
++ {
++ Event* event = new Event (Event::DELETED, it->first);
++ events.push_back (event);
++ }
++ }
++ else
++ STRIGI_LOG_WARNING ("strigi.FamListener.dirRemoved",
++ "m_pManager == NULL!");
++
++ p->dirRemoved (dir);
++}
+--- /dev/null
++++ b/src/daemon/eventlistener/famlistener.h
+@@ -0,0 +1,84 @@
++/* This file is part of Strigi Desktop Search
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++#ifndef FAMLISTENER_H
++#define FAMLISTENER_H
++
++#include "fslistener.h"
++#include "strigi_thread.h"
++
++#include <fam.h>
++#include <map>
++#include <vector>
++
++class Event;
++
++class FamEvent : public FsEvent
++{
++ public:
++ FamEvent(const std::string& watchName, FAMEvent event);
++
++ const std::string description();
++ bool regardsDir();
++
++ FAMEvent event() { return m_event;}
++ FAMRequest fr() { return m_event.fr; }
++ std::string watchName() { return m_watchName;}
++ char* name();
++
++ private:
++ struct FAMEvent m_event;
++ std::string m_watchName;
++ bool m_regardsDir;
++};
++
++/*!
++ * @class FamListener
++ * @brief Uses FAM to keep strigi's index updated.
++ */
++class FamListener : public FsListener
++{
++ private:
++ class Private;
++ Private* p;
++
++ public:
++ explicit FamListener(std::set<std::string>& indexedDirs);
++
++ virtual ~FamListener();
++
++ bool init();
++
++ protected:
++ void stopMonitoring();
++
++ // event methods
++ bool pendingEvent();
++ FsEvent* retrieveEvent();
++ bool isEventValid(FsEvent* event);
++ bool isEventInteresting (FsEvent * event);
++
++ void dirRemoved (std::string dir, std::vector<Event*>& events);
++
++ // watches methods
++ bool addWatch (const std::string& path);
++ void clearWatches();
++};
++
++#endif
+--- /dev/null
++++ b/src/daemon/eventlistener/fslistener.cpp
+@@ -0,0 +1,632 @@
++/* This file is part of Strigi Desktop Search
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++#include "fslistener.h"
++
++#include "combinedindexmanager.h"
++#include "event.h"
++#include "eventlistenerqueue.h"
++#include "filelister.h"
++#include "indexreader.h"
++#include "pollinglistener.h"
++#include "../strigilogging.h"
++
++#include <cerrno>
++#include <sys/resource.h>
++#include <sys/select.h>
++#include <sys/types.h>
++#include <sstream>
++#include <vector>
++#include <algorithm>
++
++using namespace std;
++using namespace Strigi;
++
++namespace {
++ /*!
++ * @param path string containing path to check
++ * Removes the terminating char to path.
++ * Under Windows that char is '\', '/' under *nix
++ */
++ string fixPath (string path)
++ {
++ if ( path.c_str() == NULL || path.length() == 0 )
++ return "";
++
++ string temp(path);
++
++ #ifdef HAVE_WINDOWS_H
++ size_t l= temp.length();
++ char* t = (char*)temp.c_str();
++ for (size_t i=0;i<l;i++){
++ if ( t[i] == '\\' )
++ t[i] = '/';
++ }
++ temp[0] = tolower(temp.at(0));
++ #endif
++
++ char separator = '/';
++
++ if (temp[temp.length() - 1 ] == separator)
++ return temp.substr(0, temp.size() - 1);
++
++ return temp;
++ }
++}
++
++void calculateDiff(set<string> actualDirs, set<string> reindexDirs,
++ set<string>& dirsDeleted,set<string>& dirsCreated)
++{
++ set<string>::iterator iter;
++ stringstream strDirsDeleted;
++ stringstream strDirsCreated;
++ stringstream strDirsActual;
++ stringstream strDirsReindex;
++
++ strDirsDeleted << "Dirs deleted:";
++ strDirsCreated << "Dirs created:";
++ strDirsActual << "Actual dirs:";
++ strDirsReindex << "Reindex dirs:";
++
++ dirsCreated.clear();
++ dirsDeleted.clear();
++
++ for (iter = actualDirs.begin(); iter != actualDirs.end(); iter++)
++ strDirsActual << "\n\t-" << *iter;
++
++ for (iter = reindexDirs.begin(); iter != reindexDirs.end(); iter++)
++ strDirsReindex << "\n\t-" << *iter;
++
++ STRIGI_LOG_DEBUG ("strigi.calculateDiff", strDirsActual.str())
++ STRIGI_LOG_DEBUG ("strigi.calculateDiff", strDirsReindex.str())
++
++ for (iter = actualDirs.begin(); iter != actualDirs.end(); iter++) {
++ set<string>::iterator match = reindexDirs.find (*iter);
++ if (match == reindexDirs.end()) {
++ dirsDeleted.insert (*iter);
++ strDirsDeleted << "\n\t-" << *iter;
++ }
++ else
++ reindexDirs.erase (match);
++ }
++
++ for (iter = reindexDirs.begin(); iter != reindexDirs.end(); iter++) {
++ dirsCreated.insert (*iter);
++ strDirsCreated << "\n\t-" << *iter;
++ }
++
++ STRIGI_LOG_DEBUG ("strigi.calculateDiff", strDirsCreated.str())
++ STRIGI_LOG_DEBUG ("strigi.calculateDiff", strDirsDeleted.str())
++}
++
++class File
++{
++ public:
++ File(string name, time_t mtime)
++ : m_name (name),
++ m_mtime (mtime)
++ {};
++
++ string m_name;
++ time_t m_mtime;
++};
++
++bool operator< (const File& f1, const File& f2)
++{
++ if (f1.m_name.compare(f2.m_name) < 0)
++ return true;
++ else
++ return false;
++}
++
++class MatchFile
++{
++ string m_name;
++ public:
++ MatchFile( string name)
++ : m_name (name) {};
++
++ bool operator ()(const File& f)
++ { return (m_name == f.m_name); }
++};
++
++FsEvent::FsEvent (const string path, const string name)
++{
++ m_file = fixPath(path);
++ if (!name.empty()) {
++ m_file += '/';
++ m_file += name;
++ }
++}
++
++// improved multimap, stl multimap class doesn't provide a method that returns
++// all the available keys
++typedef map< string, set<File> > iMultimap;
++
++
++FsListener::FsListener(const char* name, set<string>& indexedDirs)
++ : EventListener(name)
++{
++ m_bMonitor = true;
++ setState(Idling);
++ m_bInitialized = false;
++ m_bBootstrapped = false;
++ m_bReindexReq = false;
++ m_counter = 0;
++ m_pollingListener = 0;
++
++ for (set<string>::iterator iter = indexedDirs.begin();
++ iter != indexedDirs.end(); iter++)
++ m_indexedDirs.insert (fixPath (*iter));
++
++ STRIGI_MUTEX_INIT (&m_reindexLock);
++}
++
++FsListener::~FsListener()
++{
++ if (m_pollingListener) {
++ m_pollingListener->stop();
++ delete m_pollingListener;
++ m_pollingListener = 0;
++ }
++ STRIGI_MUTEX_DESTROY (&m_reindexLock);
++}
++
++void* FsListener::run(void*)
++{
++ while (getState() != Stopping) {
++ if (!m_bBootstrapped)
++ bootstrap();
++ else if (reindexReq())
++ reindex();
++ else
++ watch();
++
++ if (getState() == Working)
++ setState(Idling);
++ }
++
++ STRIGI_LOG_DEBUG ("strigi.FsListener.run",
++ string("exit state: ") + getStringState());
++ return 0;
++}
++
++bool FsListener::reindexReq()
++{
++ bool ret;
++
++ STRIGI_MUTEX_LOCK (&m_reindexLock);
++ ret = m_bReindexReq;
++ STRIGI_MUTEX_UNLOCK (&m_reindexLock);
++
++ return ret;
++}
++
++void FsListener::getReindexDirs(set<string>& reindexDirs)
++{
++ STRIGI_MUTEX_LOCK (&m_reindexLock);
++ m_bReindexReq = false;
++ reindexDirs = m_reindexDirs;
++ m_reindexDirs.clear();
++ STRIGI_MUTEX_UNLOCK (&m_reindexLock);
++}
++
++void FsListener::bootstrap()
++{
++ STRIGI_LOG_DEBUG ("strigi.FsListener.bootstrap","BOOTSTRAP INIT");
++
++ set<string> toWatch;
++ vector<Event*> events;
++ iMultimap files;
++ set<string> bootstrapDirs;
++
++ if (reindexReq())
++ getReindexDirs ( bootstrapDirs);
++ else
++ bootstrapDirs = m_indexedDirs;
++
++ for (set<string>::iterator iter = bootstrapDirs.begin();
++ iter != bootstrapDirs.end(); iter++)
++ {
++ toWatch.insert (*iter);
++
++ DirLister lister(m_pAnalyzerConfiguration);
++ string path;
++ vector<pair<string, struct stat> > dirs;
++
++ lister.startListing (*iter);
++ int ret = lister.nextDir(path, dirs);
++
++ while (ret != -1) {
++ if (reindexReq())
++ break;
++
++ vector<pair<string, struct stat> >::iterator iter;
++
++ for (iter = dirs.begin(); iter != dirs.end(); iter++) {
++ struct stat stats = iter->second;
++
++ if (S_ISDIR(stats.st_mode)) { //dir
++ toWatch.insert (iter->first);
++ }
++ else if (S_ISREG(stats.st_mode)) { //file
++ iMultimap::iterator mIter = files.find (path);
++ File file (iter->first, stats.st_mtime);
++ if (mIter != files.end())
++ (mIter->second).insert (file);
++ else {
++ set<File> temp;
++ temp.insert (file);
++ files.insert(make_pair(path, temp));
++ }
++ }
++ }
++
++ ret = lister.nextDir(path, dirs);
++ }
++ }
++
++ stringstream msg;
++ msg << "there're " << files.size();
++ msg << " keys inside iMultimap";
++ STRIGI_LOG_DEBUG ("strigi.FsListener.bootstrap", msg.str())
++
++ for (iMultimap::iterator iter = files.begin(); iter != files.end(); iter++)
++ {
++ map <string, time_t> indexedFiles;
++ stringstream msg;
++ string path = iter->first;
++
++ // retrieve all indexed files contained by path
++ m_pManager->indexReader()->getChildren (path, indexedFiles);
++ msg << "there're " << indexedFiles.size();
++ msg << " indexed files associated to dir " << path;
++
++ STRIGI_LOG_DEBUG ("strigi.FsListener.bootstrap", msg.str())
++
++ if (indexedFiles.empty()) {
++ string temp = path + "/";
++ msg.str("");
++ m_pManager->indexReader()->getChildren (temp, indexedFiles);
++ msg << "there're " << indexedFiles.size();
++ msg << " indexed files associated to dir " << temp;
++
++ STRIGI_LOG_DEBUG ("strigi.FsListener.bootstrap", msg.str())
++ }
++
++ if (!indexedFiles.empty()) {
++ // find differences between fs files and indexed ones
++ map<string, time_t>::iterator it = indexedFiles.begin();
++ for ( ; it != indexedFiles.end(); it++) {
++ MatchFile finder (it->first);
++ set<File>::iterator match;
++ match = find_if( (iter->second).begin(),
++ (iter->second).end(), finder);
++
++ if (match == (iter->second).end()) {
++ // indexed file has been deleted from filesystem
++ events.push_back (new Event (Event::DELETED, iter->first));
++ }
++ else if ((*match).m_mtime > it->second) {
++ // file has been updated
++ events.push_back (new Event (Event::UPDATED, iter->first));
++ }
++ else {
++ // file has not been modified since index time
++ (iter->second).erase( match);
++ }
++
++ if (reindexReq())
++ break;
++ }
++ }
++
++ for (set<File>::iterator it = (iter->second).begin();
++ it != (iter->second).end(); it++)
++ {
++ File file = *it;
++ events.push_back (new Event (Event::CREATED, file.m_name));
++ }
++ }
++
++ //TODO: check
++ if (reindexReq()) {
++ for (vector<Event*>::iterator iter = events.begin();
++ iter != events.end(); iter++) {
++ delete *iter;
++ }
++
++ events.clear();
++ }
++ else {
++ if (events.size() > 0)
++ m_pEventQueue->addEvents (events);
++
++ addWatches (toWatch);
++
++ m_bBootstrapped = true;
++ m_indexedDirs = bootstrapDirs;
++ }
++}
++
++void FsListener::reindex()
++{
++ STRIGI_LOG_DEBUG ("strigi.FsListener.reindex","REINDEX INIT");
++
++ if (m_pEventQueue == NULL) {
++ STRIGI_LOG_ERROR ("strigi.FsListener.reindex",
++ "m_pEventQueue == NULL!");
++ return;
++ }
++
++ set<string> reindexDirs;
++ set<string> dirsDeleted;
++ set<string> dirsCreated;
++ set<string> dirsMonitored;
++ vector<Event*> events;
++
++ if (!reindexReq ())
++ return;
++
++ getReindexDirs(reindexDirs);
++
++ calculateDiff(m_indexedDirs, reindexDirs, dirsDeleted, dirsCreated);
++
++ for (set<string>::iterator iter = dirsCreated.begin();
++ iter != dirsCreated.end() && !reindexReq(); iter++)
++ {
++ DirLister lister(m_pAnalyzerConfiguration);
++ string path;
++ vector<pair<string, struct stat> > dirs;
++
++ lister.startListing (*iter);
++ int ret = lister.nextDir(path, dirs);
++
++ while (ret != -1) {
++ vector<pair<string, struct stat> >::iterator iter;
++ for (iter = dirs.begin(); iter != dirs.end(); iter++) {
++ struct stat stats = iter->second;
++ if (S_ISDIR(stats.st_mode)) {//dir
++ set<string> toWatch;
++ recursivelyMonitor (iter->first, toWatch, events);
++ // add new watches
++ addWatches (toWatch);
++ dirsMonitored.insert (iter->first);
++ }
++ else if (S_ISREG(stats.st_mode)) {
++ //file
++ events.push_back (new Event (Event::CREATED, iter->first));
++ }
++ }
++ ret = lister.nextDir(path, dirs);
++ }
++ }
++
++ if (reindexReq()) {
++ // another reindex request arrived, undo last actions
++ for (vector<Event*>::iterator iter = events.begin();
++ iter != events.end(); iter++)
++ delete *iter;
++ events.clear();
++ //TODO check!!!
++ dirsRemoved (dirsMonitored, events);
++ }
++ else {
++ // finish reindex operation
++ dirsRemoved (dirsDeleted, events);
++
++ if (events.size() > 0)
++ m_pEventQueue->addEvents (events);
++
++ //update indexedDirs
++ m_indexedDirs = reindexDirs;
++ }
++}
++
++void FsListener::watch ()
++{
++ if (m_pEventQueue == NULL) {
++ STRIGI_LOG_ERROR ("strigi.FsListener.watch",
++ "m_pEventQueue == NULL!");
++ return;
++ }
++
++ vector <Event*> events;
++
++ while (pendingEvent()) {
++ FsEvent* fsevent = retrieveEvent();
++
++ if (!isEventValid(fsevent)) {
++ STRIGI_LOG_WARNING ("strigi.FsListener.watch",
++ "discarded invalid event");
++ continue;
++ }
++
++ if (isEventInteresting(fsevent)) {
++ STRIGI_LOG_DEBUG ("strigi.FsListener.watch",
++ fsevent->description());
++
++ switch (fsevent->type()) {
++ case FsEvent::UPDATE:
++ {
++ Event* event = new Event (Event::UPDATED, fsevent->file());
++ events.push_back (event);
++ break;
++ }
++ case FsEvent::DELETE:
++ {
++ if (fsevent->regardsDir())
++ dirRemoved (fsevent->file(), events);
++ else {
++ Event* event = new Event (Event::DELETED,
++ fsevent->file());
++ events.push_back (event);
++ }
++ break;
++ }
++ case FsEvent::CREATE:
++ {
++ if (fsevent->regardsDir()) {
++ set<string> toWatch;
++ recursivelyMonitor (fsevent->file(), toWatch, events);
++ addWatches( toWatch);
++ }
++ else {
++ Event* event = new Event (Event::CREATED,
++ fsevent->file());
++ events.push_back (event);
++ }
++ break;
++ }
++ default:
++ STRIGI_LOG_DEBUG ("strigi.FsListener.watch",
++ "unknown event type")
++ break;
++ }
++
++ delete fsevent;
++ }
++ }
++
++ if (events.size() > 0) {
++ STRIGI_LOG_DEBUG ("strigi.FsListener.watch",
++ "adding events to processing queue")
++ m_pEventQueue->addEvents (events);
++ }
++}
++
++void FsListener::recursivelyMonitor (string dir, set<string>& toWatch,
++ vector<Event*>& events)
++{
++ STRIGI_LOG_DEBUG ("FsListener.recursivelyMonitor","going to monitor " + dir)
++ DirLister lister(m_pAnalyzerConfiguration);
++ string path;
++ vector<pair<string, struct stat> > fsitems;
++
++ toWatch.insert (dir);
++
++ lister.startListing (dir);
++ int ret = lister.nextDir(path, fsitems);
++
++ while (ret != -1) {
++ for (vector<pair<string,struct stat> >::iterator iter = fsitems.begin();
++ iter != fsitems.end(); iter++)
++ {
++ struct stat stats = iter->second;
++
++ if (S_ISDIR(stats.st_mode)) //dir
++ recursivelyMonitor(iter->first, toWatch, events);
++ else if (S_ISREG(stats.st_mode)) {
++ //file
++ Event* event = new Event (Event::CREATED, iter->first);
++ events.push_back (event);
++ }
++ }
++ ret = lister.nextDir(path, fsitems);
++ }
++}
++
++void FsListener::addWatches(const set<string> &watches)
++{
++ set<string>::iterator iter;
++ set<string> toPool;
++ set<string> watched;
++
++ if (!m_bInitialized) {
++ STRIGI_LOG_ERROR ("strigi.FsListener.addWatches",
++ "Listener has not been initialized, unable"
++ "to add watches!")
++ toPool = watches;
++ }
++ else {
++ for (iter = watches.begin(); iter != watches.end(); iter++) {
++ if (!addWatch (*iter)) {
++ // adding watch failed, we've to use polling in order to watch it
++ toPool.insert(*iter);
++ }
++ else
++ watched.insert (*iter);
++ }
++ }
++
++ if (!toPool.empty())
++ {
++ if (m_pollingListener == NULL)
++ {
++ m_pollingListener = new PollingListener();
++ m_pollingListener->setEventListenerQueue( m_pEventQueue);
++ m_pollingListener->setCombinedIndexManager( m_pManager);
++ m_pollingListener->setIndexerConfiguration(m_pAnalyzerConfiguration);
++ //TODO: start with a low priority?
++ m_pollingListener->start( );
++ }
++
++ m_pollingListener->addWatches( toPool);
++ m_pollingDirs.insert (toPool.begin(), toPool.end());
++ }
++}
++
++void FsListener::dirsRemoved (set<string> dirs, vector<Event*>& events)
++{
++ for (set<string>::iterator iter = dirs.begin();
++ iter != dirs.end(); iter++)
++ {
++ string path = fixPath(*iter);
++ set<string>::iterator match = m_pollingDirs.find (path);
++ if (match == m_pollingDirs.end())
++ dirRemoved (path, events);
++ else
++ m_pollingListener->rmWatch (*match);
++ }
++}
++
++void FsListener::setIndexedDirectories (const set<string> &dirs)
++{
++ stringstream msg;
++ set<string> fixedDirs;
++
++ // fix path, all dir must end with a '/'
++ for (set<string>::iterator iter = dirs.begin(); iter != dirs.end(); iter++)
++ fixedDirs.insert (fixPath (*iter));
++
++ STRIGI_MUTEX_LOCK (&m_reindexLock);
++ m_reindexDirs = fixedDirs;
++ m_bReindexReq = true;
++ STRIGI_MUTEX_UNLOCK (&m_reindexLock);
++
++
++ msg << fixedDirs.size() << " dirs specified";
++ STRIGI_LOG_DEBUG ("FsListener.setIndexedDirectories", msg.str())
++}
++
++void FsListener::dumpEvents()
++{
++ unsigned int counter = 1;
++ for (vector<FsEvent*>::iterator iter = m_events.begin();
++ iter != m_events.end(); iter++)
++ {
++ FsEvent* event = *iter;
++ stringstream msg;
++
++ msg << "Event " << counter << "/" << m_events.size() << ": ";
++ msg << event->description();
++ STRIGI_LOG_DEBUG("strigi.FsListener.dumpEvents", msg.str())
++ counter++;
++ }
++}
+--- /dev/null
++++ b/src/daemon/eventlistener/fslistener.h
+@@ -0,0 +1,153 @@
++/* This file is part of Strigi Desktop Search
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++#ifndef FSLISTENER_H
++#define FSLISTENER_H
++
++#include "eventlistener.h"
++#include "strigi_thread.h"
++#include <map>
++#include <vector>
++
++class Event;
++class PollingListener;
++
++
++class FsEvent
++{
++ public:
++ FsEvent (const std::string path, const std::string name);
++ virtual ~FsEvent() {};
++
++ enum TYPE {CREATE, UPDATE, DELETE};
++
++ virtual const std::string description() = 0;
++ TYPE type() { return m_type; }
++ const std::string file() { return m_file;}
++ virtual bool regardsDir() = 0;
++
++ protected:
++ std::string m_file;
++ TYPE m_type;
++};
++
++
++/*!
++ * @class FsListener
++ * @brief Interacts generic class for low level monitoring facilities
++ */
++
++class FsListener : public EventListener
++{
++ public:
++ explicit FsListener(const char* name,
++ std::set<std::string>& indexedDirs);
++
++ virtual ~FsListener();
++
++ virtual bool init() = 0;
++
++ void setIndexedDirectories (const std::set<std::string>& dirs);
++
++ void* run(void*);
++
++ protected:
++ void setInitialized () { m_bInitialized = true; }
++
++ /*!
++ * @param event the filesystem event to analyze
++ * returns true if event is to process (ergo is interesting), false otherwise
++ */
++ virtual bool isEventInteresting (FsEvent * event) = 0;
++
++ /*!
++ * main FsListener thread
++ */
++ void watch ();
++
++ void bootstrap();
++ void reindex();
++ bool reindexReq();
++ void getReindexDirs(std::set<std::string>&);
++
++ // event methods
++ virtual bool pendingEvent() = 0;
++ virtual FsEvent* retrieveEvent() = 0;
++ virtual bool isEventValid(FsEvent* event) = 0;
++ void dumpEvents();
++
++ // dir methods
++
++ /*!
++ * @param dir removed dir
++ * Removes all db entries of files contained into the removed dir.
++ * Removes also all watches related to removed dir (including watches over subdirs)
++ */
++ virtual void dirRemoved (std::string dir,
++ std::vector<Event*>& events) = 0;
++
++ /*!
++ * @param dirs removed dirs
++ * @param events all generated events
++ * Removes all db entries of files contained into the removed dirs.
++ */
++ void dirsRemoved (std::set<std::string> dirs,
++ std::vector<Event*>& events);
++
++ /*!
++ * @param dir dir to monitor
++ * @param toWatch directories to be watched
++ * @param events all generated events
++ * Index all files contained inside the directory dir, and all its subdirs
++ */
++ void recursivelyMonitor (const std::string dir,
++ std::set<std::string>& toWatch,
++ std::vector<Event*>& events);
++
++ // watches methods
++
++ /*!
++ * @param watches directories to be watched
++ * Add a watch for each directory specified
++ */
++ void addWatches (const std::set<std::string>& watches);
++ virtual bool addWatch (const std::string& path) = 0;
++
++ /*!
++ * removes and release all watches
++ */
++ virtual void clearWatches() {};
++
++ bool m_bMonitor;
++ bool m_bInitialized;
++ bool m_bBootstrapped;
++ unsigned int m_counter;
++
++ std::set<std::string> m_indexedDirs;
++ std::set<std::string> m_pollingDirs;
++
++ bool m_bReindexReq;
++ std::set<std::string> m_reindexDirs;
++ pthread_mutex_t m_reindexLock;
++ std::vector<FsEvent*> m_events;
++
++ PollingListener* m_pollingListener;
++};
++
++#endif
+--- a/src/daemon/eventlistener/inotifylistener.cpp
++++ b/src/daemon/eventlistener/inotifylistener.cpp
+@@ -18,8 +18,8 @@
+ * Boston, MA 02110-1301, USA.
+ */
+ #include "inotifylistener.h"
+-#include "pollinglistener.h"
+
++#include "analyzerconfiguration.h"
+ #include "combinedindexmanager.h"
+ #include "event.h"
+ #include "eventlistenerqueue.h"
+@@ -28,10 +28,12 @@
+ #include "../strigilogging.h"
+
+ #include <cerrno>
++#include <cstring>
+ #include <sys/resource.h>
+ #include <sys/select.h>
+ #include <sys/types.h>
+ #include <vector>
++#include <algorithm>
+
+ #include "local_inotify.h"
+ #include "local_inotify-syscalls.h"
+@@ -39,38 +41,6 @@
+ using namespace std;
+ using namespace Strigi;
+
+-namespace {
+- /*!
+- * @param path string containing path to check
+- * Appends the terminating char to path.
+- * Under Windows that char is '\', '/' under *nix
+- */
+- string fixPath (string path)
+- {
+- if ( path.c_str() == NULL || path.length() == 0 )
+- return "";
+-
+- string temp(path);
+-
+- #ifdef HAVE_WINDOWS_H
+- size_t l= temp.length();
+- char* t = (char*)temp.c_str();
+- for (size_t i=0;i<l;i++){
+- if ( t[i] == '\\' )
+- t[i] = '/';
+- }
+- temp[0] = tolower(temp.at(0));
+- #endif
+-
+- char separator = '/';
+-
+- if (temp[temp.length() - 1 ] != separator)
+- temp += separator;
+-
+- return temp;
+- }
+-}
+-
+ class MatchString {
+ string m_fixed_val;
+
+@@ -81,597 +51,147 @@
+ }
+ };
+
+-////////////////////////////////////////////
+-// ReindexDirsThread class implementation //
+-///////////////////////////////////////////
+-
+-/*!
+- * @class ReindexDirsThread
+- * @brief Simple thread called when user changes indexed dirs
+- *
+- * It's a separate thread that takes care of updating inotify watches and indexed files according to the new directories specified by the user
+-*/
+-class InotifyListener::ReindexDirsThread : public StrigiThread
+-{
+- //friend class InotifyListener;
+-
+- public:
+- explicit ReindexDirsThread (const char* name,
+- const std::set<std::string> &olddirs);
+-
+- explicit ReindexDirsThread (const char* name);
+-
+- ~ReindexDirsThread();
+-
+- void* run(void*);
+-
+- void setIndexerConfiguration(Strigi::AnalyzerConfiguration* ic) {
+- m_pindexerconfiguration = ic;
+- }
+-
+- void setIndexedDirs (const std::set<std::string>& dirs,
+- const std::map<int, std::string>& watchedDirs);
+-
+- void setCombinedIndexManager (CombinedIndexManager* m) {
+- m_pManager = m;
+- }
+-
+- void getData(std::set<std::string>& noMoreIndexed,
+- std::set<std::string>& toWatch,
+- std::set<std::string>& newDirs,
+- std::vector<Event*>& events);
+-
+- bool working();
+-
+- protected:
+- void cleanup();
+- void reindex();
+-
+- void interrupt ();
+- bool testInterrupt();
+- void setWorking (bool value);
+-
+- CombinedIndexManager* m_pManager;
+- Strigi::AnalyzerConfiguration* m_pindexerconfiguration;
+- std::map<std::string, time_t> m_toIndex; //!< new files to index
+- std::set<std::string> m_toWatch; //!< new directories to watch
+- std::vector<Event*> m_events;
+- std::set<std::string> m_newDirs; //!< new indexed dirs specified by the user
+- std::set<std::string> m_oldDirs; //!< old indexed dirs
+- std::set<std::string> m_nextJobDirs; //!< new dirs to index, user changed indexed dirs more than one time
+- std::map<int, std::string> m_nextJobWatchedDirs;
+- std::set<std::string> m_nomoreIndexedDirs; //!< dirs no more indexed
+- pthread_mutex_t m_nextJobLock;//!< mutex lock over m_nextJobDirs
+- pthread_mutex_t m_resourcesLock; //!< mutex lock over all variables (excluding m_nextJobDirs)
+- pthread_mutex_t m_interruptLock;
+- pthread_mutex_t m_workingLock;
+- bool m_bWorking;
+- bool m_bInterrupt;
+- bool m_bInterrupted;
+- bool m_bDataTaken;
+- bool m_bHasWorkTodo;
+-};
+-
+-InotifyListener::ReindexDirsThread::ReindexDirsThread (const char* name,
+- const std::set<std::string> &olddirs)
+-: StrigiThread (name) {
+- m_pManager = NULL;
+- m_pindexerconfiguration = NULL;
+- m_oldDirs = olddirs;
+- m_bInterrupt = false;
+- m_bInterrupted = false;
+- m_bDataTaken = true;
+- m_bHasWorkTodo = false;
+- m_bWorking = false;
+- STRIGI_MUTEX_INIT (&m_nextJobLock);
+- STRIGI_MUTEX_INIT (&m_resourcesLock);
+- STRIGI_MUTEX_INIT (&m_interruptLock);
+- STRIGI_MUTEX_INIT (&m_workingLock);
+-}
+-
+-InotifyListener::ReindexDirsThread::ReindexDirsThread (const char* name)
+-: StrigiThread (name) {
+- m_pManager = NULL;
+- m_pindexerconfiguration = NULL;
+- m_newDirs.clear();
+- m_oldDirs.clear();
+- m_bInterrupt = false;
+- m_bInterrupted = false;
+- m_bDataTaken = true;
+- m_bHasWorkTodo = false;
+- m_bWorking = false;
+- STRIGI_MUTEX_INIT (&m_nextJobLock);
+- STRIGI_MUTEX_INIT (&m_resourcesLock);
+- STRIGI_MUTEX_INIT (&m_interruptLock);
+- STRIGI_MUTEX_INIT (&m_workingLock);
+-}
+-
+-void InotifyListener::ReindexDirsThread::cleanup() {
+- STRIGI_LOG_DEBUG( "strigi.ReindexDirsThread.cleanup",
+- "reindexing cancelled");
+-
+- //clean-up everything
+- m_newDirs.clear();
+- m_toWatch.clear();
+- m_toIndex.clear();
+-
+- // free some memory
+- for (vector<Event*>::iterator iter = m_events.begin();
+- iter != m_events.end(); iter++)
+- {
+- if (*iter != NULL) {
+- delete *iter;
+- *iter = NULL;
+- }
+- }
+- m_events.clear();
+-
+- m_bDataTaken = true;
+- m_bInterrupt = false;
+- m_bInterrupted = true;
+- setWorking (false);
+-
+- STRIGI_MUTEX_UNLOCK (&m_resourcesLock);
++InotifyEvent::InotifyEvent (int watchID, const string& watchName,
++ struct inotify_event* event)
++ : FsEvent (watchName, event->name),
++ m_event(event),
++ m_watchName (watchName),
++ m_watchID (watchID)
++{
++ if ( ((IN_MODIFY & m_event->mask) != 0) ||
++ ((IN_CLOSE_WRITE & m_event->mask) != 0) )
++ m_type = UPDATE;
++ else if ( ((IN_DELETE & m_event->mask) != 0) ||
++ ((IN_MOVED_FROM & m_event->mask) != 0 ) ||
++ ((IN_DELETE_SELF & m_event->mask) != 0 ) ||
++ ((IN_MOVE_SELF & m_event->mask) != 0 ))
++ m_type = DELETE;
++ else if ( ((IN_CREATE & m_event->mask) != 0) ||
++ ((IN_MOVED_TO & m_event->mask) != 0 ) )
++ m_type = CREATE;
++ else
++ STRIGI_LOG_DEBUG ("strigi.InotifyEvent.InotifyEvent",
++ "inotify's unknown event");
+ }
+
+-InotifyListener::ReindexDirsThread::~ReindexDirsThread()
++char* InotifyEvent::name()
+ {
+- for (unsigned int i = 0; i < m_events.size(); i++)
+- delete m_events[i];
+-
+- m_events.clear();
+-
+- STRIGI_MUTEX_DESTROY(&m_nextJobLock);
+- STRIGI_MUTEX_DESTROY(&m_resourcesLock);
+- STRIGI_MUTEX_DESTROY(&m_interruptLock);
+- STRIGI_MUTEX_DESTROY(&m_workingLock);
++ return m_event->name;
+ }
+
+-void InotifyListener::ReindexDirsThread::interrupt () {
+- STRIGI_MUTEX_LOCK(&m_interruptLock);
+- m_bInterrupt = true;
+- STRIGI_MUTEX_UNLOCK(&m_interruptLock);
+-}
++const string InotifyEvent::description()
++{
++ string message;
+
+-bool InotifyListener::ReindexDirsThread::testInterrupt() {
+- bool value;
+- STRIGI_MUTEX_LOCK(&m_interruptLock);
+- value = m_bInterrupt;
+- STRIGI_MUTEX_UNLOCK(&m_interruptLock);
+-
+- if (value)
+- STRIGI_LOG_DEBUG ("strigi.ReindexDirsThread.testInterrupt",
+- "Caught interrupt event");
+-
+- return value;
+-}
++ if ( (IN_ACCESS & m_event->mask) != 0 )
++ message += "ACCESS";
++ else if ( (IN_MODIFY & m_event->mask) != 0 )
++ message += "MODIFY";
++ else if ( (IN_ATTRIB & m_event->mask) != 0 )
++ message += "ATTRIB";
++ else if ( (IN_CLOSE_WRITE & m_event->mask) != 0 )
++ message += "CLOSE_WRITE";
++ else if ( (IN_CLOSE_NOWRITE & m_event->mask) != 0 )
++ message += "CLOSE_NOWRITE";
++ else if ( (IN_OPEN & m_event->mask) != 0 )
++ message += "OPEN";
++ else if ( (IN_MOVED_FROM & m_event->mask) != 0 )
++ message += "MOVED_FROM";
++ else if ( (IN_MOVED_TO & m_event->mask) != 0 )
++ message += "MOVED_TO";
++ else if ( (IN_CREATE & m_event->mask) != 0 )
++ message += "CREATE";
++ else if ( (IN_DELETE & m_event->mask) != 0 )
++ message += "DELETE";
++ else if ( (IN_DELETE_SELF & m_event->mask) != 0 )
++ message += "DELETE_SELF";
++ else if ( (IN_UNMOUNT & m_event->mask) != 0 )
++ message += "UNMOUNT";
++ else if ( (IN_Q_OVERFLOW & m_event->mask) != 0 )
++ message += " Q_OVERFLOW";
++ else if ( (IN_IGNORED & m_event->mask) != 0 )
++ message += " IGNORED";
++ else if ( (IN_CLOSE & m_event->mask) != 0 )
++ message += "CLOSE";
++ else if ( (IN_MOVE & m_event->mask) != 0 )
++ message += "MOVE";
++ else if ( (IN_ISDIR & m_event->mask) != 0 )
++ message += " ISDIR";
++ else if ( (IN_ONESHOT & m_event->mask) != 0 )
++ message += " ONESHOT";
++ else
++ message = "UNKNOWN";
+
+-void InotifyListener::ReindexDirsThread::getData(set<string>& noMoreIndexed,
+- set<string>& toWatch,
+- set<string>& newDirs,
+- vector<Event*>& events)
+-{
+- // assure all arrays are empty
+- noMoreIndexed.clear();
+- toWatch.clear();
+- newDirs.clear();
+- events.clear();
+-
+- if (STRIGI_MUTEX_TRY_LOCK (&m_resourcesLock) != 0)
+- return;
++ message += " event regarding ";
++ message += string(m_event->name, m_event->len);
++ message += " ; associated watch description: " + m_watchName;
+
+- if (m_bDataTaken) {
+- STRIGI_MUTEX_UNLOCK (&m_resourcesLock);
+- return;
+- }
+-
+- noMoreIndexed = m_nomoreIndexedDirs;
+- toWatch = m_toWatch;
+- events = m_events;
+- newDirs = m_newDirs;
+-
+- m_toWatch.clear();
+- m_nomoreIndexedDirs.clear();
+- m_events.clear();
+-
+- m_bDataTaken = true;
+-
+- STRIGI_MUTEX_UNLOCK (&m_resourcesLock);
+-
+- char buffer [50];
+- snprintf (buffer, 50 * sizeof (char), "%i", noMoreIndexed.size());
+- STRIGI_LOG_DEBUG ("strigi.ReindexDirsThread.getData",
+- string("dirs no more indexed: ") + buffer);
+- snprintf (buffer, 50 * sizeof (char), "%i", toWatch.size());
+- STRIGI_LOG_DEBUG ("strigi.ReindexDirsThread.getData",
+- string("dirs to watch: ") + buffer);
+- snprintf (buffer, 50 * sizeof (char), "%i", newDirs.size());
+- STRIGI_LOG_DEBUG ("strigi.ReindexDirsThread.getData",
+- string("dirs selected by the user: ") + buffer);
+- snprintf (buffer, 50 * sizeof (char), "%i", events.size());
+- STRIGI_LOG_DEBUG ("strigi.ReindexDirsThread.getData",
+- string("events to process: ") + buffer);
++ return message;
+ }
+
+-void* InotifyListener::ReindexDirsThread::run(void*)
++bool InotifyEvent::regardsDir()
+ {
+- STRIGI_LOG_DEBUG ("strigi.ReindexDirsThread.run","starting");
+-
+- while (getState() != Stopping)
+- {
+- //TODO: increase value
+- sleep (1);
+-
+- if (STRIGI_MUTEX_TRY_LOCK (&m_nextJobLock) == 0) {
+- if ((m_bHasWorkTodo)
+- && STRIGI_MUTEX_TRY_LOCK (&m_resourcesLock) == 0) {
+- // there's work to do, we've acquired locks over m_nextJobLock
+- // and m_resourcesLock
+-
+- if (!m_bDataTaken) {
+- // previous reindex() run data has to be taken
+- STRIGI_MUTEX_UNLOCK (&m_nextJobLock);
+- STRIGI_MUTEX_UNLOCK (&m_resourcesLock);
+- continue;
+- }
+-
+- setWorking (true);
+-
+- if (!m_bInterrupted)
+- m_oldDirs = m_newDirs;
+-
+- m_newDirs = m_nextJobDirs;
+- m_nextJobDirs.clear();
+- m_bHasWorkTodo = false;
+- STRIGI_MUTEX_UNLOCK (&m_nextJobLock);
+-
+- // clear old structures
+- m_toWatch.clear();
+- m_toIndex.clear();
+- m_events.clear();
+- m_nomoreIndexedDirs.clear();
+-
+- if (m_newDirs == m_oldDirs) {
+- STRIGI_MUTEX_UNLOCK (&m_resourcesLock);
+- setWorking (false);
+- continue;
+- }
+-
+- m_bDataTaken = false;
+-
+- reindex();
+-
+- setWorking (false);
+-
+- STRIGI_MUTEX_UNLOCK (&m_resourcesLock);
+- }
+- else
+- STRIGI_MUTEX_UNLOCK (&m_nextJobLock);
+- }
+-
+- if (getState() == Working)
+- setState(Idling);
+- }
+-
+- STRIGI_LOG_DEBUG ("strigi.ReindexDirsThread.run",
+- string("exit state: ") + getStringState());
+- return 0;
++ return ((IN_ISDIR & m_event->mask) != 0);
+ }
+
+-void InotifyListener::ReindexDirsThread::reindex () {
+- if (!m_pManager) {
+- STRIGI_LOG_ERROR ("strigi.ReindexDirsThread.reindex",
+- "m_pManager == NULL!");
+- return;
+- }
+-
+- m_bInterrupted = false;
+-
+- map<int, string> watchedDirs = m_nextJobWatchedDirs;
+-
+- if (testInterrupt()) {
+- cleanup();
+- return;
+- }
+-
+- m_toWatch.clear();
+- m_toIndex.clear();
+-
+- for (set<string>::iterator iter = m_newDirs.begin();
+- iter != m_newDirs.end(); iter++)
+- {
+- DirLister lister(m_pindexerconfiguration);
+- string path;
+- vector<pair<string, struct stat> > dirs;
+-
+- lister.startListing (*iter);
+- int ret = lister.nextDir(path, dirs);
+-
+- while (ret != -1) {
+- for (vector<pair<string, struct stat> >::iterator iter = dirs.begin();
+- iter != dirs.end(); iter++)
+- {
+- struct stat stats = iter->second;
+-
+- if (S_ISDIR(stats.st_mode)) {
+- //dir
+- m_toWatch.insert (iter->first);
+- }
+- else if (S_ISREG(stats.st_mode)) {
+- //file
+- m_events.push_back (new Event (Event::CREATED, iter->first));
+- }
+- }
+- ret = lister.nextDir(path, dirs);
+- }
+- }
+-
+- m_nomoreIndexedDirs.clear();
+- set<string> alreadyWatched;
+-
+- for (map<int, string>::iterator it = watchedDirs.begin();
+- it != watchedDirs.end(); it++)
+- {
+- set<string>::iterator match = m_toWatch.find(it->second);
+- if (match == m_toWatch.end()) // dir is no longer watched
+- m_nomoreIndexedDirs.insert(it->second);
+- else // dir is already watched
+- alreadyWatched.insert (*match);
+- }
+-
+- // look for updated dirs
+- m_toIndex.clear();
+- for (set<string>::iterator iter = alreadyWatched.begin();
+- iter != alreadyWatched.end(); iter++)
+- {
+- // retrieve files contained into the already watched dirs
+-
+- Strigi::DirLister lister (m_pindexerconfiguration);
++class InotifyListener::Private
++{
++ public:
++ Private();
+
+- lister.startListing (*iter);
++ virtual ~Private();
+
+- string path;
+- vector<pair<string, struct stat> > dirs;
+- int ret = lister.nextDir(path, dirs);
+-
+- while (ret != -1) {
+- cout << "path = " << path << endl;
+-
+- for (vector<pair<string, struct stat> >::iterator iter = dirs.begin();
+- iter != dirs.end(); iter++)
+- {
+- struct stat stats = iter->second;
+- if (S_ISREG(stats.st_mode))
+- m_toIndex.insert (make_pair (iter->first, stats.st_mtime));
+- }
+- ret = lister.nextDir(path, dirs);
+- }
++ bool init();
+
+- map <string, time_t> indexedFiles;
+- m_pManager->indexReader()->getChildren (*iter, indexedFiles);
+- for (map<string, time_t>::iterator iter = m_toIndex.begin();
+- iter != m_toIndex.end(); iter++)
+- {
+- map<string, time_t>::iterator match;
+- match = indexedFiles.find(iter->first);
+- if (match == indexedFiles.end()) {
+- // new file created
+- m_events.push_back (new Event (Event::CREATED, iter->first));
+- }
+- else if (match->second < iter->second) {
+- // file has been updated
+- m_events.push_back (new Event (Event::UPDATED, iter->first));
+- }
+- }
+- m_toIndex.clear();
+- }
++ bool isEventInteresting (FsEvent * event);
++ bool isEventValid(FsEvent* event);
+
+- // remove no more indexed items
+- for (set<string>::iterator iter = m_nomoreIndexedDirs.begin();
+- iter != m_nomoreIndexedDirs.end(); iter++)
+- {
+- map <string, time_t> indexedFiles;
+- m_pManager->indexReader()->getChildren (*iter, indexedFiles);
++ void stopMonitoring();
+
+- for (map<string,time_t>::iterator mi = indexedFiles.begin();
+- mi != indexedFiles.end(); mi++)
+- {
+- m_events.push_back (new Event (Event::DELETED, mi->first));
+- }
+- }
+-
+- if (testInterrupt()) {
+- cleanup();
+- return;
+- }
+-}
++ // event methods
++ void pendingEvent(vector<FsEvent*>& events, unsigned int& counter);
+
+-bool InotifyListener::ReindexDirsThread::working()
+-{
+- bool value;
+-
+- STRIGI_MUTEX_LOCK (&m_workingLock);
+- value = m_bWorking;
+- STRIGI_MUTEX_UNLOCK (&m_workingLock);
+-
+- return value;
+-}
++ // watches methods
++ bool addWatch (const std::string& path);
++ void addWatches (const std::set<std::string>& watches);
++ void rmWatch(int wd, std::string path);
++ void rmWatches(std::map<int, std::string>& watchesToRemove);
++ void rmWatches(std::set<std::string>& watchesToRemove);
++ void clearWatches();
+
+-void InotifyListener::ReindexDirsThread::setWorking(bool value)
+-{
+- STRIGI_MUTEX_LOCK (&m_workingLock);
+- m_bWorking = value;
+- STRIGI_MUTEX_UNLOCK (&m_workingLock);
+-}
++ void dirRemoved (string dir);
+
+-void InotifyListener::ReindexDirsThread::setIndexedDirs (
+- const set<string>& dirs,
+- const map<int,string>& watchedDirs)
+-{
+- if (working()) {
+- STRIGI_LOG_DEBUG ("strigi.ReindexDirsThread.setIndexedDirectories",
+- "going to interrupt previous reindexing operation");
+- interrupt();
+- }
+-
+- STRIGI_MUTEX_LOCK (&m_nextJobLock);
+- m_nextJobDirs = dirs;
+- m_nextJobWatchedDirs = watchedDirs;
+- m_bHasWorkTodo = true;
+- STRIGI_MUTEX_UNLOCK (&m_nextJobLock);
+-}
++ private:
++ int m_iInotifyFD;
++ int m_iEvents;
++ std::map<int, std::string> m_watches; //!< map containing all inotify watches added by InotifyListener. Key is watch descriptor, value is dir path
++};
+
+-/* ----------- End ReindexDirsThread -------------------- */
+
+-InotifyListener::InotifyListener(set<string>& indexedDirs)
+- :EventListener("InotifyListener")
++InotifyListener::Private::Private()
+ {
+ // listen only to interesting events
+ m_iEvents = IN_CLOSE_WRITE | IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO
+- | IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF;
+-
+- m_bMonitor = true;
+- setState(Idling);
+- m_bInitialized = false;
+- m_pollingListener = NULL;
+- m_pReindexDirThread = NULL;
+- STRIGI_MUTEX_INIT (&m_watchesLock);
+-
+- // fix path, all dir must end with a '/'
+- for (set<string>::iterator iter = indexedDirs.begin();
+- iter != indexedDirs.end(); iter++)
+- m_indexedDirs.insert (fixPath(*iter));
++ | IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF;
+ }
+
+-InotifyListener::~InotifyListener()
++InotifyListener::Private::~Private()
+ {
+- clearWatches();
+-
+- if (m_pollingListener != NULL) {
+- m_pollingListener->stop();
+- delete m_pollingListener;
+- m_pollingListener = NULL;
+- }
+-
+- if (m_pReindexDirThread != NULL) {
+- m_pReindexDirThread->stop();
+- delete m_pReindexDirThread;
+- m_pReindexDirThread = NULL;
+- }
+-
+- STRIGI_MUTEX_DESTROY (&m_watchesLock);
+ }
+
+-bool InotifyListener::init()
++bool InotifyListener::Private::init()
+ {
+ m_iInotifyFD = inotify_init();
+- if (m_iInotifyFD < 0) {
+- STRIGI_LOG_ERROR ("strigi.InotifyListener",
+- "inotify_init() failed. Are you running Linux 2.6.13\
+- or later? If so, something mysterious has gone wrong.");
++ if (m_iInotifyFD < 0)
+ return false;
+- }
+-
+- m_bInitialized = true;
+-
+- STRIGI_LOG_DEBUG ("strigi.InotifyListener", "successfully initialized");
+
+ return true;
+ }
+
+-void* InotifyListener::run(void*)
+-{
+- if (m_pReindexDirThread == NULL) {
+- m_pReindexDirThread = new ReindexDirsThread ("ReindexDirThread");
+- m_pReindexDirThread->setIndexerConfiguration(m_pindexerconfiguration);
+- m_pReindexDirThread->setCombinedIndexManager( m_pManager);
+-
+- m_pReindexDirThread->start();
+-
+- m_pReindexDirThread->setIndexedDirs (m_indexedDirs, m_watches);
+- // clear m_indexedDirs, ReindexDirsThread will set it by few seconds
+- m_indexedDirs.clear();
+- }
+-
+- while (getState() != Stopping) {
+- watch();
+-
+- processReindexDirThreadData();
+-
+- if (getState() == Working)
+- setState(Idling);
+- }
+-
+- STRIGI_LOG_DEBUG ("strigi.InotifyListener.run",
+- string("exit state: ") + getStringState());
+- return 0;
+-}
+-
+-void InotifyListener::processReindexDirThreadData()
+-{
+- if (m_pReindexDirThread == NULL) {
+- STRIGI_LOG_WARNING("strigi.InotifyListener.processReindexDirThreadData",
+- "m_pReindexDirThread == NULL");
+- return;
+- }
+-
+- set<string> noMoreIndexed, toWatch, newDirs;
+- vector<Event*> events;
+- char buffer [50];
+-
+- m_pReindexDirThread->getData(noMoreIndexed, toWatch, newDirs, events);
+-
+- if (events.size() > 0) {
+- snprintf (buffer, 50 * sizeof (char), "%i", events.size());
+- STRIGI_LOG_DEBUG ("strigi.InotifyListener.processReindexDirThreadData",
+- string("events to process (without delete events: ")
+- + buffer + ")");
+- }
+-
+- if (toWatch.size() > 0) {
+- snprintf (buffer, 50 * sizeof (char), "%i", toWatch.size());
+- STRIGI_LOG_DEBUG ("strigi.InotifyListener.processReindexDirThreadData",
+- string("dirs to watch: ") + buffer);
+- addWatches (toWatch, true);
+- }
+-
+- if (newDirs.size() > 0) {
+- snprintf (buffer, 50 * sizeof (char), "%i", newDirs.size());
+- STRIGI_LOG_DEBUG ("strigi.InotifyListener.processReindexDirThreadData",
+- string("dirs selected by the user: ") + buffer);
+- }
+-
+- if (noMoreIndexed.size() > 0) {
+- snprintf (buffer, 50 * sizeof (char), "%i", noMoreIndexed.size());
+- STRIGI_LOG_DEBUG ("strigi.InotifyListener.processReindexDirThreadData",
+- string("dirs no more indexed: ") + buffer);
+- // remove old indexed files from db (interrupt enabled)
+- rmWatches (noMoreIndexed, true);
+- }
+-
+- if (!testInterrupt() && (events.size() > 0))
+- m_pEventQueue->addEvents (events);
+-
+- // restore interrupt state
+- setInterrupt (false);
+-
+- // TODO: lock?
+- if (noMoreIndexed.size() != 0 || toWatch.size() != 0)
+- m_indexedDirs = newDirs;
+-}
+-
+-void InotifyListener::watch ()
++void InotifyListener::Private::pendingEvent(vector<FsEvent*>& events,
++ unsigned int& counter)
+ {
+- if (m_pEventQueue == NULL) {
+- STRIGI_LOG_ERROR ("strigi.InotifyListener.watch",
+- "m_pEventQueue == NULL!");
+- return;
+- }
+-
+- // some code taken from inotify-tools (http://inotify-tools.sourceforge.net/)
+-
+- vector <Event*> events;
+-
++ counter = 0;
+ struct timeval read_timeout;
+ read_timeout.tv_sec = 1;
+ read_timeout.tv_usec = 0;
+@@ -695,7 +215,7 @@
+ int rc = select(m_iInotifyFD + 1, &read_fds, NULL, NULL, &read_timeout);
+
+ if ( rc < 0 ) {
+- STRIGI_LOG_ERROR ("strigi.InotifyListener.watch",
++ STRIGI_LOG_ERROR ("strigi.InotifyListener.Private.pendingEvent",
+ "Select on inotify failed");
+ return;
+ }
+@@ -703,19 +223,20 @@
+ //Inotify select timeout
+ return;
+ }
+-
+- int thisBytes = read(m_iInotifyFD, &event + bytes, sizeof(struct inotify_event)*MAX_EVENTS - bytes);
++
++ int thisBytes = read(m_iInotifyFD, &event + bytes,
++ sizeof(struct inotify_event)*MAX_EVENTS - bytes);
+
+ if ( thisBytes < 0 ) {
+- STRIGI_LOG_ERROR ("strigi.InotifyListener.watch",
++ STRIGI_LOG_ERROR ("strigi.InotifyListener.Private.pendingEvent",
+ "Read from inotify failed");
+ return;
+ }
+
+ if ( thisBytes == 0 ) {
+- STRIGI_LOG_WARNING ("strigi.InotifyListener.watch",
+- "Inotify reported end-of-file.\
+- Possibly too many events occurred at once.");
++ STRIGI_LOG_WARNING ("strigi.InotifyListener.Private.pendingEvent",
++ "Inotify reported end-of-file."
++ "Possibly too many events occurred at once.");
+ return;
+ }
+
+@@ -732,129 +253,22 @@
+
+ do
+ {
+- string message;
+- string watchName;
+- int watchID;
+-
++ string watchName = "";
++ int watchID = -1;
++
+ this_event = (struct inotify_event *)this_event_char;
+
+- STRIGI_MUTEX_LOCK (&m_watchesLock);
+-
+ map <int, string>::iterator watchIter = m_watches.find (this_event->wd);
+
+- if (watchIter == m_watches.end()) {
+- if ((this_event->wd && IN_IGNORED) == 0) {
+- // event wasn't marked with IGNORE flag, print some message
+-
+- char buff [20];
+- snprintf(buff, 20 * sizeof (char), "%i",this_event->wd);
+-
+- STRIGI_LOG_WARNING ("strigi.InotifyListener.watch",
+- string("returned an unknown watch descriptor: ") + buff);
+-
+- string eventtype = eventToString (this_event->mask);
+-
+- STRIGI_LOG_WARNING ("strigi.InotifyListener.watch",
+- "missed event type: " + eventtype);
+- }
+- // jump to next event
+- this_event_char += sizeof(struct inotify_event) + this_event->len;
+- remaining_bytes -= sizeof(struct inotify_event) + this_event->len;
+-
+- STRIGI_MUTEX_UNLOCK (&m_watchesLock);
+-
+- continue;
+- }
+-
+- watchName = watchIter->second;
+- watchID = watchIter->first;
+-
+- STRIGI_MUTEX_UNLOCK (&m_watchesLock);
+-
+- if (isEventInteresting (this_event)) {
+- STRIGI_LOG_DEBUG ("strigi.InotifyListener.watch",
+- watchName + " changed");
+- STRIGI_LOG_DEBUG ("strigi.InotifyListener.watch",
+- string("caught inotify event: ") +
+- eventToString( this_event->mask));
+-
+- if ((this_event->len > 0))
+- STRIGI_LOG_DEBUG ("strigi.InotifyListener.watch",
+- "event regards " +
+- string(this_event->name, this_event->len));
+-
+- string file (watchName + string(this_event->name));
+-
+- if ( ((IN_MODIFY & this_event->mask) != 0) ||
+- ((IN_CLOSE_WRITE & this_event->mask) != 0) )
+- {
+- Event* event = new Event (Event::UPDATED, file);
+- events.push_back (event);
+- }
+- else if (((IN_DELETE & this_event->mask) != 0) ||
+- ((IN_MOVED_FROM & this_event->mask) != 0 ) ||
+- ((IN_DELETE_SELF & this_event->mask) != 0 ) ||
+- ((IN_MOVE_SELF & this_event->mask) != 0 ))
+- {
+- if ((IN_ISDIR & this_event->mask) != 0)
+- dirRemoved (file, events);
+- else {
+- Event* event = new Event (Event::DELETED, file);
+- events.push_back (event);
+- }
+- }
+- else if (( (IN_CREATE & this_event->mask) != 0) ||
+- ((IN_MOVED_TO & this_event->mask) != 0 ) )
+- {
+- if ( (IN_ISDIR & this_event->mask) != 0 ) {
+- // a new directory has been created or an already watched
+- // directory has been moved into a watched place
+-
+- m_toWatch.clear();
+-
+- DirLister lister(m_pindexerconfiguration);
+- string path;
+- vector<pair<string, struct stat> > dirs;
+-
+- lister.startListing (file);
+- int ret = lister.nextDir(path, dirs);
+-
+- while (ret != -1) {
+- for (vector<pair<string, struct stat> >::iterator iter = dirs.begin();
+- iter != dirs.end(); iter++)
+- {
+- struct stat stats = iter->second;
+-
+- if (S_ISDIR(stats.st_mode)) {
+- //dir
+- m_toWatch.insert (iter->first);
+- }
+- else if (S_ISREG(stats.st_mode)) {
+- //file
+- Event* event = new Event (Event::CREATED,
+- iter->first);
+- events.push_back (event);
+- }
+- }
+- ret = lister.nextDir(path, dirs);
+- }
+-
+- // add new watches
+- addWatches (m_toWatch);
+-
+- m_toWatch.clear();
+- m_toIndex.clear();
+- }
+- else {
+- Event* event = new Event (Event::CREATED, file);
+- events.push_back (event);
+- }
+- }
+- else
+- STRIGI_LOG_DEBUG ("strigi.InotifyListener.watch",
+- "inotify's unknown event");
++ if (watchIter != m_watches.end()) {
++ watchName = watchIter->second;
++ watchID = watchIter->first;
+ }
+
++ events.push_back (new InotifyEvent ( watchID, watchName, this_event));
++ counter++;
++
++ // next event
+ this_event_char += sizeof(struct inotify_event) + this_event->len;
+ remaining_bytes -= sizeof(struct inotify_event) + this_event->len;
+ }
+@@ -868,50 +282,19 @@
+ snprintf(buff, 20 * sizeof (char), "%f", (((float)remaining_bytes)/((float)sizeof(struct inotify_event))));
+ message = buff;
+ message += "event(s) may have been lost!";
+- STRIGI_LOG_ERROR ("strigi.InotifyListener.watch", message);
++ STRIGI_LOG_ERROR ("strigi.InotifyListener.Private.pendingEvent", message);
+ }
+
+- if (events.size() > 0)
+- m_pEventQueue->addEvents (events);
+-
+ fflush( NULL );
+ }
+
+-bool
+-InotifyListener::isEventInteresting (struct inotify_event * event)
+-{
+- // ignore files starting with '.'
+- if (((IN_ISDIR & event->mask) == 0) && (event->len > 0)
+- && ((event->name)[0] == '.'))
+- return false;
+-
+- //TODO: FIX with AnalyzerConfiguration
+-// if (m_pFilterManager != NULL)
+-// {
+-// if ((event->len > 0) && m_pFilterManager->findMatch(event->name, event->len))
+-// return false;
+-// }
+-// else
+-// STRIGI_LOG_WARNING ("strigi.InotifyListener.isEventInteresting", "unable to use filters, m_pFilterManager == NULL!")
+-
+- return true;
+-}
+-
+-bool InotifyListener::addWatch (const string& path)
++bool InotifyListener::Private::addWatch (const string& path)
+ {
+- if (!m_bInitialized)
+- return false;
+-
+- STRIGI_MUTEX_LOCK (&m_watchesLock);
+-
+ map<int, string>::iterator iter;
+ for (iter = m_watches.begin(); iter != m_watches.end(); iter++)
+ {
+ if ((iter->second).compare (path) == 0) // dir is already watched
+- {
+- STRIGI_MUTEX_UNLOCK (&m_watchesLock);
+ return true;
+- }
+ }
+
+ static int wd;
+@@ -919,30 +302,28 @@
+
+ if (wd < 0)
+ {
+- STRIGI_MUTEX_UNLOCK (&m_watchesLock);
+-
+ if ((wd == -1) && ( errno == ENOSPC))
+ {
+- STRIGI_LOG_ERROR ("strigi.InotifyListener.addWatch",
++ STRIGI_LOG_ERROR ("strigi.InotifyListener.Private.addWatch",
+ "Failed to watch, maximum watch number reached");
+- STRIGI_LOG_ERROR ("strigi.InotifyListener.addWatch",
++ STRIGI_LOG_ERROR ("strigi.InotifyListener.Private.addWatch",
+ "You've to increase the value stored into\
+ /proc/sys/fs/inotify/max_user_watches");
+ }
+ else if ( wd == -1 )
+ {
+- STRIGI_LOG_ERROR ("strigi.InotifyListener.addWatch",
++ STRIGI_LOG_ERROR ("strigi.InotifyListener.Private.addWatch",
+ "Failed to watch " + path + " because of: " +
+- strerror(-wd));
++ strerror(-wd));
+ }
+ else
+ {
+ char buff [20];
+ snprintf(buff, 20* sizeof (char), "%i", wd);
+
+- STRIGI_LOG_ERROR ("strigi.InotifyListener.addWatch",
++ STRIGI_LOG_ERROR ("strigi.InotifyListener.Private.addWatch",
+ "Failed to watch " + path + ": returned wd was " +
+- buff + " (expected -1 or >0 )");
++ buff + " (expected -1 or >0 )");
+ }
+
+ return false;
+@@ -951,412 +332,270 @@
+ {
+ m_watches.insert(make_pair(wd, path));
+
+- STRIGI_MUTEX_UNLOCK (&m_watchesLock);
+-
+- STRIGI_LOG_INFO ("strigi.InotifyListener.addWatch",
++ STRIGI_LOG_INFO ("strigi.InotifyListener.Private.addWatch",
+ "added watch for " + path);
+
+ return true;
+ }
+ }
+
+-void InotifyListener::addWatches (const set<string> &watches,
+- bool enableInterrupt)
+-{
+- set<string>::iterator iter;
+- set<string> toPool;
+- set<string> watched;
+-
+- for (iter = watches.begin(); iter != watches.end(); iter++)
+- {
+- if (enableInterrupt && testInterrupt())
+- break;
+-
+- if (!addWatch (*iter))
+- {
+-
+- if (errno == ENOSPC)
+- // user can't add no more watches, it's useless to go on
+- break;
+-
+- // adding watch failed for other reason, keep trying with others
+- toPool.insert(*iter);
+- }
+- else
+- watched.insert (*iter);
+- }
+-
+- if (iter != watches.end())
+- {
+- // probably we reached the max_user_watches limit
+- for ( ; iter != watches.end(); iter++)
+- toPool.insert (*iter);
+- }
+-
+-
+- if (enableInterrupt && testInterrupt())
+- {
+- //TODO: check
+-
+- vector <Event*> events;
+- // recover from interrupt
+- for (set<string>::iterator iter = watched.begin();
+- iter != watched.end(); iter++)
+- {
+- dirRemoved (*iter, events);
+- }
+-
+- m_pEventQueue->addEvents(events);
+-
+- setInterrupt (false);
+- }
+- else if (!toPool.empty())
+- {
+- if (m_pollingListener == NULL)
+- {
+- m_pollingListener = new PollingListener();
+- m_pollingListener->setEventListenerQueue( m_pEventQueue);
+- m_pollingListener->setCombinedIndexManager( m_pManager);
+- m_pollingListener->setIndexerConfiguration(m_pindexerconfiguration);
+- //TODO: start with a low priority?
+- m_pollingListener->start( );
+- }
+-
+- m_pollingListener->addWatches( toPool);
+- }
+-}
+-
+-void InotifyListener::rmWatch(int wd, string path)
++void InotifyListener::Private::rmWatch(int wd, string path)
+ {
+ char buff [20];
+ snprintf(buff, 20 * sizeof (char), "%i",wd);
+
+ if (inotify_rm_watch (m_iInotifyFD, wd) == -1)
+ {
+- STRIGI_LOG_ERROR ("strigi.InotifyListener.rmWatch",
+- string("Error removing watch ") + buff +
+- " associated to path: " + path);
+- STRIGI_LOG_ERROR ("strigi.InotifyListener.rmWatch",
+- string("error: ") + strerror(errno));
++ STRIGI_LOG_ERROR ("strigi.InotifyListener.Private.rmWatch",
++ string("Error removing watch ") + buff +
++ " associated to path: " + path);
++ STRIGI_LOG_ERROR ("strigi.InotifyListener.Private.rmWatch",
++ string("error: ") + strerror(errno));
+ }
+ else
+- STRIGI_LOG_DEBUG ("strigi.InotifyListener.rmWatch",
+- string("Removed watch ") + buff +
+- " associated to path: " + path);
++ STRIGI_LOG_DEBUG ("strigi.InotifyListener.Private.rmWatch",
++ string("Removed watch ") + buff +
++ " associated to path: " + path);
+
+- STRIGI_MUTEX_LOCK (&m_watchesLock);
+ map<int, string>::iterator match = m_watches.find(wd);
+
+ if (match != m_watches.end() && (path.compare(match->second) == 0))
+ m_watches.erase (match);
+ else
+- STRIGI_LOG_ERROR ("strigi.InotifyListener.rmWatch",
++ STRIGI_LOG_ERROR ("strigi.InotifyListener.Private.rmWatch",
+ "unable to remove internal watch reference for " + path);
+-
+- STRIGI_MUTEX_UNLOCK (&m_watchesLock);
+ }
+
+-void InotifyListener::rmWatches(map<int, string>& watchesToRemove,
+- bool enableInterrupt)
++void InotifyListener::Private::rmWatches(map<int, string>& watchesToRemove)
+ {
+- set<string> removedWatches;
+- map<int,string> watches;
+-
+- STRIGI_MUTEX_LOCK (&m_watchesLock);
+- watches = m_watches;
+- STRIGI_MUTEX_UNLOCK (&m_watchesLock);
+-
+ for (map<int,string>::iterator it = watchesToRemove.begin();
+ it != watchesToRemove.end(); it++)
+ {
+- if (enableInterrupt && testInterrupt())
+- break;
+-
+- map<int,string>::iterator match = watches.find (it->first);
+- if (match != watches.end())
+- {
++ map<int,string>::iterator match = m_watches.find (it->first);
++ if (match != m_watches.end())
+ rmWatch (it->first, it->second);
+- removedWatches.insert (it->second);
+- }
+ else
+- STRIGI_LOG_WARNING ("strigi.InotifyListener.rmWatches",
+- "unable to remove watch associated to " + it->second);
+- }
+-
+- if (enableInterrupt && testInterrupt())
+- {
+- // undo the delete operation
+- for (set<string>::iterator it = removedWatches.begin();
+- it != removedWatches.end(); it++)
+- {
+- addWatch (*it);
+- }
++ STRIGI_LOG_WARNING ("strigi.InotifyListener.Private.rmWatches",
++ "unable to remove watch associated to " + it->second);
+ }
+ }
+
+-void InotifyListener::rmWatches(set<string>& watchesToRemove,
+- bool enableInterrupt)
++void InotifyListener::Private::rmWatches(set<string>& watchesToRemove)
+ {
+ map<int, string> removedWatches;
+
+- STRIGI_MUTEX_LOCK (&m_watchesLock);
+ // find all pairs <watch-id, watch-name> that have to be removed
+ for (set<string>::iterator it = watchesToRemove.begin();
+ it != watchesToRemove.end(); it++)
+ {
+ MatchString finder (*it);
+- map<int, string>::iterator match = find_if (m_watches.begin(), m_watches.end(),
+- finder);
++ map<int, string>::iterator match = find_if (m_watches.begin(),
++ m_watches.end(), finder);
+ if (match != m_watches.end())
+ removedWatches.insert (make_pair (match->first, match->second));
+ else
+- STRIGI_LOG_WARNING ("strigi.InotifyListener.rmWatches",
+- "unable to find the watch associated to " + *it);
++ STRIGI_LOG_WARNING ("strigi.InotifyListener.Private.rmWatches",
++ "unable to find the watch associated to " + *it);
+ }
+- STRIGI_MUTEX_UNLOCK (&m_watchesLock);
+-
+- if (!enableInterrupt || !testInterrupt())
+- rmWatches (removedWatches, enableInterrupt);
++
++ rmWatches (removedWatches);
+ }
+
+-void InotifyListener::clearWatches ()
++void InotifyListener::Private::clearWatches ()
+ {
+- STRIGI_MUTEX_LOCK (&m_watchesLock);
++ map<int, string>::iterator iter;
++ for (iter = m_watches.begin(); iter != m_watches.end(); iter++) {
++ char buff [20];
++ snprintf(buff, 20 * sizeof (char), "%i", iter->first);
+
+- for (map<int, string>::iterator iter = m_watches.begin();
+- iter != m_watches.end(); iter++)
+- {
+- rmWatch (iter->first, iter->second);
++ if (inotify_rm_watch (m_iInotifyFD, iter->first) == -1)
++ {
++ STRIGI_LOG_ERROR ("strigi.InotifyListener.Private.clearWatches",
++ string("Error removing watch ") + buff +
++ " associated to path: " + iter->second);
++ STRIGI_LOG_ERROR ("strigi.InotifyListener.Private.clearWatches",
++ string("error: ") + strerror(errno));
++ }
++ else
++ STRIGI_LOG_DEBUG ("strigi.InotifyListener.Private.clearWatches",
++ string("Removed watch ") + buff +
++ " associated to path: " + iter->second);
+ }
+
+ m_watches.clear();
+-
+- STRIGI_MUTEX_UNLOCK (&m_watchesLock);
+ }
+
+-void InotifyListener::dirRemoved (string dir, vector<Event*>& events,
+- bool enableInterrupt)
++bool InotifyListener::Private::isEventValid(FsEvent* event)
+ {
+- dir = fixPath (dir);
+- vector<Event*> newEvents;
+- map<int, string> watches;
+- map<int, string> watchesToRemove;
++ InotifyEvent* inotifyEvent = dynamic_cast<InotifyEvent*> (event);
+
+- STRIGI_LOG_DEBUG ("strigi.InotifyListener.dirRemoved", dir +
+- " is no longer watched, removing all indexed files contained");
++ if (inotifyEvent == 0)
++ return false;
+
+- // we've to de-index all files contained into the deleted/moved directory
+- if (m_pManager)
+- {
+- // all indexed files contained into dir
+- map<string, time_t> indexedFiles;
+- m_pManager->indexReader()->getChildren(dir, indexedFiles);
++ map<int, string>::iterator match = m_watches.find(inotifyEvent->watchID());
+
+- // remove all entries that were contained into the removed dir
+- for (map<string, time_t>::iterator it = indexedFiles.begin();
+- it != indexedFiles.end(); it++)
+- {
+- if (enableInterrupt && testInterrupt())
+- break;
+-
+- Event* event = new Event (Event::DELETED, it->first);
+- newEvents.push_back (event);
+- }
+- }
+- else
+- STRIGI_LOG_WARNING ("strigi.InotifyListener.dirRemoved",
+- "m_pManager == NULL!");
++ return (match != m_watches.end());
++}
+
+- // remove inotify watches over no more indexed dirs
+- STRIGI_MUTEX_LOCK (&m_watchesLock);
+- watches = m_watches;
+- STRIGI_MUTEX_UNLOCK (&m_watchesLock);
++void InotifyListener::Private::dirRemoved (string dir)
++{
++ map <int, string> watchesToRemove;
+
+- for (map<int, string>::iterator mi = watches.begin();
+- mi != watches.end(); mi++)
++ // remove inotify watches over no more indexed directories
++ for (map<int, string>::iterator mi = m_watches.begin();
++ mi != m_watches.end(); mi++)
+ {
+ if ((mi->second).find (dir,0) == 0)
+ watchesToRemove.insert (make_pair (mi->first, mi->second));
+-
+- if (enableInterrupt && testInterrupt())
+- break;
+- }
+-
+- if (enableInterrupt && testInterrupt())
+- {
+- for (vector<Event*>::iterator iter = newEvents.begin();
+- iter != newEvents.end(); iter++)
+- {
+- delete *iter;
+- }
+- newEvents.clear();
+- }
+- else
+- {
+- rmWatches (watchesToRemove);
+-
+- for (vector<Event*>::iterator iter = newEvents.begin();
+- iter != newEvents.end(); iter++)
+- {
+- events.push_back(*iter);
+- }
+- newEvents.clear();
+ }
+
+- // remove also dir watched by pollinglistener
+- //FIXME: to fix, call right method
+- if (m_pollingListener != NULL)
+- m_pollingListener->rmWatch( dir);
++ rmWatches (watchesToRemove);
+ }
+
+-void InotifyListener::dirsRemoved (set<string> dirs, vector<Event*>& events,
+- bool enableInterrupt)
++// END InotifyListener::Private
++
++
++InotifyListener::InotifyListener(set<string>& indexedDirs)
++ :FsListener("InotifyListener", indexedDirs)
+ {
+- vector<Event*> newEvents;
+-
+- for (set<string>::iterator iter = dirs.begin();
+- iter != dirs.end(); iter++)
+- {
+- if (enableInterrupt && testInterrupt())
+- break;
+-
+- dirRemoved (*iter, newEvents, enableInterrupt);
+- }
+-
+- if (enableInterrupt && testInterrupt())
+- {
+- STRIGI_LOG_DEBUG ("strigi.InotifyListener.dirsRemoved",
+- "recovering from interrupt signal");
+-
+- for (vector<Event*>::iterator iter = newEvents.begin();
+- iter != newEvents.end(); iter++)
+- {
+- delete *iter;
+- }
+- newEvents.clear();
+- }
+- else
+- {
+- printf ("newEvents.size() == %i\n",events.size());
+- for (vector<Event*>::iterator iter = newEvents.begin();
+- iter != newEvents.end(); iter++)
+- {
+- events.push_back(*iter);
+- }
+- newEvents.clear();
+- }
++ p = new Private();
+ }
+
+-void InotifyListener::setInterrupt (bool value)
++InotifyListener::~InotifyListener()
+ {
+- STRIGI_MUTEX_LOCK(&m_interruptLock);
+- m_bInterrupt = value;
+- STRIGI_MUTEX_UNLOCK(&m_interruptLock);
++ clearWatches();
++ delete p;
++
+ }
+
+-bool InotifyListener::testInterrupt()
++void InotifyListener::clearWatches ()
+ {
+- bool value;
+- STRIGI_MUTEX_LOCK(&m_interruptLock);
+- value = m_bInterrupt;
+- STRIGI_MUTEX_UNLOCK(&m_interruptLock);
+-
+- if (value)
+- STRIGI_LOG_DEBUG ("strigi.ReindexDirsThread.testInterrupt",
+- "Caught interrupt event");
+-
+- return value;
++ p->clearWatches();
+ }
+
+-void InotifyListener::setIndexedDirectories (const set<string> &dirs) {
+- if (!m_pManager) {
+- STRIGI_LOG_ERROR ("strigi.InotifyListener.setIndexedDirectories",
+- "m_pManager == NULL!");
+- return;
+- }
+- if (m_pEventQueue == NULL)
+- {
+- STRIGI_LOG_ERROR ("strigi.InotifyListener.setIndexedDirectories",
+- "m_pEventQueue == NULL!");
+- return;
++bool InotifyListener::init()
++{
++ if (!p->init()) {
++ STRIGI_LOG_ERROR ("strigi.InotifyListener.init",
++ "inotify_init() failed. Are you running Linux 2.6.13\
++ or later? If so, something mysterious has gone wrong.");
++ return false;
+ }
+
+- set<string> fixedDirs;
++ m_bInitialized = true;
+
+- // fix path, all dir must end with a '/'
+- for (set<string>::iterator iter = dirs.begin(); iter != dirs.end(); iter++)
+- fixedDirs.insert (fixPath (*iter));
++ STRIGI_LOG_DEBUG ("strigi.InotifyListener.init","successfully initialized");
+
+- if (m_pReindexDirThread == NULL)
+- {
+- m_pReindexDirThread = new ReindexDirsThread ("ReindexDirThread",
+- m_indexedDirs);
+- m_pReindexDirThread->setIndexerConfiguration(m_pindexerconfiguration);
+- m_pReindexDirThread->setCombinedIndexManager ( m_pManager);
++ return true;
++}
++
++FsEvent* InotifyListener:: retrieveEvent()
++{
++ if (m_events.empty())
++ return 0;
++
++ vector<FsEvent*>::iterator iter = m_events.begin();
++
++ FsEvent* event = *iter;
++ m_events.erase(iter);
++
++ STRIGI_LOG_DEBUG("strigi.InotifyListener.retrieveEvent",
++ "returning " + event->description())
++
++ return event;
++}
+
+- m_pReindexDirThread->start();
++bool InotifyListener::pendingEvent()
++{
++ unsigned int counter = 0;
++ p->pendingEvent(m_events, counter);
++
++ if (counter > 0) {
++ char buff [20];
++ snprintf(buff, 20 * sizeof (char), "%i", counter);
++ string message = "Caught ";
++ message += buff;
++ message += " inotify's pending event(s)";
++
++ STRIGI_LOG_DEBUG ("strigi.InotifyListener.pendingEvent", message)
+ }
+
+- if (m_pReindexDirThread->working())
+- setInterrupt (true);
++ dumpEvents();
+
+- STRIGI_MUTEX_LOCK (&m_watchesLock);
+- map <int, string> watches = m_watches;
+- STRIGI_MUTEX_UNLOCK (&m_watchesLock);
++ return !m_events.empty();
++}
++
++bool InotifyListener::isEventInteresting (FsEvent* event)
++{
++ InotifyEvent* inotifyEvent = dynamic_cast<InotifyEvent*> (event);
++
++ if (inotifyEvent == 0)
++ return false;
+
+- m_pReindexDirThread->setIndexedDirs( fixedDirs, watches);
++ struct inotify_event* structInotify = inotifyEvent->event();
++
++ if ((structInotify->wd && IN_IGNORED) == 0)
++ return false;
++
++ // ignore files starting with '.'
++ if (((IN_ISDIR & structInotify->mask) == 0) && (structInotify->len > 0)
++ && ((structInotify->name)[0] == '.'))
++ return false;
++
+
+- string newdirs;
++ if (m_pAnalyzerConfiguration == NULL) {
++ STRIGI_LOG_WARNING ("strigi.InotifyListener.isEventInteresting",
++ "AnalyzerConfiguration == NULL")
++ return true;
++ }
+
+- for (set<string>::iterator iter = fixedDirs.begin(); iter != fixedDirs.end(); iter++)
+- newdirs += ('|' + *iter);
++ string path = inotifyEvent->watchName();
++ if (path[path.length() - 1] != '/')
++ path += "/";
++ path += inotifyEvent->name();
++
++ if (inotifyEvent->regardsDir())
++ return m_pAnalyzerConfiguration->indexDir( path.c_str(),
++ inotifyEvent->name());
++ else
++ return m_pAnalyzerConfiguration->indexFile( path.c_str(),
++ inotifyEvent->name());
++}
+
+- STRIGI_LOG_DEBUG ("strigi.InotifyListener.setIndexedDirectories",
+- "new indexed dirs: " + newdirs);
++bool InotifyListener::isEventValid(FsEvent* event)
++{
++ return p->isEventValid (event);
+ }
+
+-string InotifyListener::eventToString(int events)
++bool InotifyListener::addWatch (const string& path)
+ {
+- string message;
++ return p->addWatch (path);
++}
+
+- if ( (IN_ACCESS & events) != 0 )
+- message += "ACCESS";
+- else if ( (IN_MODIFY & events) != 0 )
+- message += "MODIFY";
+- else if ( (IN_ATTRIB & events) != 0 )
+- message += "ATTRIB";
+- else if ( (IN_CLOSE_WRITE & events) != 0 )
+- message += "CLOSE_WRITE";
+- else if ( (IN_CLOSE_NOWRITE & events) != 0 )
+- message += "CLOSE_NOWRITE";
+- else if ( (IN_OPEN & events) != 0 )
+- message += "OPEN";
+- else if ( (IN_MOVED_FROM & events) != 0 )
+- message += "MOVED_FROM";
+- else if ( (IN_MOVED_TO & events) != 0 )
+- message += "MOVED_TO";
+- else if ( (IN_CREATE & events) != 0 )
+- message += "CREATE";
+- else if ( (IN_DELETE & events) != 0 )
+- message += "DELETE";
+- else if ( (IN_DELETE_SELF & events) != 0 )
+- message += "DELETE_SELF";
+- else if ( (IN_UNMOUNT & events) != 0 )
+- message += "UNMOUNT";
+- else if ( (IN_Q_OVERFLOW & events) != 0 )
+- message += " Q_OVERFLOW";
+- else if ( (IN_IGNORED & events) != 0 )
+- message += " IGNORED";
+- else if ( (IN_CLOSE & events) != 0 )
+- message += "CLOSE";
+- else if ( (IN_MOVE & events) != 0 )
+- message += "MOVE";
+- else if ( (IN_ISDIR & events) != 0 )
+- message += " ISDIR";
+- else if ( (IN_ONESHOT & events) != 0 )
+- message += " ONESHOT";
+- else
+- message = "UNKNOWN";
++void InotifyListener::dirRemoved (string dir, vector<Event*>& events)
++{
++ map<int, string> watchesToRemove;
++
++ STRIGI_LOG_DEBUG ("strigi.InotifyListener.dirRemoved", dir +
++ " is no longer watched, removing all indexed files contained");
++
++ // we've to de-index all files contained into the deleted/moved directory
++ if (m_pManager)
++ {
++ // all indexed files contained into directory
++ map<string, time_t> indexedFiles;
++ m_pManager->indexReader()->getChildren(dir, indexedFiles);
+
+- return message;
++ // remove all entries that were contained into the removed directory
++ for (map<string, time_t>::iterator it = indexedFiles.begin();
++ it != indexedFiles.end(); it++)
++ {
++ Event* event = new Event (Event::DELETED, it->first);
++ events.push_back (event);
++ }
++ }
++ else
++ STRIGI_LOG_WARNING ("strigi.InotifyListener.Private.dirRemoved",
++ "m_pManager == NULL!");
++ p->dirRemoved (dir);
+ }
+--- a/src/daemon/eventlistener/inotifylistener.h
++++ b/src/daemon/eventlistener/inotifylistener.h
+@@ -20,7 +20,7 @@
+ #ifndef INOTIFYLISTENER_H
+ #define INOTIFYLISTENER_H
+
+-#include "eventlistener.h"
++#include "fslistener.h"
+ #include "strigi_thread.h"
+ #include <map>
+ #include <vector>
+@@ -28,105 +28,59 @@
+ class Event;
+ class PollingListener;
+
+-/*!
+-* @class InotifyListener
+-* @brief Interacts with kernel inotify monitoring recursively all changes over indexed directories
+-*/
++class InotifyEvent : public FsEvent
++{
++ public:
++ InotifyEvent(int watchID, const std::string& watchName,
++ struct inotify_event* event);
++
++ const std::string description();
++ bool regardsDir();
++
++ struct inotify_event* event() { return m_event;}
++ char* name();
++ int watchID() { return m_watchID;}
++ std::string watchName() { return m_watchName;}
++
++ private:
++ struct inotify_event* m_event;
++ std::string m_watchName;
++ int m_watchID;
++
++};
+
+-class InotifyListener : public EventListener
++/*!
++ * @class InotifyListener
++ * @brief Uses linux kernel inotify facility in order to keep strigi's index updated.
++ */
++class InotifyListener : public FsListener
+ {
++ private:
++ class Private;
++ Private* p;
++
+ public:
+ explicit InotifyListener(std::set<std::string>& indexedDirs);
+
+- ~InotifyListener();
++ virtual ~InotifyListener();
+
+ bool init();
+
+- bool addWatch (const std::string& path);
+- void addWatches (const std::set<std::string>& watches,
+- bool enableInterrupt = false);
+- void setIndexedDirectories (const std::set<std::string>& dirs);
+-
+- void* run(void*);
+-
+ protected:
+- /*!
+- * @param event inotify's event mask
+- * returns a string containing the event description
+- */
+- std::string eventToString(int events);
+-
+- /*!
+- * @param event the inotify event to analyze
+- * returns true if event is to process (ergo is interesting), false otherwise
+- */
+- bool isEventInteresting (struct inotify_event * event);
+-
+- /*!
+- * main InotifyListener thread
+- */
+- void watch ();
++ void stopMonitoring();
+
+-
+- void processReindexDirThreadData();
+- /*!
+- * @param dir removed dir
+- * Removes all db entries of files contained into the removed dir.
+- * Removes also all inotify watches related to removed dir (including watches over subdirs), there's <b>no need</b> to call rmWatch after invoking that method
+- * Updates also m_watches
+- */
+- void dirRemoved (std::string dir,
+- std::vector<Event*>& events,
+- bool enableInterrupt = false);
+-
+- /*!
+- * @param dirs removed dirs
+- * Removes all db entries of files contained into the removed dirs.
+- * Removes also all inotify watches related to removed dirs (including watches over subdirs), there's <b>no need</b> to call rmWatch after invoking that method
+- * Optimized for large removal
+- * Updates also m_watches
+- */
+- void dirsRemoved (std::set<std::string> dirs,
+- std::vector<Event*>& events,
+- bool enableInterrupt = false);
+-
+- /*!
+- * @param wd inotify watch descriptor
+- * @param path path associated to inotify watch
+- * removes and release an inotify watch. Usually there's no need to use this method.
+- * @sa dirRemoved
+- */
+- void rmWatch(int wd, std::string path);
++ // event methods
++ bool pendingEvent();
++ FsEvent* retrieveEvent();
++ bool isEventValid(FsEvent* event);
++ bool isEventInteresting (FsEvent * event);
+
+- void rmWatches(std::map<int, std::string>& watchesToRemove,
+- bool enableInterrupt = false);
+-
+- void rmWatches(std::set<std::string>& watchesToRemove,
+- bool enableInterrupt = false);
+-
+- /*!
+- * removes and release all inotify watches
+- */
+- void clearWatches();
+-
+- void setInterrupt (bool value);
+- bool testInterrupt();
++ void dirRemoved (std::string dir, std::vector<Event*>& events);
+
+- PollingListener* m_pollingListener;
+- int m_iInotifyFD;
+- int m_iEvents;
+- std::map<int, std::string> m_watches; //!< map containing all inotify watches added by InotifyListener. Key is watch descriptor, value is dir path
+- bool m_bMonitor;
+- bool m_bInterrupt;
+- bool m_bInitialized;
+- std::map<std::string, time_t> m_toIndex;
+- std::set<std::string> m_toWatch;
+- std::set<std::string> m_indexedDirs;
+- pthread_mutex_t m_watchesLock;
+- pthread_mutex_t m_interruptLock;
++ // watches methods
++ bool addWatch (const std::string& path);
+
+- class ReindexDirsThread;
+- ReindexDirsThread* m_pReindexDirThread;
++ void clearWatches();
+ };
+
+ #endif
+--- a/src/daemon/eventlistener/pollinglistener.cpp
++++ b/src/daemon/eventlistener/pollinglistener.cpp
+@@ -106,14 +106,14 @@
+ }
+ void PollingListener::poll () {
+ assert(m_pManager);
+- assert(m_pindexerconfiguration);
++ assert(m_pAnalyzerConfiguration);
+
+ // get a shadow copy of m_watches
+ STRIGI_MUTEX_LOCK (&m_mutex);
+ vector<string> watches = m_watches;
+ STRIGI_MUTEX_UNLOCK (&m_mutex);
+
+- DirAnalyzer diranalyzer(*m_pManager, *m_pindexerconfiguration);
++ DirAnalyzer diranalyzer(*m_pManager, *m_pAnalyzerConfiguration);
+ STRIGI_LOG_DEBUG ("strigi.PollingListener.poll", "going across filesystem");
+ diranalyzer.updateDirs(watches, 1, this);
+ STRIGI_LOG_DEBUG ("strigi.PollingListener.poll",
+@@ -157,7 +157,7 @@
+ }
+
+ void
+-PollingListener::addWatches(const set<string>& watches, bool enableInterrupt) {
++PollingListener::addWatches(const set<string>& watches) {
+ for (set<string>::iterator iter = watches.begin();
+ iter != watches.end(); iter++) {
+ string temp = fixPath(*iter);
+--- a/src/daemon/eventlistener/pollinglistener.h
++++ b/src/daemon/eventlistener/pollinglistener.h
+@@ -53,8 +53,7 @@
+
+ bool addWatch (const std::string& path);
+ void rmWatch (const std::string& path);
+- void addWatches (const std::set<std::string>& watches,
+- bool enableInterrupt = false);
++ void addWatches (const std::set<std::string>& watches);
+ void setIndexedDirectories (const std::set<std::string>& dirs);
+
+ void* run(void*);
+--- a/src/luceneindexer/luceneindexer.cpp
++++ b/src/luceneindexer/luceneindexer.cpp
+@@ -25,6 +25,7 @@
+ #include <sys/types.h>
+ #include <stgdirent.h>
+ #include <string.h>
++#include <stdlib.h>
+
+ using namespace std;
+
+@@ -36,19 +37,20 @@
+ }
+ return false;
+ }
+-void
++bool
+ checkIndexdirIsEmpty(const char* dir) {
+ DIR* d = opendir(dir);
+- if (!d) return;
++ if (!d) return false;
+ struct dirent* de = readdir(d);
+ while (de) {
+ if (strcmp(de->d_name, "..") && strcmp(de->d_name, ".")) {
+ fprintf(stderr, "Directory %s is not empty.\n", dir);
+- exit(1);
++ return false;
+ }
+ de = readdir(d);
+ }
+ closedir(d);
++ return true;
+ }
+ int
+ main(int argc, char **argv) {
+@@ -57,7 +59,9 @@
+ return -1;
+ }
+
+- checkIndexdirIsEmpty(argv[1]);
++ if (!checkIndexdirIsEmpty(argv[1])) {
++ return 1;
++ }
+
+ vector<pair<bool,string> >filters;
+ filters.push_back(make_pair<bool,string>(false,".*/"));
+--- a/src/streamanalyzer/CMakeLists.txt
++++ b/src/streamanalyzer/CMakeLists.txt
+@@ -67,7 +67,8 @@
+ # set(streamindex_LIBS ${streamindex_LIBS} ntquery)
+ #ELSE(WIN32)
+ add_library(streamanalyzer SHARED ${streamanalyzer_SRCS})
+- install(TARGETS streamanalyzer LIBRARY DESTINATION ${LIB_DESTINATION})
++ install(TARGETS streamanalyzer LIBRARY DESTINATION ${LIB_DESTINATION}
++ RUNTIME DESTINATION ${LIB_DESTINATION})
+ #ENDIF(WIN32)
+
+ set_target_properties(streamanalyzer PROPERTIES
+--- a/src/streamanalyzer/endanalyzers/arendanalyzer.cpp
++++ b/src/streamanalyzer/endanalyzers/arendanalyzer.cpp
+@@ -21,11 +21,13 @@
+ #include <strigi/strigiconfig.h>
+ #include "arinputstream.h"
+ #include "analysisresult.h"
++#include "fieldtypes.h"
+ #include "subinputstream.h"
+ using namespace Strigi;
+
+ void
+ ArEndAnalyzerFactory::registerFields(FieldRegister& reg) {
++ typeField = reg.typeField;
+ }
+
+ bool
+@@ -34,7 +36,9 @@
+ }
+ char
+ ArEndAnalyzer::analyze(AnalysisResult& idx, InputStream* in) {
+- return staticAnalyze(idx, in);
++ char result = staticAnalyze(idx, in);
++ idx.addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#Archive");
++ return result;
+ }
+ char
+ ArEndAnalyzer::staticAnalyze(AnalysisResult& idx,
+--- a/src/streamanalyzer/endanalyzers/arendanalyzer.h
++++ b/src/streamanalyzer/endanalyzers/arendanalyzer.h
+@@ -23,8 +23,14 @@
+ #include "streamendanalyzer.h"
+ #include "streambase.h"
+
++class ArEndAnalyzerFactory;
+ class ArEndAnalyzer : public Strigi::StreamEndAnalyzer {
++private:
++ const ArEndAnalyzerFactory* factory;
+ public:
++ ArEndAnalyzer(const ArEndAnalyzerFactory* f)
++ :factory(f) {}
++
+ bool checkHeader(const char* header, int32_t headersize) const;
+ char analyze(Strigi::AnalysisResult& idx, Strigi::InputStream* in);
+ static char staticAnalyze(Strigi::AnalysisResult& idx,
+@@ -33,12 +39,15 @@
+ };
+
+ class ArEndAnalyzerFactory : public Strigi::StreamEndAnalyzerFactory {
++friend class ArEndAnalyzer;
++private:
++ const Strigi::RegisteredField* typeField;
+ public:
+ const char* name() const {
+ return "ArEndAnalyzer";
+ }
+ Strigi::StreamEndAnalyzer* newInstance() const {
+- return new ArEndAnalyzer();
++ return new ArEndAnalyzer(this);
+ }
+ bool analyzesSubStreams() const { return true; }
+ void registerFields(Strigi::FieldRegister&);
+--- a/src/streamanalyzer/endanalyzers/bmpendanalyzer.cpp
++++ b/src/streamanalyzer/endanalyzers/bmpendanalyzer.cpp
+@@ -40,6 +40,8 @@
+ widthField = reg.registerField(widthFieldName);
+ heightField = reg.registerField(heightFieldName);
+ colorDepthField = reg.registerField(colorDepthFieldName);
++
++ rdftypeField = reg.typeField;
+ }
+
+ bool
+@@ -115,6 +117,8 @@
+ rs.addValue(factory->compressionField, "Unknown");
+ }
+
++ rs.addValue(factory->rdftypeField, "http://freedesktop.org/standards/xesam/1.0/core#Image");
++
+ return 0;
+ }
+
+--- a/src/streamanalyzer/endanalyzers/bmpendanalyzer.h
++++ b/src/streamanalyzer/endanalyzers/bmpendanalyzer.h
+@@ -47,6 +47,9 @@
+ const Strigi::RegisteredField* heightField;
+ const Strigi::RegisteredField* colorDepthField;
+ const Strigi::RegisteredField* compressionField;
++
++ const Strigi::RegisteredField* rdftypeField;
++
+ const char* name() const {
+ return "BmpEndAnalyzer";
+ }
+--- a/src/streamanalyzer/endanalyzers/bz2endanalyzer.cpp
++++ b/src/streamanalyzer/endanalyzers/bz2endanalyzer.cpp
+@@ -24,11 +24,14 @@
+ #include "tarinputstream.h"
+ #include "streamanalyzer.h"
+ #include "analysisresult.h"
++#include "fieldtypes.h"
++
+ using namespace std;
+ using namespace Strigi;
+
+ void
+ Bz2EndAnalyzerFactory::registerFields(FieldRegister& reg) {
++ typeField = reg.typeField;
+ }
+
+ bool
+@@ -53,6 +56,7 @@
+ fprintf(stderr, "Error reading bz2: %s\n", stream.error());
+ return -2;
+ }
++ idx.addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#Archive");
+ stream.reset(0);
+ if (TarInputStream::checkHeader(start, nread)) {
+ return TarEndAnalyzer::staticAnalyze(idx, &stream);
+--- a/src/streamanalyzer/endanalyzers/bz2endanalyzer.h
++++ b/src/streamanalyzer/endanalyzers/bz2endanalyzer.h
+@@ -23,20 +23,29 @@
+ #include "streamendanalyzer.h"
+ #include "streambase.h"
+
++class Bz2EndAnalyzerFactory;
+ class Bz2EndAnalyzer : public Strigi::StreamEndAnalyzer {
++private:
++ const Bz2EndAnalyzerFactory* factory;
+ public:
++ Bz2EndAnalyzer(const Bz2EndAnalyzerFactory* f)
++ :factory(f) {}
++
+ bool checkHeader(const char* header, int32_t headersize) const;
+ char analyze(Strigi::AnalysisResult& idx, Strigi::InputStream* in);
+ const char* name() const { return "Bz2EndAnalyzer"; }
+ };
+
+ class Bz2EndAnalyzerFactory : public Strigi::StreamEndAnalyzerFactory {
++friend class Bz2EndAnalyzer;
++private:
++ const Strigi::RegisteredField* typeField;
+ public:
+ const char* name() const {
+ return "Bz2EndAnalyzer";
+ }
+ Strigi::StreamEndAnalyzer* newInstance() const {
+- return new Bz2EndAnalyzer();
++ return new Bz2EndAnalyzer(this);
+ }
+ bool analyzesSubStreams() const { return true; }
+ void registerFields(Strigi::FieldRegister&);
+--- a/src/streamanalyzer/endanalyzers/gzipendanalyzer.cpp
++++ b/src/streamanalyzer/endanalyzers/gzipendanalyzer.cpp
+@@ -23,11 +23,14 @@
+ #include "tarendanalyzer.h"
+ #include "tarinputstream.h"
+ #include "analysisresult.h"
++#include "fieldtypes.h"
++
+ using namespace Strigi;
+ using namespace std;
+
+ void
+ GZipEndAnalyzerFactory::registerFields(FieldRegister& reg) {
++ typeField = reg.typeField;
+ }
+
+ bool
+@@ -48,6 +51,9 @@
+ printf("Error reading gzip: %s\n", stream.error());
+ return -2;
+ }
++
++ idx.addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#Archive");
++
+ stream.reset(0);
+ if (TarInputStream::checkHeader(start, nread)) {
+ return TarEndAnalyzer::staticAnalyze(idx, &stream);
+--- a/src/streamanalyzer/endanalyzers/gzipendanalyzer.h
++++ b/src/streamanalyzer/endanalyzers/gzipendanalyzer.h
+@@ -23,20 +23,29 @@
+ #include "streamendanalyzer.h"
+ #include "streambase.h"
+
++class GZipEndAnalyzerFactory;
+ class GZipEndAnalyzer : public Strigi::StreamEndAnalyzer {
++private:
++ const GZipEndAnalyzerFactory* factory;
+ public:
++ GZipEndAnalyzer(const GZipEndAnalyzerFactory* f)
++ :factory(f) {}
++
+ bool checkHeader(const char* header, int32_t headersize) const;
+ char analyze(Strigi::AnalysisResult& idx, Strigi::InputStream* in);
+ const char* name() const { return "GZipEndAnalyzer"; }
+ };
+
+ class GZipEndAnalyzerFactory : public Strigi::StreamEndAnalyzerFactory {
++friend class GZipEndAnalyzer;
++private:
++ const Strigi::RegisteredField* typeField;
+ public:
+ const char* name() const {
+ return "GZipEndAnalyzer";
+ }
+ Strigi::StreamEndAnalyzer* newInstance() const {
+- return new GZipEndAnalyzer();
++ return new GZipEndAnalyzer(this);
+ }
+ bool analyzesSubStreams() const { return true; }
+ void registerFields(Strigi::FieldRegister&);
+--- a/src/streamanalyzer/endanalyzers/mailendanalyzer.cpp
++++ b/src/streamanalyzer/endanalyzers/mailendanalyzer.cpp
+@@ -30,7 +30,7 @@
+
+ const string MailEndAnalyzerFactory::titleFieldName = "http://freedesktop.org/standards/xesam/1.0/core#subject";
+ const string MailEndAnalyzerFactory::contenttypeFieldName = "http://freedesktop.org/standards/xesam/1.0/core#contentType";
+-const string MailEndAnalyzerFactory::fromFieldName = "TODO.from";
++const string MailEndAnalyzerFactory::fromFieldName = "http://freedesktop.org/standards/xesam/1.0/core#author";
+ const string MailEndAnalyzerFactory::toFieldName = "http://freedesktop.org/standards/xesam/1.0/core#to";
+ const string MailEndAnalyzerFactory::ccFieldName = "http://freedesktop.org/standards/xesam/1.0/core#cc";
+ const string MailEndAnalyzerFactory::bccFieldName = "http://freedesktop.org/standards/xesam/1.0/core#bcc";
+@@ -51,6 +51,8 @@
+ contentidField = r.registerField(contentidFieldName);
+ contentlinkField = r.registerField(contentlinkFieldName);
+ emailInReplyToField = r.registerField(emailInReplyToFieldName);
++
++ typeField = r.typeField;
+ }
+
+ bool
+@@ -87,6 +89,7 @@
+ if (enc.length()) {
+ idx.setEncoding(enc.c_str());
+ }
++ idx.addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#Email");
+ idx.addValue(factory->titleField, mail.subject());
+ idx.addValue(factory->contenttypeField, mail.contentType());
+ idx.addValue(factory->fromField, mail.from());
+--- a/src/streamanalyzer/endanalyzers/mailendanalyzer.h
++++ b/src/streamanalyzer/endanalyzers/mailendanalyzer.h
+@@ -57,6 +57,8 @@
+ const Strigi::RegisteredField* contentlinkField;
+ const Strigi::RegisteredField* emailInReplyToField;
+
++ const Strigi::RegisteredField* typeField;
++
+ public:
+ const char* name() const {
+ return "MailEndAnalyzer";
+--- a/src/streamanalyzer/endanalyzers/mpegendanalyzer.cpp
++++ b/src/streamanalyzer/endanalyzers/mpegendanalyzer.cpp
+@@ -38,6 +38,8 @@
+ fields["video codec"] = r.registerField("http://freedesktop.org/standards/xesam/1.0/core#videoCodec");
+ fields["audio codec"] = r.registerField("http://freedesktop.org/standards/xesam/1.0/core#audioCodec");
+ fields["aspect ratio"] = r.registerField("http://freedesktop.org/standards/xesam/1.0/core#aspectRatio");
++
++ fields["type"] = r.typeField;
+ }
+
+ bool MpegEndAnalyzer::checkHeader(const char* header, int32_t headersize) const
+@@ -132,6 +134,7 @@
+ break;
+ }
+ }
++ idx.addValue(tempfields["type"], "http://freedesktop.org/standards/xesam/1.0/core#Video");
+ return 0;
+ }
+
+--- a/src/streamanalyzer/endanalyzers/oleendanalyzer.cpp
++++ b/src/streamanalyzer/endanalyzers/oleendanalyzer.cpp
+@@ -163,6 +163,8 @@
+ if (r) (*m)[14] = r;
+ r = reg.registerField("ole.company", FieldRegister::stringType, 1, 0);
+ if (r) (*m)[15] = r;
++
++ typeField = reg.typeField;
+ }
+ const map<int, const RegisteredField*>*
+ OleEndAnalyzerFactory::getFieldMap(const string& key) const {
+@@ -392,6 +394,7 @@
+ m_error = ole.error();
+ return -1;
+ } else {
++ ar.addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#Document");
+ m_error.resize(0);
+ }
+ return 0;
+--- a/src/streamanalyzer/endanalyzers/oleendanalyzer.h
++++ b/src/streamanalyzer/endanalyzers/oleendanalyzer.h
+@@ -72,9 +72,11 @@
+ };
+
+ class OleEndAnalyzerFactory : public Strigi::StreamEndAnalyzerFactory {
++friend class OleEndAnalyzer;
+ private:
+ std::map<std::string,
+ std::map<int,const Strigi::RegisteredField*> > fieldsMaps;
++ const Strigi::RegisteredField* typeField;
+ public:
+ const char* name() const {
+ return "OleEndAnalyzer";
+--- a/src/streamanalyzer/endanalyzers/pdfendanalyzer.cpp
++++ b/src/streamanalyzer/endanalyzers/pdfendanalyzer.cpp
+@@ -21,6 +21,7 @@
+ #include "pdfendanalyzer.h"
+ #include <strigi/strigiconfig.h>
+ #include "analysisresult.h"
++#include "fieldtypes.h"
+ #include "textutils.h"
+ #include <sstream>
+ #include <cstring>
+@@ -29,7 +30,9 @@
+
+ void
+ PdfEndAnalyzerFactory::registerFields(FieldRegister& reg) {
++ typeField = reg.typeField;
+ }
++
+ PdfEndAnalyzer::PdfEndAnalyzer(const PdfEndAnalyzerFactory* f) :factory(f) {
+ parser.setStreamHandler(this);
+ parser.setTextHandler(this);
+@@ -57,5 +60,6 @@
+ n = 0;
+ StreamStatus r = parser.parse(in);
+ if (r != Eof) m_error.assign(parser.error());
++ analysisresult->addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#TextDocument");
+ return (r == Eof) ?0 :-1;
+ }
+--- a/src/streamanalyzer/endanalyzers/pdfendanalyzer.h
++++ b/src/streamanalyzer/endanalyzers/pdfendanalyzer.h
+@@ -46,6 +46,8 @@
+ class PdfEndAnalyzerFactory : public Strigi::StreamEndAnalyzerFactory {
+ friend class PdfEndAnalyzer;
+ private:
++ const Strigi::RegisteredField* typeField;
++public:
+ const char* name() const {
+ return "PdfEndAnalyzer";
+ }
+--- a/src/streamanalyzer/endanalyzers/pngendanalyzer.cpp
++++ b/src/streamanalyzer/endanalyzers/pngendanalyzer.cpp
+@@ -46,9 +46,10 @@
+ const string PngEndAnalyzerFactory::creationTimeFieldName("http://freedesktop.org/standards/xesam/1.0/core#contentCreated");
+ const string PngEndAnalyzerFactory::softwareFieldName("http://freedesktop.org/standards/xesam/1.0/core#generator");
+ const string PngEndAnalyzerFactory::disclaimerFieldName("http://freedesktop.org/standards/xesam/1.0/core#disclaimer");
+-const string PngEndAnalyzerFactory::warningFieldName("content.warning");
++ // putting warning into comment field since it's the closest equivalent
++const string PngEndAnalyzerFactory::warningFieldName("http://freedesktop.org/standards/xesam/1.0/core#contentComment");
+ // PNG spec says Source is Device used to create the image
+-const string PngEndAnalyzerFactory::sourceFieldName("photo.camera_model");
++const string PngEndAnalyzerFactory::sourceFieldName("http://freedesktop.org/standards/xesam/1.0/core#cameraModel");
+ const string PngEndAnalyzerFactory::commentFieldName("http://freedesktop.org/standards/xesam/1.0/core#contentComment");
+
+ // and for the colors
+@@ -86,6 +87,8 @@
+ warningField = reg.registerField(warningFieldName);
+ sourceField = reg.registerField(sourceFieldName);
+ commentField = reg.registerField(commentFieldName);
++
++ typeField = reg.typeField;
+ }
+
+ PngEndAnalyzer::PngEndAnalyzer(const PngEndAnalyzerFactory* f) :factory(f) {
+@@ -128,6 +131,8 @@
+ return -1;
+ }
+
++ as.addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#Image");
++
+ // read the png dimensions
+ uint32_t width = readBigEndianUInt32(c+4);
+ uint32_t height = readBigEndianUInt32(c+8);
+--- a/src/streamanalyzer/endanalyzers/pngendanalyzer.h
++++ b/src/streamanalyzer/endanalyzers/pngendanalyzer.h
+@@ -82,6 +82,9 @@
+ const Strigi::RegisteredField* warningField;
+ const Strigi::RegisteredField* sourceField;
+ const Strigi::RegisteredField* commentField;
++
++ const Strigi::RegisteredField* typeField;
++
+ const char* name() const {
+ return "PngEndAnalyzer";
+ }
+--- a/src/streamanalyzer/endanalyzers/rpmendanalyzer.cpp
++++ b/src/streamanalyzer/endanalyzers/rpmendanalyzer.cpp
+@@ -22,10 +22,13 @@
+ #include "rpminputstream.h"
+ #include "subinputstream.h"
+ #include "analysisresult.h"
++#include "fieldtypes.h"
++
+ using namespace Strigi;
+
+ void
+ RpmEndAnalyzerFactory::registerFields(FieldRegister& reg) {
++ typeField = reg.typeField;
+ }
+
+ bool
+@@ -40,6 +43,7 @@
+ fprintf(stderr, "error: %s\n", rpm.error());
+ // exit(1);
+ }
++ idx.addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#SoftwarePackage");
+ while (s) {
+ idx.indexChild(rpm.entryInfo().filename, rpm.entryInfo().mtime,
+ s);
+--- a/src/streamanalyzer/endanalyzers/rpmendanalyzer.h
++++ b/src/streamanalyzer/endanalyzers/rpmendanalyzer.h
+@@ -23,20 +23,29 @@
+ #include "streamendanalyzer.h"
+ #include "streambase.h"
+
++class RpmEndAnalyzerFactory;
+ class RpmEndAnalyzer : public Strigi::StreamEndAnalyzer {
++private:
++ const RpmEndAnalyzerFactory* factory;
+ public:
++ RpmEndAnalyzer(const RpmEndAnalyzerFactory* f)
++ :factory(f) {}
++
+ bool checkHeader(const char* header, int32_t headersize) const;
+ char analyze(Strigi::AnalysisResult& idx, Strigi::InputStream* in);
+ const char* name() const { return "RpmEndAnalyzer"; }
+ };
+
+ class RpmEndAnalyzerFactory : public Strigi::StreamEndAnalyzerFactory {
++friend class RpmEndAnalyzer;
++private:
++ const Strigi::RegisteredField* typeField;
+ public:
+ const char* name() const {
+ return "RpmEndAnalyzer";
+ }
+ Strigi::StreamEndAnalyzer* newInstance() const {
+- return new RpmEndAnalyzer();
++ return new RpmEndAnalyzer(this);
+ }
+ bool analyzesSubStreams() const { return true; }
+ void registerFields(Strigi::FieldRegister&);
+--- a/src/streamanalyzer/endanalyzers/sdfendanalyzer.cpp
++++ b/src/streamanalyzer/endanalyzers/sdfendanalyzer.cpp
+@@ -24,11 +24,9 @@
+ using namespace Strigi;
+ using namespace std;
+
+-const string SdfEndAnalyzerFactory::moleculeCountFieldName = "chemistry.molecule_count";
+-
+ void
+ SdfEndAnalyzerFactory::registerFields(FieldRegister& r) {
+- moleculeCountField = r.registerField(moleculeCountFieldName);
++ moleculeCountField = r.registerField("http://rdf.openmolecules.net/0.9#moleculeCount");
+ }
+
+ bool
+--- a/src/streamanalyzer/endanalyzers/tarendanalyzer.cpp
++++ b/src/streamanalyzer/endanalyzers/tarendanalyzer.cpp
+@@ -22,10 +22,12 @@
+ #include "tarinputstream.h"
+ #include "subinputstream.h"
+ #include "analysisresult.h"
++#include "fieldtypes.h"
+ using namespace Strigi;
+
+ void
+ TarEndAnalyzerFactory::registerFields(FieldRegister& reg) {
++ typeField = reg.typeField;
+ }
+
+ bool
+@@ -34,7 +36,9 @@
+ }
+ char
+ TarEndAnalyzer::analyze(AnalysisResult& idx, InputStream* in) {
+- return staticAnalyze(idx, in);
++ char result = staticAnalyze(idx, in);
++ idx.addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#Archive");
++ return result;
+ }
+ char
+ TarEndAnalyzer::staticAnalyze(AnalysisResult& idx,
+--- a/src/streamanalyzer/endanalyzers/tarendanalyzer.h
++++ b/src/streamanalyzer/endanalyzers/tarendanalyzer.h
+@@ -23,8 +23,14 @@
+ #include "streamendanalyzer.h"
+ #include "streambase.h"
+
++class TarEndAnalyzerFactory;
+ class TarEndAnalyzer : public Strigi::StreamEndAnalyzer {
++private:
++ const TarEndAnalyzerFactory* factory;
+ public:
++ TarEndAnalyzer(const TarEndAnalyzerFactory* f)
++ :factory(f) {}
++
+ bool checkHeader(const char* header, int32_t headersize) const;
+ char analyze(Strigi::AnalysisResult& idx, Strigi::InputStream* in);
+ static char staticAnalyze(Strigi::AnalysisResult& idx,
+@@ -33,12 +39,15 @@
+ };
+
+ class TarEndAnalyzerFactory : public Strigi::StreamEndAnalyzerFactory {
++friend class TarEndAnalyzer;
++private:
++ const Strigi::RegisteredField* typeField;
+ public:
+ const char* name() const {
+ return "TarEndAnalyzer";
+ }
+ Strigi::StreamEndAnalyzer* newInstance() const {
+- return new TarEndAnalyzer();
++ return new TarEndAnalyzer(this);
+ }
+ bool analyzesSubStreams() const { return true; }
+ void registerFields(Strigi::FieldRegister&);
+--- a/src/streamanalyzer/endanalyzers/zipendanalyzer.cpp
++++ b/src/streamanalyzer/endanalyzers/zipendanalyzer.cpp
+@@ -28,6 +28,7 @@
+ void
+ ZipEndAnalyzerFactory::registerFields(FieldRegister& reg) {
+ mimetypefield = reg.mimetypeField;
++ typeField = reg.typeField;
+ }
+
+ bool
+@@ -45,6 +46,7 @@
+ fprintf(stderr, "error: %s\n", zip.error());
+ // exit(1);
+ }
++
+ while (s) {
+ // fprintf(stderr, "zip: %s\n", zip.entryInfo().filename.c_str());
+ idx.indexChild(zip.entryInfo().filename, zip.entryInfo().mtime,
+@@ -56,6 +58,7 @@
+ return -1;
+ } else {
+ idx.addValue(factory->mimetypefield, "application/zip");
++ idx.addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#Archive");
+ m_error.resize(0);
+ }
+ return 0;
+--- a/src/streamanalyzer/endanalyzers/zipendanalyzer.h
++++ b/src/streamanalyzer/endanalyzers/zipendanalyzer.h
+@@ -40,6 +40,8 @@
+ return "ZipEndAnalyzer";
+ }
+ const Strigi::RegisteredField* mimetypefield;
++ const Strigi::RegisteredField* typeField;
++
+ Strigi::StreamEndAnalyzer* newInstance() const {
+ return new ZipEndAnalyzer(this);
+ }
+--- a/src/streamanalyzer/endplugins/CMakeLists.txt
++++ b/src/streamanalyzer/endplugins/CMakeLists.txt
+@@ -22,9 +22,11 @@
+ install(TARGETS ${libname} LIBRARY DESTINATION ${LIB_DESTINATION}/strigi)
+ ENDMACRO(ADD_STRIGIEA)
+
+-if(EXIV2_FOUND)
+- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions")
++if(Exiv2_FOUND)
++ IF (CMAKE_COMPILER_IS_GNUCXX)
++ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions")
++ ENDIF(CMAKE_COMPILER_IS_GNUCXX)
+ include_directories(${EXIV2_INCLUDE_DIR})
+ ADD_STRIGIEA(jpeg jpegendanalyzer.cpp)
+ target_link_libraries(jpeg ${EXIV2_LIBRARIES})
+-endif(EXIV2_FOUND)
++endif(Exiv2_FOUND)
+--- a/src/streamanalyzer/endplugins/jpegendanalyzer.cpp
++++ b/src/streamanalyzer/endplugins/jpegendanalyzer.cpp
+@@ -128,31 +128,34 @@
+ const RegisteredField* userCommentField;
+ const RegisteredField* jpegProcessField;
+ const RegisteredField* thumbnailField;
++
++ const RegisteredField* typeField;
++
+ };
+
+-const string JpegEndAnalyzerFactory::commentFieldName("content.comment");
+-const string JpegEndAnalyzerFactory::manufacturerFieldName("photo.camera_manufacturer");
+-const string JpegEndAnalyzerFactory::modelFieldName("photo.camera_model");
+-const string JpegEndAnalyzerFactory::creationDateFieldName("content.creation_time");
+-const string JpegEndAnalyzerFactory::widthFieldName("image.width");
+-const string JpegEndAnalyzerFactory::heightFieldName("image.height");
+-const string JpegEndAnalyzerFactory::orientationFieldName("photo.orientation");
+-const string JpegEndAnalyzerFactory::colorModeFieldName("image.color_space");
+-const string JpegEndAnalyzerFactory::flashUsedFieldName("photo.flash_used");
+-const string JpegEndAnalyzerFactory::focalLengthFieldName("photo.focal_length");
+-const string JpegEndAnalyzerFactory::_35mmEquivalentFieldName("photo.35mm_equivalent");
+-const string JpegEndAnalyzerFactory::ccdWidthFieldName("photo.cdd_width");
+-const string JpegEndAnalyzerFactory::exposureTimeFieldName("photo.exposure_time");
+-const string JpegEndAnalyzerFactory::apertureFieldName("photo.aperture");
+-const string JpegEndAnalyzerFactory::focusDistFieldName("photo.focus_distance");
+-const string JpegEndAnalyzerFactory::exposureBiasFieldName("photo.exposure_bias");
+-const string JpegEndAnalyzerFactory::whiteBalanceFieldName("photo.white_balance");
+-const string JpegEndAnalyzerFactory::meteringModeFieldName("photo.metering_mode");
+-const string JpegEndAnalyzerFactory::exposureFieldName("photo.exposure_program");
+-const string JpegEndAnalyzerFactory::isoEquivFieldName("photo.iso_equivalent");
+-const string JpegEndAnalyzerFactory::jpegQualityFieldName("compressed.target_quality");
+-const string JpegEndAnalyzerFactory::userCommentFieldName("content.comment");
+-const string JpegEndAnalyzerFactory::jpegProcessFieldName("compressed.compression_algorithm");
++const string JpegEndAnalyzerFactory::commentFieldName("http://freedesktop.org/standards/xesam/1.0/core#contentComment");
++const string JpegEndAnalyzerFactory::manufacturerFieldName("http://freedesktop.org/standards/xesam/1.0/core#cameraManufacturer");
++const string JpegEndAnalyzerFactory::modelFieldName("http://freedesktop.org/standards/xesam/1.0/core#cameraModel");
++const string JpegEndAnalyzerFactory::creationDateFieldName("http://freedesktop.org/standards/xesam/1.0/core#contentCreated");
++const string JpegEndAnalyzerFactory::widthFieldName("http://freedesktop.org/standards/xesam/1.0/core#width");
++const string JpegEndAnalyzerFactory::heightFieldName("http://freedesktop.org/standards/xesam/1.0/core#height");
++const string JpegEndAnalyzerFactory::orientationFieldName("http://freedesktop.org/standards/xesam/1.0/core#orientation");
++const string JpegEndAnalyzerFactory::colorModeFieldName("http://freedesktop.org/standards/xesam/1.0/core#colorSpace");
++const string JpegEndAnalyzerFactory::flashUsedFieldName("http://freedesktop.org/standards/xesam/1.0/core#flashUsed");
++const string JpegEndAnalyzerFactory::focalLengthFieldName("http://freedesktop.org/standards/xesam/1.0/core#focalLength");
++const string JpegEndAnalyzerFactory::_35mmEquivalentFieldName("http://freedesktop.org/standards/xesam/1.0/core#35mmEquivalent");
++const string JpegEndAnalyzerFactory::ccdWidthFieldName("http://freedesktop.org/standards/xesam/1.0/core#ccdWidth");
++const string JpegEndAnalyzerFactory::exposureTimeFieldName("http://freedesktop.org/standards/xesam/1.0/core#exposureTime");
++const string JpegEndAnalyzerFactory::apertureFieldName("http://freedesktop.org/standards/xesam/1.0/core#aperture");
++const string JpegEndAnalyzerFactory::focusDistFieldName("http://freedesktop.org/standards/xesam/1.0/core#focusDistance");
++const string JpegEndAnalyzerFactory::exposureBiasFieldName("http://freedesktop.org/standards/xesam/1.0/core#exposureBias");
++const string JpegEndAnalyzerFactory::whiteBalanceFieldName("http://freedesktop.org/standards/xesam/1.0/core#whiteBalance");
++const string JpegEndAnalyzerFactory::meteringModeFieldName("http://freedesktop.org/standards/xesam/1.0/core#meteringMode");
++const string JpegEndAnalyzerFactory::exposureFieldName("http://freedesktop.org/standards/xesam/1.0/core#exposureProgram");
++const string JpegEndAnalyzerFactory::isoEquivFieldName("http://freedesktop.org/standards/xesam/1.0/core#isoEquivalent");
++const string JpegEndAnalyzerFactory::jpegQualityFieldName("http://freedesktop.org/standards/xesam/1.0/core#targetQuality");
++const string JpegEndAnalyzerFactory::userCommentFieldName("http://freedesktop.org/standards/xesam/1.0/core#userComment");
++const string JpegEndAnalyzerFactory::jpegProcessFieldName("http://freedesktop.org/standards/xesam/1.0/core#compressionAlgorithm");
+ const string JpegEndAnalyzerFactory::thumbnailFieldName("content.thumbnail");
+
+ /*
+@@ -164,22 +167,16 @@
+ commentField = r.registerField(commentFieldName, FieldRegister::stringType,
+ -1, 0);
+
+- exifFields["Exif.Image.DateTime"] = r.registerField(creationDateFieldName,
+- FieldRegister::stringType, -1, 0);
+- exifFields["Exif.Image.Make"] = r.registerField(manufacturerFieldName,
+- FieldRegister::stringType, -1, 0);
+- exifFields["Exif.Image.Model"] = r.registerField(modelFieldName,
+- FieldRegister::stringType, -1, 0);
+- exifFields["Exif.Photo.PixelXDimension"] = r.registerField(widthFieldName,
+- FieldRegister::integerType, -1, 0);
+- exifFields["Exif.Photo.PixelYDimension"] = r.registerField(heightFieldName,
+- FieldRegister::integerType, -1, 0);
++ exifFields["Exif.Image.DateTime"] = r.registerField(creationDateFieldName);
++ exifFields["Exif.Image.Make"] = r.registerField(manufacturerFieldName);
++ exifFields["Exif.Image.Model"] = r.registerField(modelFieldName);
++ exifFields["Exif.Photo.PixelXDimension"] = r.registerField(widthFieldName);
++ exifFields["Exif.Photo.PixelYDimension"] = r.registerField(heightFieldName);
+ exifFields["Exif.Image.Orientation"] = r.registerField(orientationFieldName,
+ FieldRegister::stringType, -1 ,0);
+ exifFields["Exif.Photo.Flash"] = r.registerField(flashUsedFieldName,
+ FieldRegister::integerType, -1, 0);
+- exifFields["Exif.Photo.FocalLength"] = r.registerField(focalLengthFieldName,
+- FieldRegister::floatType, -1, 0);
++ exifFields["Exif.Photo.FocalLength"] = r.registerField(focalLengthFieldName);
+ exifFields["Exif.Photo.FocalLengthIn35mmFilm"] = r.registerField(
+ _35mmEquivalentFieldName, FieldRegister::integerType, -1, 0);
+ exifFields["Exif.Photo.ExposureTime"] =
+@@ -194,15 +191,17 @@
+ r.registerField(meteringModeFieldName, FieldRegister::integerType,-1,0);
+
+
+- colorModeField = r.registerField(colorModeFieldName, FieldRegister::stringType, -1, 0);
+- ccdWidthField = r.registerField(ccdWidthFieldName, FieldRegister::stringType, -1, 0);
+- focusDistField = r.registerField(focusDistFieldName, FieldRegister::stringType, -1, 0);
+- exposureField = r.registerField(exposureFieldName, FieldRegister::stringType, -1, 0);
+- isoEquivField = r.registerField(isoEquivFieldName, FieldRegister::stringType, -1, 0);
+- jpegQualityField = r.registerField(jpegQualityFieldName, FieldRegister::stringType, -1, 0);
+- userCommentField = r.registerField(userCommentFieldName, FieldRegister::stringType, -1, 0);
+- jpegProcessField = r.registerField(jpegProcessFieldName, FieldRegister::stringType, -1, 0);
+- thumbnailField = r.registerField(thumbnailFieldName, FieldRegister::stringType, -1, 0);
++ colorModeField = r.registerField(colorModeFieldName);
++ ccdWidthField = r.registerField(ccdWidthFieldName);
++ focusDistField = r.registerField(focusDistFieldName);
++ exposureField = r.registerField(exposureFieldName);
++ isoEquivField = r.registerField(isoEquivFieldName);
++ jpegQualityField = r.registerField(jpegQualityFieldName);
++ userCommentField = r.registerField(userCommentFieldName);
++ jpegProcessField = r.registerField(jpegProcessFieldName);
++ thumbnailField = r.registerField(thumbnailFieldName);
++
++ typeField = r.typeField;
+ }
+
+ bool
+@@ -262,6 +261,13 @@
+ }
+
+ const Exiv2::ExifData& exif = img->exifData();
++ // if there's exif data, this is a photo, otherwise just an image
++ if( exif.size() ) {
++ ar.addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#Photo");
++ } else {
++ ar.addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#Image");
++ }
++
+ for (Exiv2::ExifData::const_iterator i = exif.begin(); i != exif.end();i++){
+ map<string,const RegisteredField*>::const_iterator f
+ = factory->exifFields.find(i->key());
+--- a/src/streamanalyzer/fieldproperties/chemical.rdfs
++++ b/src/streamanalyzer/fieldproperties/chemical.rdfs
+@@ -12,7 +12,7 @@
+ xmlns:xss="&xss;"
+ xmlns:rdfs="&rdfs;">
+
+-<rdfs:Class rdf:about="&xesam;ChemicalContent">
++<rdfs:Class rdf:about="&chemical;ChemicalContent">
+ <rdfs:subClassOf rdf:resource="&xesam;Content"/>
+ <rdfs:label>chemical:ChemicalContent</rdfs:label>
+ <rdfs:comment>Chemical Content</rdfs:comment>
+@@ -29,6 +29,7 @@
+ <rdfs:label>chemical:inchi</rdfs:label>
+ <rdfs:subPropertyOf rdf:resource="&xesam;id"/>
+ <rdfs:domain rdf:resource="&xesam;ChemicalContent"/>
++ <xss:tokenized>false</xss:tokenized>
+ <rdfs:comment>IUPAC International Chemical Identifier</rdfs:comment>
+ </rdf:Property>
+
+@@ -42,6 +43,7 @@
+ <rdf:Property rdf:about="&chemical;molecularFormula">
+ <rdfs:label>chemical:molecularFormula</rdfs:label>
+ <rdfs:domain rdf:resource="&xesam;ChemicalContent"/>
++ <xss:tokenized>false</xss:tokenized>
+ <rdfs:comment>Molecular Formula</rdfs:comment>
+ </rdf:Property>
+
+@@ -76,6 +78,24 @@
+ <rdfs:comment>Bond Count</rdfs:comment>
+ </rdf:Property>
+
++<rdf:Property rdf:about="&chemical;moleculeCount">
++ <rdfs:label>chemical:moleculeCount</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;ChemicalContent"/>
++ <rdfs:comment>Molecule Count</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&chemical;classification">
++ <rdfs:label>chemical:classification</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;ChemicalContent"/>
++ <rdfs:comment>Indication of some Chemical Class</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&chemical;isChiral">
++ <rdfs:label>chemical:isChiral</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;ChemicalContent"/>
++ <rdfs:subPropertyOf rdf:resource="&xesam;classification"/>
++ <rdfs:comment>Flag that indicates if the Content contains Chiral Species</rdfs:comment>
++</rdf:Property>
+
+ <rdfs:Class rdf:about="&xesam;ProteinStructure">
+ <rdfs:label>chemical:ProteinStructure</rdfs:label>
+@@ -90,6 +110,13 @@
+ <rdfs:comment>ProteinDataBase Identifier</rdfs:comment>
+ </rdf:Property>
+
++<rdf:Property rdf:about="&chemical;depositionDate">
++ <rdfs:label>chemical:depositionDate</rdfs:label>
++ <rdfs:subPropertyOf rdf:resource="&xesam;creationDate"/>
++ <rdfs:domain rdf:resource="&xesam;ProteinStructure"/>
++ <rdfs:comment>Date on which the PDB Entry was Added to the Database</rdfs:comment>
++</rdf:Property>
++
+ <rdf:Property rdf:about="&chemical;experimentalMethod">
+ <rdfs:label>chemical:experimentalMethod</rdfs:label>
+ <rdfs:domain rdf:resource="&xesam;ProteinStructure"/>
+--- a/src/streamanalyzer/fieldproperties/CMakeLists.txt
++++ b/src/streamanalyzer/fieldproperties/CMakeLists.txt
+@@ -1,7 +1,9 @@
+
+ install(FILES
+ xesam.rdfs
+-# strigi.rdfs
++ xesam-convenience.rdfs
++ strigi.rdfs
++
+ # chemical.rdfs
+
+ DESTINATION share/strigi/fieldproperties
+--- a/src/streamanalyzer/fieldproperties/strigi_audio.fieldproperties
++++ /dev/null
+@@ -1,38 +0,0 @@
+-[Field]
+-Uri=audio.channel_count
+-Name=Channel count
+-TypeUri=string
+-Description=Audio channel count
+-Comment=
+-
+-[Field]
+-Uri=audio.duration
+-ParentUri=media.duration
+-Name=Duration
+-TypeUri=string
+-Description=Audio duration
+-Comment=
+-
+-[Field]
+-Uri=audio.artist
+-ParentUri=content.author
+-Name=Artist
+-TypeUri=string
+-Description=Audio Artist
+-Comment=
+-
+-[Field]
+-Uri=audio.title
+-ParentUri=content.title
+-Name=Title
+-TypeUri=string
+-Description=Audio Title
+-Comment=
+-
+-[Field]
+-Uri=audio.album
+-ParentUri=media.album
+-Name=Album
+-TypeUri=string
+-Description=Audio Album
+-Comment=
+--- a/src/streamanalyzer/fieldproperties/strigi_av.fieldproperties
++++ /dev/null
+@@ -1,13 +0,0 @@
+-[Field]
+-Uri=av.audio_codec
+-Name=Audio codec
+-TypeUri=string
+-Description=Audio codec
+-Comment=
+-
+-[Field]
+-Uri=av.video_codec
+-Name=Video codec
+-TypeUri=string
+-Description=Video codec
+-Comment=
+--- a/src/streamanalyzer/fieldproperties/strigi_compressed.fieldproperties
++++ /dev/null
+@@ -1,23 +0,0 @@
+-[Field]
+-Uri=compressed.compression_algorithm
+-ParentUri=content.generator_settings
+-Name=Compression algo
+-TypeUri=string
+-Description=Compression algorithm
+-Comment=
+-
+-[Field]
+-Uri=compressed.compression_level
+-ParentUri=content.generator_settings
+-Name=Compression level
+-TypeUri=string
+-Description=Lossless compression level
+-Comment=
+-
+-[Field]
+-Uri=compressed.target_quality
+-ParentUri=content.generator_settings
+-Name=Target quality
+-TypeUri=string
+-Description=Lossy compression target quality
+-Comment=
+--- a/src/streamanalyzer/fieldproperties/strigi_container.fieldproperties
++++ /dev/null
+@@ -1,13 +0,0 @@
+-[Field]
+-Uri=container.item
+-Name=Contains
+-TypeUri=string
+-Description=Contains item
+-Comment=
+-
+-[Field]
+-Uri=container.item_count
+-Name=Item count
+-TypeUri=string
+-Description=Contained item count
+-Comment=
+--- a/src/streamanalyzer/fieldproperties/strigi_content.fieldproperties
++++ /dev/null
+@@ -1,265 +0,0 @@
+-[Field]
+-Uri=content.creator
+-Name=Creator
+-TypeUri=string
+-Description=Content creator
+-Comment=abstract class, use children. List of entities who created the content(book authors, music band, artist)
+-
+-[Field]
+-Uri=content.author
+-ParentUri=content.creator
+-Name=Author
+-TypeUri=string
+-Description=Content author
+-Comment=Primary content creator(s)
+-
+-[Field]
+-Uri=content.contributor
+-ParentUri=content.creator
+-Name=Contributor
+-TypeUri=string
+-Description=Content contributor
+-Comment=Secondary content creator(s)
+-
+-[Field]
+-Uri=content.maintainer
+-ParentUri=content.creator
+-Name=Maintainer
+-TypeUri=string
+-Description=Content maintainer
+-Comment=Entity keeping the content up to date in general or for a specific purpose.
+-
+-
+-
+-[Field]
+-Uri=content.creation_time
+-Name=Creation time
+-TypeUri=datetime
+-Description=Content creation time
+-Comment=this is different from system.creation_time in that this is the time when the content was actually produced. ID3 tag Year, for e-mail, this is send time etc
+-
+-[Field]
+-Uri=content.last_modified_time
+-Name=Modification time
+-TypeUri=datetime
+-Description=Content last modification time
+-Comment=this is different from system.last_modified_time in that this is the time when the content was actually modified, while system.last_modified_time may reflect file rename or some other operation.
+-
+-[Field]
+-Uri=content.genre
+-Name=Genre
+-TypeUri=string
+-Description=Content genre
+-Comment=music, literature, movie genre. Also can be used for pictures. Regular photos can be considered Documentary.
+-
+-[Field]
+-Uri=content.subject
+-Name=Subject
+-TypeUri=string
+-Description=Content subject
+-Comment= message subject, ODF.subject. Author's subject of the content.
+-
+-[Field]
+-Uri=content.title
+-Name=Title
+-TypeUri=string
+-Description=Content title
+-Comment=Author's title of the content. ODF.title, ID3.title
+-
+-[Field]
+-Uri=content.description
+-Name=Description
+-TypeUri=string
+-Description=Content description
+-Comment=Author's description of the content.ODF.description.
+-
+-[Field]
+-Uri=content.comment
+-Name=Comment
+-TypeUri=string
+-Description=Content comment
+-Comment=Author's comment on the content.
+-
+-[Field]
+-Uri=content.keyword
+-Name=Keyword
+-TypeUri=string
+-Description=Content keyword
+-Comment=
+-
+-[Field]
+-Uri=content.version
+-Name=Version
+-TypeUri=string
+-Description=Content version
+-Comment=application version. document revision
+-
+-[Field]
+-Uri=content.mime_type
+-Name=Mime type
+-TypeUri=string
+-Description=Content mime type
+-Comment=
+-
+-[Field]
+-Uri=content.size
+-Name=Content size
+-TypeUri=integer
+-Description=
+-Comment=unpacked size for archives. for other files this might be the same as system.size, might be actual data size w/o headers(not sure about this yet).
+-
+-[Field]
+-Uri=content.charset
+-Name=Charset
+-TypeUri=string
+-Description=Content character encoding
+-Comment=document character encoding. This will eventually be obsolete.
+-
+-[Field]
+-Uri=content.language
+-Name=Language
+-TypeUri=string
+-Description=Content language
+-Comment=
+-
+-[Field]
+-Uri=content.full_text
+-Name=Full text
+-TypeUri=string
+-Description=Content converted to plain-text
+-Comment=This is indexable text body for documents, for images we might eventually do OCR, and speech recognition for audio(hopefully by the end of the century).
+-
+-[Field]
+-Uri=content.ID
+-Name=ID
+-TypeUri=string
+-Description=Content ID
+-Comment=Generic content ID. Use subproperties.
+-
+-[Field]
+-Uri=content.doi
+-ParentUri=content.ID
+-Name=DOI
+-TypeUri=string
+-Description=Digital Object Identifier
+-Comment=http://www.doi.org/
+-
+-[Field]
+-Uri=content.thumbnail
+-Name=Thumbnail
+-TypeUri=binary
+-Description=Content thumbnail
+-Comment=
+-
+-
+-
+-[Field]
+-Uri=content.legal
+-Name=Legal Info
+-TypeUri=string
+-Description=Content Legal Info
+-Comment=
+-
+-[Field]
+-Uri=content.license_type
+-ParentUri=content.legal
+-Name=License type
+-TypeUri=string
+-Description=Content license type
+-Comment=GPL, BSD, proprietary
+-
+-[Field]
+-Uri=content.license
+-ParentUri=content.legal
+-Name=License
+-TypeUri=string
+-Description=Content license
+-Comment=
+-
+-[Field]
+-Uri=content.copyright
+-ParentUri=content.legal
+-Name=Copyright
+-TypeUri=string
+-Description=Content copyright notice
+-Comment=
+-
+-[Field]
+-Uri=content.disclaimer
+-ParentUri=content.legal
+-Name=Disclaimer
+-TypeUri=string
+-Description=Content disclaimer
+-Comment=
+-
+-[Field]
+-Uri=content.warning
+-ParentUri=content.legal
+-Name=Warning
+-TypeUri=string
+-Description=Warning of nature of content
+-Comment=
+-
+-
+-
+-[Field]
+-Uri=content.generator
+-Name=Generated with
+-TypeUri=string
+-Description=Content generator software
+-Comment=
+-
+-[Field]
+-Uri=content.generator_settings
+-Name=Generator settings
+-TypeUri=string
+-Description=Content generator software settings
+-Comment=
+-
+-[Field]
+-Uri=content.format_subtype
+-Name=Format subtype
+-TypeUri=string
+-Description=Content format subtype
+-Comment=
+-
+-
+-
+-[Field]
+-Uri=content.related
+-Name=Related
+-TypeUri=string
+-Description=Related content
+-Comment=abstract. use children.
+-
+-[Field]
+-Uri=content.depends
+-ParentUri=content.related
+-Name=Depends
+-TypeUri=string
+-Description=Content dependency
+-Comment=
+-
+-[Field]
+-Uri=content.conflicts
+-ParentUri=content.related
+-Name=Conflicts
+-TypeUri=string
+-Description=Content conflicts with
+-Comment=
+-
+-[Field]
+-Uri=content.supercedes
+-ParentUri=content.related
+-Name=Supercedes
+-TypeUri=string
+-Description=Content supercedes
+-Comment=
+-
+-[Field]
+-Uri=content.links
+-ParentUri=content.related
+-Name=Links
+-TypeUri=string
+-Description=Content links to
+-Comment=
+--- a/src/streamanalyzer/fieldproperties/strigi_email.fieldproperties
++++ /dev/null
+@@ -1,46 +0,0 @@
+-[Field]
+-Uri=email.from
+-ParentUri=message.sender
+-Name=From
+-TypeUri=string
+-Description=E-mail sender
+-Comment=
+-
+-[Field]
+-Uri=email.to
+-ParentUri=message.primary_recipient
+-Name=To
+-TypeUri=string
+-Description=E-mail recipient
+-Comment=
+-
+-[Field]
+-Uri=email.cc
+-ParentUri=message.secondary_recipient
+-Name=CC
+-TypeUri=string
+-Description=E-mail carbon copy
+-Comment=
+-
+-[Field]
+-Uri=email.bcc
+-ParentUri=message.secondary_recipient
+-Name=BCC
+-TypeUri=string
+-Description=E-mail blind carbon copy
+-Comment=
+-
+-[Field]
+-Uri=email.subject
+-ParentUri=message.subject
+-Name=Subject
+-TypeUri=string
+-Description=E-mail subject
+-Comment=
+-
+-[Field]
+-Uri=email.content_type
+-Name=Content-type
+-TypeUri=string
+-Description=E-mail content-type
+-Comment=
+--- a/src/streamanalyzer/fieldproperties/strigi_image.fieldproperties
++++ /dev/null
+@@ -1,64 +0,0 @@
+-[Field]
+-Uri=image.resolution.x
+-Name=Resolution X
+-TypeUri=integer
+-Description=Image resolution along X axis
+-Comment=
+-
+-[Field]
+-Uri=image.resolution.y
+-Name=Resolution Y
+-TypeUri=integer
+-Description=Image resolution along Y axis
+-Comment=
+-
+-[Field]
+-Uri=image.width
+-Name=Width
+-TypeUri=integer
+-Description=Image width
+-Comment=
+-
+-[Field]
+-Uri=image.height
+-Name=Height
+-TypeUri=integer
+-Description=Image height
+-Comment=
+-
+-[Field]
+-Uri=image.color_space
+-Name=Color space
+-TypeUri=string
+-Description=Image color space
+-Comment=RGB, RGBA, CMYK, Grayscale, Indexed(palette) and more exotic, for pictures. Something similar exists for video. TODO: complete list
+-
+-[Field]
+-Uri=image.color_depth
+-ParentUri=media.sample_format
+-Name=Color depth
+-TypeUri=string
+-Description=Image color depth
+-Comment=
+-
+-[Field]
+-Uri=image.color_count
+-ParentUri=media.sample_format
+-Name=Color_count
+-TypeUri=string
+-Description=Image color count
+-Comment=For palette
+-
+-[Field]
+-Uri=image.aspect_ratio
+-Name=Aspect ratio
+-TypeUri=string
+-Description=Image aspect ratio
+-Comment=user-friendly representation of dimensions.x/dimensions.y
+-
+-[Field]
+-Uri=image.interlace
+-Name=Interlace
+-TypeUri=string
+-Description=Image interlace mode
+-Comment=none|Adam7|
+--- a/src/streamanalyzer/fieldproperties/strigi_media.fieldproperties
++++ /dev/null
+@@ -1,55 +0,0 @@
+-[Field]
+-Uri=media.codec
+-Name=Codec
+-TypeUri=string
+-Description=Media codec
+-Comment=
+-
+-[Field]
+-Uri=media.bitrate.type
+-Name=Bitrate type
+-TypeUri=string
+-Description=
+-Comment=lossless, CBR, VBR, ABR
+-
+-[Field]
+-Uri=media.bitrate.value
+-Name=Bitrate
+-TypeUri=integer
+-Description=
+-Comment=n/a or file average for lossless and vbr; actual value for CBR, ABR; unit: bit
+-
+-[Field]
+-Uri=media.sample_rate
+-Name=Sample rate
+-TypeUri=integer
+-Description=Media sample rate
+-Comment=FPS for video, sample rate for audio
+-
+-[Field]
+-Uri=media.sample_count
+-Name=Sample count
+-TypeUri=integer
+-Description=Media sample count
+-Comment=1, for still pictures, -1 for (semi-)infinite streams like net radio/tv
+-
+-[Field]
+-Uri=media.sample_format
+-Name=Sample format
+-TypeUri=string
+-Description=Media sample format
+-Comment=color depth for images, sample size for audio. 8/16/24/32/X bit int/float. TODO: complete list
+-
+-[Field]
+-Uri=media.album
+-Name=Album
+-TypeUri=string
+-Description=Media Album
+-Comment=
+-
+-[Field]
+-Uri=media.duration
+-Name=Duration
+-TypeUri=integer
+-Description=Media Duration
+-Comment=
+--- a/src/streamanalyzer/fieldproperties/strigi_message.fieldproperties
++++ /dev/null
+@@ -1,52 +0,0 @@
+-[Field]
+-Uri=message.sender
+-Name=Sender
+-TypeUri=string
+-Description=Message sender
+-Comment=
+-
+-[Field]
+-Uri=message.recipient
+-Name=Recipient
+-TypeUri=string
+-Description=Message recipient
+-Comment=
+-
+-[Field]
+-Uri=message.primary_recipient
+-ParentUri=message.recipient
+-Name=Primary recipient
+-TypeUri=string
+-Description=Message primary recipient
+-Comment=
+-
+-[Field]
+-Uri=message.secondary_recipient
+-ParentUri=message.recipient
+-Name=Secondary recipient
+-TypeUri=string
+-Description=Message secondary recipient
+-Comment=a "witness" recipient along the lines of e-mail cc:
+-
+-[Field]
+-Uri=message.subject
+-ParentUri=content.subject
+-Name=Subject
+-TypeUri=string
+-Description=Message subject
+-Comment=
+-
+-[Field]
+-Uri=message.sent_time
+-ParentUri=content.creation_time
+-Name=Sent time
+-TypeUri=string
+-Description=Message sent time
+-Comment=
+-
+-[Field]
+-Uri=message.specific_ID
+-Name=ID
+-TypeUri=string
+-Description=Protocol-specific message ID
+-Comment= re: email ID tag
+--- a/src/streamanalyzer/fieldproperties/strigi.namespace
++++ /dev/null
+@@ -1 +0,0 @@
+-http://strigi.sourceforge.net/RDF/#
+\ No newline at end of file
+--- a/src/streamanalyzer/fieldproperties/strigi_photo.fieldproperties
++++ /dev/null
+@@ -1,104 +0,0 @@
+-[Field]
+-Uri=photo.camera_manufacturer
+-Name=Camera Manufacturer
+-TypeUri=string
+-Description=
+-Comment=
+-
+-[Field]
+-Uri=photo.camera_model
+-Name=Camera model
+-TypeUri=string
+-Description=
+-Comment=
+-
+-[Field]
+-Uri=photo.focal_length
+-Name=Focal length
+-TypeUri=string
+-Description=
+-Comment=
+-
+-[Field]
+-Uri=photo.exposure_time
+-Name=Exposure time
+-TypeUri=string
+-Description=
+-Comment=
+-
+-[Field]
+-Uri=photo.focus_distance
+-Name=Focus distance
+-TypeUri=string
+-Description=
+-Comment=
+-
+-[Field]
+-Uri=photo.white_balance
+-Name=White balance
+-TypeUri=string
+-Description=
+-Comment=
+-
+-[Field]
+-Uri=photo.flash_used
+-Name=Flash used
+-TypeUri=string
+-Description=
+-Comment=
+-
+-[Field]
+-Uri=photo.iso_equivalent
+-Name=ISO equivalent
+-TypeUri=string
+-Description=
+-Comment=
+-
+-[Field]
+-Uri=photo.orientation
+-Name=Orientation
+-TypeUri=string
+-Description=Photo orientation
+-Comment=
+-
+-[Field]
+-Uri=photo.exposure_program
+-Name=Exposure program
+-TypeUri=string
+-Description=Photo exposure program
+-Comment=
+-
+-[Field]
+-Uri=photo.exposure_bias
+-Name=Exposure bias
+-TypeUri=string
+-Description=Photo exposure bias
+-Comment=
+-
+-[Field]
+-Uri=photo.aperture
+-Name=Aperture
+-TypeUri=string
+-Description=Photo aperture
+-Comment=
+-
+-[Field]
+-Uri=photo.cdd_width
+-Name=CCD width
+-TypeUri=string
+-Description=Photo CCD width
+-Comment=
+-
+-[Field]
+-Uri=photo.metering_mode
+-Name=Metering mode
+-TypeUri=string
+-Description=Photo metering mode
+-Comment=
+-
+-[Field]
+-Uri=photo.35mm_equivalent
+-Name=35mm equivalent
+-TypeUri=string
+-Description=Photo 35mm equivalent
+-Comment=
+--- a/src/streamanalyzer/fieldproperties/strigi.rdfs
++++ b/src/streamanalyzer/fieldproperties/strigi.rdfs
+@@ -12,6 +12,13 @@
+ xmlns:xss="&xss;"
+ xmlns:rdfs="&rdfs;">
+
++<rdf:Property rdf:about="&rdf;type">
++ <rdfs:label>rdf:type</rdfs:label>
++ <rdfs:domain rdf:resource="&rdfs;Resource"/>
++ <rdfs:range rdf:resource="&rdfs;Class"/>
++ <rdfs:comment>Declaring this here so that it's picked up by FieldPropertiesDb</rdfs:comment>
++</rdf:Property>
++
+ <rdf:Property rdf:about="&strigi;depth">
+ <rdfs:label>strigi:depth</rdfs:label>
+ <rdfs:domain rdf:resource="&xesam;Source"/>
+@@ -26,4 +33,36 @@
+
+
+
++<rdf:Property rdf:about="&strigi;codeLineCount">
++ <rdfs:label>strigi:codeLineCount</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;SourceCode"/>
++ <rdfs:comment>Source code code line count</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&strigi;commentLineCount">
++ <rdfs:label>strigi:commentLineCount</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;SourceCode"/>
++ <rdfs:comment>Source code comment line count</rdfs:comment>
++</rdf:Property>
++
++
++
++<rdfs:Class rdf:about="&strigi;Cursor">
++ <rdfs:subClassOf rdf:resource="&xesam;Visual"/>
++ <rdfs:label>strigi:Cursor</rdfs:label>
++ <rdfs:comment>Cursor is an image with a hot spot.</rdfs:comment>
++</rdfs:Class>
++
++<rdf:Property rdf:about="&strigi;hotSpotX">
++ <rdfs:label>strigi:hotSpotX</rdfs:label>
++ <rdfs:domain rdf:resource="&strigi;Cursor"/>
++ <rdfs:comment>Cursor hot spot X coordinate</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&strigi;hotSpotY">
++ <rdfs:label>strigi:hotSpotY</rdfs:label>
++ <rdfs:domain rdf:resource="&strigi;Cursor"/>
++ <rdfs:comment>Cursor hot spot Y coordinate</rdfs:comment>
++</rdf:Property>
++
+ </rdf:RDF>
+--- a/src/streamanalyzer/fieldproperties/strigi_software.fieldproperties
++++ /dev/null
+@@ -1,39 +0,0 @@
+-[Field]
+-Uri=software.name
+-ParentUri=content.title
+-Name=Name
+-TypeUri=string
+-Description=Software name
+-Comment=
+-
+-[Field]
+-Uri=software.version
+-ParentUri=content.version
+-Name=Version
+-TypeUri=string
+-Description=Software version
+-Comment=
+-
+-[Field]
+-Uri=software.depends
+-Name=Depends on
+-TypeUri=string
+-Description=Software depends on
+-Comment=
+-
+-[Field]
+-Uri=software.section
+-ParentUri=content.keyword
+-Name=Section
+-TypeUri=string
+-Description=Software section
+-Comment=
+-
+-[Field]
+-Uri=software.maintainer
+-ParentUri=content.author
+-Name=Name
+-TypeUri=string
+-Description=Software maintainer
+-Comment=maintainer is the author of package itself.
+-
+--- a/src/streamanalyzer/fieldproperties/strigi_system.fieldproperties
++++ /dev/null
+@@ -1,76 +0,0 @@
+-[Field]
+-Uri=system.owner
+-Name=Owner
+-TypeUri=string
+-Description=File owner
+-Comment=
+-
+-[Field]
+-Uri=system.ID
+-Name=ID
+-TypeUri=integer
+-Description=Unique system ID
+-Comment=unique ID within the system, or who knows even network
+-
+-[Field]
+-Uri=system.creation_time
+-Name=Creation time
+-TypeUri=datetime
+-Description=File creation time
+-Comment=time when the file was created *locally*, unlike content.creation_time
+-
+-[Field]
+-Uri=system.last_modified_time
+-Name=Last modified time
+-TypeUri=datetime
+-Description=File last modified time
+-Comment=
+-
+-[Field]
+-Uri=system.last_accessed_time
+-Name=Last accessed time
+-TypeUri=datetime
+-Description=File last accessed time
+-Comment=
+-
+-[Field]
+-Uri=system.hash.sha1
+-Name=Hash
+-TypeUri=binary
+-Description=SHA-1 hash of file contents
+-Comment=
+-
+-[Field]
+-Uri=system.location
+-Name=Location
+-TypeUri=string
+-Description=File location
+-Comment=Path
+-
+-[Field]
+-Uri=system.file_name
+-Name=File name
+-TypeUri=string
+-Description=
+-Comment=
+-
+-[Field]
+-Uri=system.file_extension
+-Name=File extension
+-TypeUri=string
+-Description=
+-Comment=
+-
+-[Field]
+-Uri=system.depth
+-Name=Depth
+-TypeUri=string
+-Description=
+-Comment=
+-
+-[Field]
+-Uri=system.size
+-Name=Size
+-TypeUri=integer
+-Description=File size
+-Comment=actual file/data size as reported by filesystem. For archive contents this is packed size.
+--- a/src/streamanalyzer/fieldproperties/strigi_text.fieldproperties
++++ /dev/null
+@@ -1,20 +0,0 @@
+-[Field]
+-Uri=text.stats.char_count
+-Name=Character count
+-TypeUri=integer
+-Description=Text character count
+-Comment=
+-
+-[Field]
+-Uri=text.stats.word_count
+-Name=Word count
+-TypeUri=integer
+-Description=Text word count
+-Comment=
+-
+-[Field]
+-Uri=text.stats.line_count
+-Name=Line count
+-TypeUri=integer
+-Description=Text line count
+-Comment=
+--- a/src/streamanalyzer/fieldproperties/strigi_trash.fieldproperties
++++ /dev/null
+@@ -1,13 +0,0 @@
+-[Field]
+-Uri=trash.original_location
+-Name=Original location
+-TypeUri=string
+-Description=File original location
+-Comment=
+-
+-[Field]
+-Uri=trash.deletion_time
+-Name=Deletion time
+-TypeUri=string
+-Description=File deletion time
+-Comment=
+--- a/src/streamanalyzer/fieldproperties/strigi_user.fieldproperties
++++ /dev/null
+@@ -1,27 +0,0 @@
+-[Field]
+-Uri=user.tags
+-Name=Tags
+-TypeUri=string
+-Description=User tags
+-Comment=user-assigned tags. tags can be initially populated from paths(i.e. /music/rock/ballads => categories music, rock and ballads)
+-
+-[Field]
+-Uri=user.comment
+-Name=User comment
+-TypeUri=string
+-Description=Comment provided by user
+-Comment=
+-
+-[Field]
+-Uri=user.rating
+-Name=Rating
+-TypeUri=integer
+-Description=User's rating of the file
+-Comment=user's opinion on the quality of file. 0 to 100, 100 being best
+-
+-[Field]
+-Uri=user.usage_intensity
+-Name=Usage intensity
+-TypeUri=integer
+-Description=Usage intensity rating
+-Comment=how often and for how long this file is used. For example play count.
+--- a/src/streamanalyzer/fieldproperties/strigi_video.fieldproperties
++++ /dev/null
+@@ -1,7 +0,0 @@
+-[Field]
+-Uri=video.frame_rate
+-ParentUri=media.sample_rate
+-Name=Frame rate
+-TypeUri=string
+-Description=Video frame rate
+-Comment=
+--- a/src/streamanalyzer/fieldproperties/strigi_xml.fieldproperties
++++ /dev/null
+@@ -1,6 +0,0 @@
+-[Field]
+-Uri=xml.usesNamespace
+-Name=XML Namespace
+-TypeUri=string
+-Description=XML Namespace used in the XML
+-Comment=XML files may use multiple namespaces.
+--- /dev/null
++++ b/src/streamanalyzer/fieldproperties/xesam-convenience.rdfs
+@@ -0,0 +1,396 @@
++<?xml version='1.0' encoding='UTF-8'?>
++<!DOCTYPE rdf:RDF [
++ <!ENTITY rdf 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
++ <!ENTITY xesam 'http://freedesktop.org/standards/xesam/1.0/core#'>
++ <!ENTITY xss 'http://freedesktop.org/standards/xesam/1.0/schema#'>
++ <!ENTITY rdfs 'http://www.w3.org/2000/01/rdf-schema#'>
++]>
++<rdf:RDF xmlns:rdf="&rdf;"
++ xmlns:xesam="&xesam;"
++ xmlns:xss="&xss;"
++ xmlns:rdfs="&rdfs;">
++
++
++
++<rdf:Property rdf:about="&xesam;musicBrainzAlbumID">
++ <rdfs:label>xesam:musicBrainzAlbumID</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Audio"/>
++ <rdfs:subPropertyOf rdf:resource="&xesam;id"/>
++ <rdfs:comment>MusicBrainz album ID in UUID format</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;musicBrainzArtistID">
++ <rdfs:label>xesam:musicBrainzArtistID</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Audio"/>
++ <rdfs:subPropertyOf rdf:resource="&xesam;id"/>
++ <rdfs:comment>MusicBrainz artist ID in UUID format</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;musicBrainzAlbumArtistID">
++ <rdfs:label>xesam:musicBrainzAlbumArtistID</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Audio"/>
++ <rdfs:subPropertyOf rdf:resource="&xesam;id"/>
++ <rdfs:comment>MusicBrainz album artist ID in UUID format</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;musicBrainzTrackID">
++ <rdfs:label>xesam:musicBrainzTrackID</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Audio"/>
++ <rdfs:subPropertyOf rdf:resource="&xesam;id"/>
++ <rdfs:comment>MusicBrainz track ID in UUID format</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;musicBrainzFingerprint">
++ <rdfs:label>xesam:musicBrainzFingerprint</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Audio"/>
++ <rdfs:subPropertyOf rdf:resource="&xesam;fingerprint"/>
++ <rdfs:comment>MusicBrainz track fingerprint</rdfs:comment>
++</rdf:Property>
++
++
++
++
++<rdf:Property rdf:about="&xesam;isrc">
++ <rdfs:label>xesam:isrc</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Media"/>
++ <rdfs:subPropertyOf rdf:resource="&xesam;id"/>
++ <rdfs:comment>International Standard Recording Code</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;imdbId">
++ <rdfs:label>xesam:imdbId</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Video"/>
++ <rdfs:subPropertyOf rdf:resource="&xesam;id"/>
++ <rdfs:comment>IMDB.com video ID</rdfs:comment>
++</rdf:Property>
++
++
++
++
++
++
++
++
++
++
++
++
++
++<rdfs:Class rdf:about="&xesam;PIM">
++ <rdfs:subClassOf rdf:resource="&xesam;Content"/>
++ <rdfs:label>xesam:PIM</rdfs:label>
++ <rdfs:comment>Generic PIM</rdfs:comment>
++</rdfs:Class>
++
++<rdf:Property rdf:about="&xesam;attendee">
++ <rdfs:label>xesam:attendee</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Alarm"/>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;FreeBusy"/>
++ <rdfs:domain rdf:resource="&xesam;Journal"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM entry attendee</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionOrganizer">
++ <rdfs:label>xesam:actionOrganizer</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;FreeBusy"/>
++ <rdfs:domain rdf:resource="&xesam;Journal"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM entry organizer</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionContact">
++ <rdfs:label>xesam:actionContact</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;FreeBusy"/>
++ <rdfs:domain rdf:resource="&xesam;Journal"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM entry contact</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionURL">
++ <rdfs:label>xesam:actionURL</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;FreeBusy"/>
++ <rdfs:domain rdf:resource="&xesam;Journal"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM entry URL</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionTrigger">
++ <rdfs:label>xesam:actionTrigger</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Alarm"/>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM entry action trigger</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionEnd">
++ <rdfs:label>xesam:actionEnd</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;FreeBusy"/>
++ <rdfs:comment>PIM entry action end</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionDuration">
++ <rdfs:label>xesam:actionDuration</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Alarm"/>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;FreeBusy"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM entry action duration</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionStart">
++ <rdfs:label>xesam:actionStart</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;FreeBusy"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM entry action start</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionPriority">
++ <rdfs:label>xesam:actionPriority</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM entry priority</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionLocation">
++ <rdfs:label>xesam:actionLocation</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM entry location</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionStatus">
++ <rdfs:label>xesam:actionStatus</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;Journal"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM entry status</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionRecurrenceID">
++ <rdfs:label>xesam:actionRecurrenceID</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;Journal"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM entry recurrence ID</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionRecurrenceDate">
++ <rdfs:label>xesam:actionRecurrenceDate</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;Journal"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM entry recurrence date</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionRecurrenceRule">
++ <rdfs:label>xesam:actionRecurrenceRule</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;Journal"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM entry recurrence rule</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionExceptionDate">
++ <rdfs:label>xesam:actionExceptionDate</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;Journal"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM entry exception date</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionExceptionRule">
++ <rdfs:label>xesam:actionExceptionRule</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;Journal"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM entry exception rule</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionAccessClassification">
++ <rdfs:label>xesam:actionAccessClassification</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;Journal"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM entry access classification</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionResources">
++ <rdfs:label>xesam:actionResources</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>Equipment or resources anticipated for an activity</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;actionAlarm">
++ <rdfs:label>xesam:actionResources</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>PIM activity has alarm</rdfs:comment>
++</rdf:Property>
++
++
++
++
++
++
++<rdfs:Class rdf:about="&xesam;Alarm">
++ <rdfs:subClassOf rdf:resource="&xesam;PIM"/>
++ <rdfs:label>xesam:Alarm</rdfs:label>
++ <rdfs:comment>Alarm</rdfs:comment>
++</rdfs:Class>
++
++<rdf:Property rdf:about="&xesam;alarmRepeat">
++ <rdfs:label>xesam:alarmRepeat</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Alarm"/>
++ <rdfs:comment>Alarm repeat</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;alarmAction">
++ <rdfs:label>xesam:alarmAction</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Alarm"/>
++ <rdfs:comment>Alarm action</rdfs:comment>
++</rdf:Property>
++
++
++
++
++
++
++
++<rdfs:Class rdf:about="&xesam;Event">
++ <rdfs:subClassOf rdf:resource="&xesam;PIM"/>
++ <rdfs:label>xesam:Event</rdfs:label>
++ <rdfs:comment>Event</rdfs:comment>
++</rdfs:Class>
++
++<rdf:Property rdf:about="&xesam;eventTransparrent">
++ <rdfs:label>xesam:eventTransparrent</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:comment>Is event transparrent(makes person busy)</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;eventStart">
++ <rdfs:label>xesam:eventStart</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:comment>Event start time</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;eventEnd">
++ <rdfs:label>xesam:eventEnd</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:comment>Event end time</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;eventLocation">
++ <rdfs:label>xesam:eventLocation</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Event"/>
++ <rdfs:comment>Event location</rdfs:comment>
++</rdf:Property>
++
++
++
++
++
++
++<rdfs:Class rdf:about="&xesam;Task">
++ <rdfs:subClassOf rdf:resource="&xesam;PIM"/>
++ <rdfs:label>xesam:Task</rdfs:label>
++ <rdfs:comment>Task</rdfs:comment>
++</rdfs:Class>
++
++<rdf:Property rdf:about="&xesam;taskDue">
++ <rdfs:label>xesam:taskDue</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>Task due date/time</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;taskCompleted">
++ <rdfs:label>xesam:taskCompleted</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>Is task completed?</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;taskPercentComplete">
++ <rdfs:label>xesam:taskPercentComplete</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Task"/>
++ <rdfs:comment>Task completeness</rdfs:comment>
++</rdf:Property>
++
++
++
++
++
++<rdfs:Class rdf:about="&xesam;Journal">
++ <rdfs:subClassOf rdf:resource="&xesam;PIM"/>
++ <rdfs:label>xesam:Journal</rdfs:label>
++ <rdfs:comment>Journal</rdfs:comment>
++</rdfs:Class>
++
++
++
++<rdfs:Class rdf:about="&xesam;FreeBusy">
++ <rdfs:subClassOf rdf:resource="&xesam;PIM"/>
++ <rdfs:label>xesam:FreeBusy</rdfs:label>
++ <rdfs:comment>FreeBusy</rdfs:comment>
++</rdfs:Class>
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++<rdf:Property rdf:about="&xesam;icqContactMedium">
++ <rdfs:label>xesam:icqContactMedium</rdfs:label>
++ <rdfs:subPropertyOf rdf:resource="&xesam;imContactMedium"/>
++ <rdfs:domain rdf:resource="&xesam;Contact"/>
++ <rdfs:comment>Contact ICQ ID</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;aimContactMedium">
++ <rdfs:label>xesam:aimContactMedium</rdfs:label>
++ <rdfs:subPropertyOf rdf:resource="&xesam;imContactMedium"/>
++ <rdfs:domain rdf:resource="&xesam;Contact"/>
++ <rdfs:comment>Contact AIM ID</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;yahooContactMedium">
++ <rdfs:label>xesam:yahooContactMedium</rdfs:label>
++ <rdfs:subPropertyOf rdf:resource="&xesam;imContactMedium"/>
++ <rdfs:domain rdf:resource="&xesam;Contact"/>
++ <rdfs:comment>Contact Yahoo ID</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;msnContactMedium">
++ <rdfs:label>xesam:msnContactMedium</rdfs:label>
++ <rdfs:subPropertyOf rdf:resource="&xesam;imContactMedium"/>
++ <rdfs:domain rdf:resource="&xesam;Contact"/>
++ <rdfs:comment>Contact MSN ID</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;skypeContactMedium">
++ <rdfs:label>xesam:skypeContactMedium</rdfs:label>
++ <rdfs:subPropertyOf rdf:resource="&xesam;imContactMedium"/>
++ <rdfs:domain rdf:resource="&xesam;Contact"/>
++ <rdfs:comment>Contact Skype ID</rdfs:comment>
++</rdf:Property>
++
++
++
++
++
++</rdf:RDF>
+--- a/src/streamanalyzer/fieldproperties/xesam.namespace
++++ /dev/null
+@@ -1 +0,0 @@
+-undecided
+--- a/src/streamanalyzer/fieldproperties/xesam.rdfs
++++ b/src/streamanalyzer/fieldproperties/xesam.rdfs
+@@ -77,7 +77,7 @@
+ <rdf:Property rdf:about="&xesam;size">
+ <rdfs:label>xesam:size</rdfs:label>
+ <rdfs:domain rdf:resource="&xesam;Content"/>
+- <rdfs:comment>Content size</rdfs:comment>
++ <rdfs:comment>Content/data size in bytes. See also storageSize</rdfs:comment>
+ </rdf:Property>
+
+ <rdf:Property rdf:about="&xesam;creator">
+@@ -259,7 +259,7 @@
+ </rdf:Property>
+
+ <rdf:Property rdf:about="&xesam;asText">
+- <rdfs:label>xesam:comment</rdfs:label>
++ <rdfs:label>xesam:asText</rdfs:label>
+ <rdfs:domain rdf:resource="&xesam;Content"/>
+ <rdfs:comment>Content plain-text representation for indexing purposes</rdfs:comment>
+ </rdf:Property>
+@@ -301,30 +301,54 @@
+
+
+
+-<rdfs:Class rdf:about="&xesam;Folder">
++
++
++<rdfs:Class rdf:about="&xesam;Annotation">
+ <rdfs:subClassOf rdf:resource="&xesam;Content"/>
+- <rdfs:label>xesam:Archive</rdfs:label>
+- <rdfs:comment>Generic folder</rdfs:comment>
++ <rdfs:label>xesam:Annotation</rdfs:label>
++ <rdfs:comment>Generic annotation. Annotation provides a set of document description properties(like subject, title, description) for a list of objects it links to. It can link to other annotations, however interpretation of this may differ between specific annotation classes..</rdfs:comment>
++</rdfs:Class>
++
++
++<rdfs:Class rdf:about="&xesam;Folder">
++ <rdfs:subClassOf rdf:resource="&xesam;Annotation"/>
++ <rdfs:label>xesam:Folder</rdfs:label>
++ <rdfs:comment>Generic folder. In general, folders represent a tree-like structure(taxonomy), however on occasion this rule may violated in cases like symlinks.</rdfs:comment>
+ </rdfs:Class>
+
+ <rdfs:Class rdf:about="&xesam;Project">
+- <rdfs:subClassOf rdf:resource="&xesam;Content"/>
++ <rdfs:subClassOf rdf:resource="&xesam;Annotation"/>
+ <rdfs:label>xesam:Project</rdfs:label>
+ <rdfs:comment>Generic project</rdfs:comment>
+ </rdfs:Class>
+
+ <rdfs:Class rdf:about="&xesam;MediaList">
+- <rdfs:subClassOf rdf:resource="&xesam;Content"/>
++ <rdfs:subClassOf rdf:resource="&xesam;Annotation"/>
+ <rdfs:label>xesam:MediaList</rdfs:label>
+- <rdfs:comment>Generic media file list(playlist)</rdfs:comment>
++ <rdfs:comment>Generic media content list(playlist). Linking to other content types is forbidden</rdfs:comment>
+ </rdfs:Class>
+
+ <rdfs:Class rdf:about="&xesam;AudioList">
+ <rdfs:subClassOf rdf:resource="&xesam;MediaList"/>
+ <rdfs:label>xesam:AudioList</rdfs:label>
+- <rdfs:comment>Generic audio file list(playlist)</rdfs:comment>
++ <rdfs:comment>Generic audio list(playlist). Linking to other content types is forbidden</rdfs:comment>
++</rdfs:Class>
++
++<rdfs:Class rdf:about="&xesam;Tag">
++ <rdfs:subClassOf rdf:resource="&xesam;Annotation"/>
++ <rdfs:label>xesam:Tag</rdfs:label>
++ <rdfs:comment>Tag/keyword. As opposed to folders, there are no limitations on the structure of tags and arbitrary overlaps are possible.</rdfs:comment>
+ </rdfs:Class>
+
++<rdfs:Class rdf:about="&xesam;Bookmark">
++ <rdfs:subClassOf rdf:resource="&xesam;Annotation"/>
++ <rdfs:label>xesam:Bookmark</rdfs:label>
++ <rdfs:comment>Bookmark. Currently there's nothing that would distinguish bookmarks and tags</rdfs:comment>
++</rdfs:Class>
++
++
++
++
+
+
+
+@@ -563,279 +587,6 @@
+
+
+
+-<rdfs:Class rdf:about="&xesam;PIM">
+- <rdfs:subClassOf rdf:resource="&xesam;Content"/>
+- <rdfs:label>xesam:PIM</rdfs:label>
+- <rdfs:comment>Generic PIM</rdfs:comment>
+-</rdfs:Class>
+-
+-<rdf:Property rdf:about="&xesam;attendee">
+- <rdfs:label>xesam:attendee</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Alarm"/>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;FreeBusy"/>
+- <rdfs:domain rdf:resource="&xesam;Journal"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry attendee</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionOrganizer">
+- <rdfs:label>xesam:actionOrganizer</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;FreeBusy"/>
+- <rdfs:domain rdf:resource="&xesam;Journal"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry organizer</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionContact">
+- <rdfs:label>xesam:actionContact</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;FreeBusy"/>
+- <rdfs:domain rdf:resource="&xesam;Journal"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry contact</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionURL">
+- <rdfs:label>xesam:actionURL</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;FreeBusy"/>
+- <rdfs:domain rdf:resource="&xesam;Journal"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry URL</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionURL">
+- <rdfs:label>xesam:actionURL</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;FreeBusy"/>
+- <rdfs:domain rdf:resource="&xesam;Journal"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry URL</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionTrigger">
+- <rdfs:label>xesam:actionTrigger</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Alarm"/>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry action trigger</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionEnd">
+- <rdfs:label>xesam:actionEnd</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;FreeBusy"/>
+- <rdfs:comment>PIM entry action end</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionDuration">
+- <rdfs:label>xesam:actionDuration</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Alarm"/>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;FreeBusy"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry action duration</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionStart">
+- <rdfs:label>xesam:actionStart</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;FreeBusy"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry action start</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionPriority">
+- <rdfs:label>xesam:actionPriority</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry priority</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionLocation">
+- <rdfs:label>xesam:actionLocation</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry location</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionStatus">
+- <rdfs:label>xesam:actionStatus</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;Journal"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry status</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionRecurrenceID">
+- <rdfs:label>xesam:actionRecurrenceID</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;Journal"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry recurrence ID</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionRecurrenceDate">
+- <rdfs:label>xesam:actionRecurrenceDate</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;Journal"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry recurrence date</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionRecurrenceRule">
+- <rdfs:label>xesam:actionRecurrenceRule</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;Journal"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry recurrence rule</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionExceptionDate">
+- <rdfs:label>xesam:actionExceptionDate</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;Journal"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry exception date</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionExceptionRule">
+- <rdfs:label>xesam:actionExceptionRule</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;Journal"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry exception rule</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionAccessClassification">
+- <rdfs:label>xesam:actionAccessClassification</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;Journal"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM entry access classification</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionResources">
+- <rdfs:label>xesam:actionResources</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>Equipment or resources anticipated for an activity</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;actionAlarm">
+- <rdfs:label>xesam:actionResources</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>PIM activity has alarm</rdfs:comment>
+-</rdf:Property>
+-
+-
+-
+-
+-
+-
+-<rdfs:Class rdf:about="&xesam;Alarm">
+- <rdfs:subClassOf rdf:resource="&xesam;PIM"/>
+- <rdfs:label>xesam:Alarm</rdfs:label>
+- <rdfs:comment>Alarm</rdfs:comment>
+-</rdfs:Class>
+-
+-<rdf:Property rdf:about="&xesam;alarmRepeat">
+- <rdfs:label>xesam:alarmRepeat</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Alarm"/>
+- <rdfs:comment>Alarm repeat</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;alarmAction">
+- <rdfs:label>xesam:alarmAction</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Alarm"/>
+- <rdfs:comment>Alarm action</rdfs:comment>
+-</rdf:Property>
+-
+-
+-
+-
+-
+-
+-
+-<rdfs:Class rdf:about="&xesam;Event">
+- <rdfs:subClassOf rdf:resource="&xesam;PIM"/>
+- <rdfs:label>xesam:Event</rdfs:label>
+- <rdfs:comment>Event</rdfs:comment>
+-</rdfs:Class>
+-
+-<rdf:Property rdf:about="&xesam;eventTransparrent">
+- <rdfs:label>xesam:eventTransparrent</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:comment>Is event transparrent(makes person busy)</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;eventStart">
+- <rdfs:label>xesam:eventStart</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:comment>Event start time</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;eventEnd">
+- <rdfs:label>xesam:eventEnd</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:comment>Event end time</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;eventLocation">
+- <rdfs:label>xesam:eventLocation</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Event"/>
+- <rdfs:comment>Event location</rdfs:comment>
+-</rdf:Property>
+-
+-
+-
+-
+-
+-
+-<rdfs:Class rdf:about="&xesam;Task">
+- <rdfs:subClassOf rdf:resource="&xesam;PIM"/>
+- <rdfs:label>xesam:Task</rdfs:label>
+- <rdfs:comment>Task</rdfs:comment>
+-</rdfs:Class>
+-
+-<rdf:Property rdf:about="&xesam;taskDue">
+- <rdfs:label>xesam:taskDue</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>Task due date/time</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;taskCompleted">
+- <rdfs:label>xesam:taskCompleted</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>Is task completed?</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;taskPercentComplete">
+- <rdfs:label>xesam:taskPercentComplete</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Task"/>
+- <rdfs:comment>Task completeness</rdfs:comment>
+-</rdf:Property>
+-
+-
+-
+-
+-
+-<rdfs:Class rdf:about="&xesam;Journal">
+- <rdfs:subClassOf rdf:resource="&xesam;PIM"/>
+- <rdfs:label>xesam:Journal</rdfs:label>
+- <rdfs:comment>Journal</rdfs:comment>
+-</rdfs:Class>
+-
+-
+-
+-<rdfs:Class rdf:about="&xesam;FreeBusy">
+- <rdfs:subClassOf rdf:resource="&xesam;PIM"/>
+- <rdfs:label>xesam:FreeBusy</rdfs:label>
+- <rdfs:comment>FreeBusy</rdfs:comment>
+-</rdfs:Class>
+
+
+
+@@ -903,6 +654,7 @@
+ <rdfs:comment>Document is an arrangement of various atomic data types with text being the primary data type.</rdfs:comment>
+ </rdfs:Class>
+
++
+ <rdf:Property rdf:about="&xesam;documentCategory">
+ <rdfs:label>xesam:documentCategory</rdfs:label>
+ <rdfs:domain rdf:resource="&xesam;Document"/>
+@@ -924,6 +676,11 @@
+
+
+
++<rdfs:Class rdf:about="&xesam;Documentation">
++ <rdfs:subClassOf rdf:resource="&xesam;Document"/>
++ <rdfs:label>xesam:Documentation</rdfs:label>
++ <rdfs:comment>Documentation is a document containing help, manuals, guides.</rdfs:comment>
++</rdfs:Class>
+
+
+
+@@ -1514,36 +1271,12 @@
+ <rdfs:comment>Contact</rdfs:comment>
+ </rdfs:Class>
+
+-<rdf:Property rdf:about="&xesam;contactName">
+- <rdfs:label>xesam:contactName</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Contact"/>
+- <rdfs:comment>Contact name</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;contactPhoto">
+- <rdfs:label>xesam:contactPhoto</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Contact"/>
+- <rdfs:comment>Contact photo/avatar</rdfs:comment>
+-</rdf:Property>
+-
+ <rdf:Property rdf:about="&xesam;contactNick">
+ <rdfs:label>xesam:contactNick</rdfs:label>
+ <rdfs:domain rdf:resource="&xesam;Contact"/>
+ <rdfs:comment>Contact nick</rdfs:comment>
+ </rdf:Property>
+
+-<rdf:Property rdf:about="&xesam;gender">
+- <rdfs:label>xesam:gender</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Contact"/>
+- <rdfs:comment>Contact gender</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;birthDate">
+- <rdfs:label>xesam:birthDate</rdfs:label>
+- <rdfs:domain rdf:resource="&xesam;Contact"/>
+- <rdfs:comment>Contact birthDate</rdfs:comment>
+-</rdf:Property>
+-
+ <rdf:Property rdf:about="&xesam;interests">
+ <rdfs:label>xesam:interests</rdfs:label>
+ <rdfs:domain rdf:resource="&xesam;Contact"/>
+@@ -1570,39 +1303,11 @@
+ <rdfs:comment>Contact Jabber ID</rdfs:comment>
+ </rdf:Property>
+
+-<rdf:Property rdf:about="&xesam;icqContactMedium">
+- <rdfs:label>xesam:icqContactMedium</rdfs:label>
+- <rdfs:subPropertyOf rdf:resource="&xesam;imContactMedium"/>
+- <rdfs:domain rdf:resource="&xesam;Contact"/>
+- <rdfs:comment>Contact ICQ ID</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;aimContactMedium">
+- <rdfs:label>xesam:aimContactMedium</rdfs:label>
+- <rdfs:subPropertyOf rdf:resource="&xesam;imContactMedium"/>
+- <rdfs:domain rdf:resource="&xesam;Contact"/>
+- <rdfs:comment>Contact AIM ID</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;yahooContactMedium">
+- <rdfs:label>xesam:yahooContactMedium</rdfs:label>
+- <rdfs:subPropertyOf rdf:resource="&xesam;imContactMedium"/>
+- <rdfs:domain rdf:resource="&xesam;Contact"/>
+- <rdfs:comment>Contact Yahoo ID</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;msnContactMedium">
+- <rdfs:label>xesam:msnContactMedium</rdfs:label>
+- <rdfs:subPropertyOf rdf:resource="&xesam;imContactMedium"/>
+- <rdfs:domain rdf:resource="&xesam;Contact"/>
+- <rdfs:comment>Contact MSN ID</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;skypeContactMedium">
+- <rdfs:label>xesam:skypeContactMedium</rdfs:label>
++<rdf:Property rdf:about="&xesam;ircContactMedium">
++ <rdfs:label>xesam:ircContactMedium</rdfs:label>
+ <rdfs:subPropertyOf rdf:resource="&xesam;imContactMedium"/>
+ <rdfs:domain rdf:resource="&xesam;Contact"/>
+- <rdfs:comment>Contact Skype ID</rdfs:comment>
++ <rdfs:comment>Contact IRC ID at server</rdfs:comment>
+ </rdf:Property>
+
+ <rdf:Property rdf:about="&xesam;postalAddress">
+@@ -1612,20 +1317,6 @@
+ <rdfs:comment>Contact postal address</rdfs:comment>
+ </rdf:Property>
+
+-<rdf:Property rdf:about="&xesam;homePostalAddress">
+- <rdfs:label>xesam:homePostalAddress</rdfs:label>
+- <rdfs:subPropertyOf rdf:resource="&xesam;postalAddress"/>
+- <rdfs:domain rdf:resource="&xesam;Contact"/>
+- <rdfs:comment>Contact home address</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;workPostalAddress">
+- <rdfs:label>xesam:workPostalAddress</rdfs:label>
+- <rdfs:subPropertyOf rdf:resource="&xesam;postalAddress"/>
+- <rdfs:domain rdf:resource="&xesam;Contact"/>
+- <rdfs:comment>Contact work address</rdfs:comment>
+-</rdf:Property>
+-
+ <rdf:Property rdf:about="&xesam;mailingPostalAddress">
+ <rdfs:label>xesam:mailingPostalAddress</rdfs:label>
+ <rdfs:subPropertyOf rdf:resource="&xesam;postalAddress"/>
+@@ -1640,20 +1331,6 @@
+ <rdfs:comment>Contact email address</rdfs:comment>
+ </rdf:Property>
+
+-<rdf:Property rdf:about="&xesam;workEmailAddress">
+- <rdfs:label>xesam:workEmailAddress</rdfs:label>
+- <rdfs:subPropertyOf rdf:resource="&xesam;emailAddress"/>
+- <rdfs:domain rdf:resource="&xesam;Contact"/>
+- <rdfs:comment>Contact work email address</rdfs:comment>
+-</rdf:Property>
+-
+-<rdf:Property rdf:about="&xesam;homeEmailAddress">
+- <rdfs:label>xesam:homeEmailAddress</rdfs:label>
+- <rdfs:subPropertyOf rdf:resource="&xesam;emailAddress"/>
+- <rdfs:domain rdf:resource="&xesam;Contact"/>
+- <rdfs:comment>Contact home email address</rdfs:comment>
+-</rdf:Property>
+-
+ <rdf:Property rdf:about="&xesam;contactURL">
+ <rdfs:label>xesam:contactURL</rdfs:label>
+ <rdfs:subPropertyOf rdf:resource="&xesam;contactMedium"/>
+@@ -1682,37 +1359,110 @@
+ <rdfs:comment>Contact phone number</rdfs:comment>
+ </rdf:Property>
+
+-<rdf:Property rdf:about="&xesam;homePhoneNumber">
+- <rdfs:label>xesam:homePhoneNumber</rdfs:label>
++<rdf:Property rdf:about="&xesam;cellPhoneNumber">
++ <rdfs:label>xesam:cellPhoneNumber</rdfs:label>
+ <rdfs:subPropertyOf rdf:resource="&xesam;phoneNumber"/>
+ <rdfs:domain rdf:resource="&xesam;Contact"/>
+- <rdfs:comment>Contact home phone number</rdfs:comment>
++ <rdfs:comment>Contact cell phone number</rdfs:comment>
+ </rdf:Property>
+
+-<rdf:Property rdf:about="&xesam;cellPhoneNumber">
+- <rdfs:label>xesam:cellPhoneNumber</rdfs:label>
++<rdf:Property rdf:about="&xesam;faxPhoneNumber">
++ <rdfs:label>xesam:faxPhoneNumber</rdfs:label>
+ <rdfs:subPropertyOf rdf:resource="&xesam;phoneNumber"/>
+ <rdfs:domain rdf:resource="&xesam;Contact"/>
+- <rdfs:comment>Contact cell phone number</rdfs:comment>
++ <rdfs:comment>Contact fax phone number</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;knows">
++ <rdfs:label>xesam:knows</rdfs:label>
++ <rdfs:subPropertyOf rdf:resource="&xesam;related"/>
++ <rdfs:domain rdf:resource="&xesam;Contact"/>
++ <rdfs:comment>FOAF:knows relation</rdfs:comment>
++</rdf:Property>
++
++
++
++
++
++
++<rdfs:Class rdf:about="&xesam;Person">
++ <rdfs:subClassOf rdf:resource="&xesam;Contact"/>
++ <rdfs:label>xesam:Person</rdfs:label>
++ <rdfs:comment>Person</rdfs:comment>
++</rdfs:Class>
++
++<rdf:Property rdf:about="&xesam;personPhoto">
++ <rdfs:label>xesam:personPhoto</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Person"/>
++ <rdfs:comment>Contact photo/avatar</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;gender">
++ <rdfs:label>xesam:gender</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Person"/>
++ <rdfs:comment>Contact gender</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;birthDate">
++ <rdfs:label>xesam:birthDate</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Person"/>
++ <rdfs:comment>Contact birthDate</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;homePostalAddress">
++ <rdfs:label>xesam:homePostalAddress</rdfs:label>
++ <rdfs:subPropertyOf rdf:resource="&xesam;postalAddress"/>
++ <rdfs:domain rdf:resource="&xesam;Person"/>
++ <rdfs:comment>Contact home address</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;workPostalAddress">
++ <rdfs:label>xesam:workPostalAddress</rdfs:label>
++ <rdfs:subPropertyOf rdf:resource="&xesam;postalAddress"/>
++ <rdfs:domain rdf:resource="&xesam;Person"/>
++ <rdfs:comment>Contact work address</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;workEmailAddress">
++ <rdfs:label>xesam:workEmailAddress</rdfs:label>
++ <rdfs:subPropertyOf rdf:resource="&xesam;emailAddress"/>
++ <rdfs:domain rdf:resource="&xesam;Person"/>
++ <rdfs:comment>Contact work email address</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;homeEmailAddress">
++ <rdfs:label>xesam:homeEmailAddress</rdfs:label>
++ <rdfs:subPropertyOf rdf:resource="&xesam;emailAddress"/>
++ <rdfs:domain rdf:resource="&xesam;Person"/>
++ <rdfs:comment>Contact home email address</rdfs:comment>
++</rdf:Property>
++
++<rdf:Property rdf:about="&xesam;homePhoneNumber">
++ <rdfs:label>xesam:homePhoneNumber</rdfs:label>
++ <rdfs:subPropertyOf rdf:resource="&xesam;phoneNumber"/>
++ <rdfs:domain rdf:resource="&xesam;Person"/>
++ <rdfs:comment>Contact home phone number</rdfs:comment>
+ </rdf:Property>
+
+ <rdf:Property rdf:about="&xesam;workPhoneNumber">
+ <rdfs:label>xesam:workPhoneNumber</rdfs:label>
+ <rdfs:subPropertyOf rdf:resource="&xesam;phoneNumber"/>
+- <rdfs:domain rdf:resource="&xesam;Contact"/>
++ <rdfs:domain rdf:resource="&xesam;Person"/>
+ <rdfs:comment>Contact work phone number</rdfs:comment>
+ </rdf:Property>
+
+-<rdf:Property rdf:about="&xesam;faxPhoneNumber">
+- <rdfs:label>xesam:faxPhoneNumber</rdfs:label>
+- <rdfs:subPropertyOf rdf:resource="&xesam;phoneNumber"/>
+- <rdfs:domain rdf:resource="&xesam;Contact"/>
+- <rdfs:comment>Contact fax phone number</rdfs:comment>
+-</rdf:Property>
+
+
+
+
++<rdfs:Class rdf:about="&xesam;Organization">
++ <rdfs:subClassOf rdf:resource="&xesam;Contact"/>
++ <rdfs:label>xesam:Organization</rdfs:label>
++ <rdfs:comment>Organization</rdfs:comment>
++</rdfs:Class>
++
++
++
+
+
+
+@@ -1800,7 +1550,7 @@
+ <rdf:Property rdf:about="&xesam;storageSize">
+ <rdfs:label>xesam:storageSize</rdfs:label>
+ <rdfs:domain rdf:resource="&xesam;Source"/>
+- <rdfs:comment>Space occupied by the object in the source storage</rdfs:comment>
++ <rdfs:comment>Actual space occupied by the object in the source storage. e.g. compressed file size in archive</rdfs:comment>
+ </rdf:Property>
+
+ <rdf:Property rdf:about="&xesam;sourceCreated">
+@@ -1819,20 +1569,20 @@
+ <rdfs:label>xesam:userComment</rdfs:label>
+ <rdfs:subPropertyOf rdf:resource="&xesam;comment"/>
+ <rdfs:domain rdf:resource="&xesam;Source"/>
+- <rdfs:comment>User comment</rdfs:comment>
++ <rdfs:comment>User-provided comment</rdfs:comment>
+ </rdf:Property>
+
+ <rdf:Property rdf:about="&xesam;userKeyword">
+ <rdfs:label>xesam:userKeyword</rdfs:label>
+ <rdfs:subPropertyOf rdf:resource="&xesam;keyword"/>
+ <rdfs:domain rdf:resource="&xesam;Source"/>
+- <rdfs:comment>User keywords</rdfs:comment>
++ <rdfs:comment>User-provided keywords</rdfs:comment>
+ </rdf:Property>
+
+ <rdf:Property rdf:about="&xesam;userRating">
+ <rdfs:label>xesam:userRating</rdfs:label>
+ <rdfs:domain rdf:resource="&xesam;Source"/>
+- <rdfs:comment>User rating of the object</rdfs:comment>
++ <rdfs:comment>User-provided rating of the object</rdfs:comment>
+ </rdf:Property>
+
+ <rdf:Property rdf:about="&xesam;autoRating">
+@@ -1881,7 +1631,7 @@
+ <rdfs:Class rdf:about="&xesam;OfflineMedia">
+ <rdfs:subClassOf rdf:resource="&xesam;Source"/>
+ <rdfs:label>xesam:OfflineMedia</rdfs:label>
+- <rdfs:comment>Generic offline media. e.g. USB drive that is not attached at this momeny.</rdfs:comment>
++ <rdfs:comment>Generic offline media. e.g. USB drive not attached at this moment.</rdfs:comment>
+ </rdfs:Class>
+
+ <rdf:Property rdf:about="&xesam;seenAttachedAs">
+@@ -1994,6 +1744,12 @@
+ <rdfs:comment>File permissions</rdfs:comment>
+ </rdf:Property>
+
++<rdf:Property rdf:about="&xesam;acl">
++ <rdfs:label>xesam:acl</rdfs:label>
++ <rdfs:domain rdf:resource="&xesam;Filelike"/>
++ <rdfs:comment>File access control list</rdfs:comment>
++</rdf:Property>
++
+ <rdf:Property rdf:about="&xesam;owner">
+ <rdfs:label>xesam:owner</rdfs:label>
+ <rdfs:domain rdf:resource="&xesam;Filelike"/>
+@@ -2018,7 +1774,7 @@
+ <rdfs:Class rdf:about="&xesam;File">
+ <rdfs:subClassOf rdf:resource="&xesam;Filelike"/>
+ <rdfs:label>xesam:File</rdfs:label>
+- <rdfs:comment>Regular file</rdfs:comment>
++ <rdfs:comment>Regular file stored in a filesystem</rdfs:comment>
+ </rdfs:Class>
+
+
+@@ -2116,7 +1872,7 @@
+ <rdfs:Class rdf:about="&xesam;ArchivedFile">
+ <rdfs:subClassOf rdf:resource="&xesam;Filelike"/>
+ <rdfs:label>xesam:ArchivedFile</rdfs:label>
+- <rdfs:comment>Generic archived file</rdfs:comment>
++ <rdfs:comment>File stored in an archive</rdfs:comment>
+ </rdfs:Class>
+
+ <rdf:Property rdf:about="&xesam;isSourceEncrypted">
+@@ -2149,7 +1905,7 @@
+ <rdfs:Class rdf:about="&xesam;MessageboxMessage">
+ <rdfs:subClassOf rdf:resource="&xesam;Source"/>
+ <rdfs:label>xesam:MessageboxMessage</rdfs:label>
+- <rdfs:comment>Generic messagebox message</rdfs:comment>
++ <rdfs:comment>Message stored in a message box</rdfs:comment>
+ </rdfs:Class>
+
+ <rdf:Property rdf:about="&xesam;isRead">
+@@ -2161,13 +1917,13 @@
+ <rdf:Property rdf:about="&xesam;isImportant">
+ <rdfs:label>xesam:isImportant</rdfs:label>
+ <rdfs:domain rdf:resource="&xesam;MessageboxMessage"/>
+- <rdfs:comment>Is the message important(Kontact tag)</rdfs:comment>
++ <rdfs:comment>Is the message important</rdfs:comment>
+ </rdf:Property>
+
+ <rdf:Property rdf:about="&xesam;isInProgress">
+ <rdfs:label>xesam:isInProgress</rdfs:label>
+ <rdfs:domain rdf:resource="&xesam;MessageboxMessage"/>
+- <rdfs:comment>Is the message in progress(Kontact tag)</rdfs:comment>
++ <rdfs:comment>Is the message in progress</rdfs:comment>
+ </rdf:Property>
+
+
+--- a/src/streamanalyzer/fieldpropertiesdb.cpp
++++ b/src/streamanalyzer/fieldpropertiesdb.cpp
+@@ -47,6 +47,7 @@
+ class FieldPropertiesDb::Private {
+ public:
+ map<string, FieldProperties> properties;
++ map<string, FieldProperties> propertiesByAlias;
+ map<string, ClassProperties> classes;
+ static const FieldProperties& emptyField();
+ static const ClassProperties& emptyClass();
+@@ -126,6 +127,18 @@
+ return j->second;
+ }
+ }
++
++const FieldProperties&
++FieldPropertiesDb::propertiesByAlias(const std::string& alias) const {
++ map<std::string, FieldProperties>::const_iterator j
++ = p->propertiesByAlias.find(alias);
++ if (j == p->propertiesByAlias.end()) {
++ return FieldPropertiesDb::Private::emptyField();
++ } else {
++ return j->second;
++ }
++}
++
+ const map<string, FieldProperties>&
+ FieldPropertiesDb::allProperties() const {
+ return p->properties;
+@@ -239,17 +252,35 @@
+ }
+
+ copy(pClasses.begin(), pClasses.end(), inserter(classes, classes.end()) );
+- copy(pProperties.begin(), pProperties.end(),
+- inserter(properties, properties.end()) );
++
++ // Construct properties and propertiesByAlias lists
++ for (map<string, FieldProperties::Private>::const_iterator prop = pProperties.begin();
++ prop != pProperties.end(); ++prop) {
++ FieldProperties property(prop->second);
++ string alias = prop->second.alias;
++
++ if(alias.size()) {
++ if(propertiesByAlias.find(alias) == propertiesByAlias.end()) {
++ propertiesByAlias[alias] = property;
++ } else {
++ cerr << "Error: alias " << alias << " requested by several properties" << endl;
++ }
++ }
++
++ properties[property.uri()] = property;
++ }
+
+ pProperties.clear();
+ pClasses.clear();
+ }
++
++// FIXME (phreedom): should not directly fill properties[]
++// not all properties from fieldtypes.* are created
+ void
+ FieldPropertiesDb::Private::addEssentialProperties() {
+- FieldProperties::Private props;
++ FieldProperties::Private props;
+ props.stored = true;
+-
++/*
+ props.typeuri = FieldRegister::datetimeType;
+ props.uri = FieldRegister::mtimeFieldName;
+ properties[FieldRegister::mtimeFieldName] = props;
+@@ -273,7 +304,7 @@
+
+ props.uri = FieldRegister::parentLocationFieldName;
+ props.tokenized = false;
+- properties[FieldRegister::parentLocationFieldName] = props;
++ properties[FieldRegister::parentLocationFieldName] = props;*/
+ }
+ void
+ FieldPropertiesDb::Private::loadProperties(const string& dir) {
+@@ -473,6 +504,14 @@
+ } else {
+ currentField.uri.assign(val);
+ }
++ } else if (strcmp(name, "alias") == 0) {
++ warnIfLocale(val.c_str(), currentElementLang);
++ if (currentField.alias.size()) {
++ cerr << "alias is already defined for " << currentField.uri << "."
++ << endl;
++ } else {
++ currentField.alias.assign(val);
++ }
+ } else if(strcmp(name, "range") == 0) {
+ warnIfLocale(currentField.uri.c_str(), currentElementLang);
+ if (currentField.typeuri.size()) {
+@@ -645,6 +684,12 @@
+ if (p->currentDefinition!=defNone) {
+ if (strcmp((const char *)localname, "Property") == 0) {
+ if (p->currentField.uri.size()) {
++ if(!p->currentField.alias.size()) {
++ size_t pos;
++ if( (pos = p->currentField.uri.rfind('#')) != string::npos) {
++ p->currentField.alias = p->currentField.uri.substr(pos+1);
++ }
++ }
+ p->pProperties[p->currentField.uri] = p->currentField;
+ p->currentField.clear();
+ }
+@@ -708,6 +753,7 @@
+ FieldProperties::Private::clear() {
+ uri.clear();
+ name.clear();
++ alias.clear();
+ description.clear();
+ localized.clear();
+ locales.clear();
+--- a/src/streamanalyzer/fieldpropertiesdb.h
++++ b/src/streamanalyzer/fieldpropertiesdb.h
+@@ -48,6 +48,7 @@
+ ~FieldPropertiesDb();
+
+ const FieldProperties& properties(const std::string& uri) const;
++ const FieldProperties& propertiesByAlias(const std::string& alias) const;
+ const std::map<std::string, FieldProperties>& allProperties() const;
+
+ const ClassProperties& classes(const std::string& uri) const;
+--- a/src/streamanalyzer/fieldproperties_private.h
++++ b/src/streamanalyzer/fieldproperties_private.h
+@@ -38,6 +38,7 @@
+ public:
+ std::string uri;
+ std::string name;
++ std::string alias;
+ std::string typeuri;
+ std::string description;
+ std::map<std::string,FieldProperties::Localized> localized;
+--- a/src/streamanalyzer/fieldtypes.cpp
++++ b/src/streamanalyzer/fieldtypes.cpp
+@@ -57,8 +57,9 @@
+ const string FieldRegister::embeddepthFieldName = "http://strigi.sf.net/ontologies/0.9#depth";
+ const string FieldRegister::mtimeFieldName = "http://freedesktop.org/standards/xesam/1.0/core#sourceModified";
+ const string FieldRegister::sizeFieldName = "http://freedesktop.org/standards/xesam/1.0/core#size";
++const string FieldRegister::typeFieldName = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
+
+-const string FieldRegister::defaultNamespace = "strigi.";
++const string FieldRegister::defaultNamespace = "http://strigi.sf.net/ontologies/0.9#";
+
+ FieldRegister::FieldRegister() {
+ pathField = registerField(pathFieldName);
+@@ -70,6 +71,7 @@
+ embeddepthField = registerField(embeddepthFieldName);//, integerType, 1, 0);
+ mtimeField = registerField(mtimeFieldName);//, integerType, 1, 0);
+ sizeField = registerField(sizeFieldName);//, integerType, 1, 0);
++ typeField = registerField(typeFieldName);
+ }
+
+ FieldRegister::~FieldRegister() {
+--- a/src/streamanalyzer/fieldtypes.h
++++ b/src/streamanalyzer/fieldtypes.h
+@@ -231,6 +231,8 @@
+ static const std::string mtimeFieldName;
+ /** The name of a field for storing the size of a file */
+ static const std::string sizeFieldName;
++ /** The name of a field for storing rdf:type of the file/data */
++ static const std::string typeFieldName;
+
+ /** Default namespace for fields */
+ static const std::string defaultNamespace;
+@@ -254,6 +256,9 @@
+ const RegisteredField* mtimeField;
+ /** A field for storing the size of a file */
+ const RegisteredField* sizeField;
++ /** A field for storing rdf:type of the file/data */
++ const RegisteredField* typeField;
++
+ };
+
+ }
+--- a/src/streamanalyzer/filelister.cpp
++++ b/src/streamanalyzer/filelister.cpp
+@@ -51,7 +51,7 @@
+ {
+ /*!
+ * @param path string containing path to check
+- * Appends the terminating char to path.
++ * Removes the terminating char to path.
+ * Under Windows that char is '\', '/' under *nix
+ */
+ string fixPath (string path)
+@@ -73,8 +73,8 @@
+
+ char separator = '/';
+
+- if (temp[temp.length() - 1 ] != separator)
+- temp += separator;
++ if (temp[temp.length() - 1 ] == separator)
++ return temp.substr(0, temp.size() - 1);
+
+ return temp;
+ }
+--- a/src/streamanalyzer/id3v2throughanalyzer.cpp
++++ b/src/streamanalyzer/id3v2throughanalyzer.cpp
+@@ -32,6 +32,7 @@
+ const string ID3V2ThroughAnalyzerFactory::composerFieldName("http://freedesktop.org/standards/xesam/1.0/core#composer");
+ const string ID3V2ThroughAnalyzerFactory::genreFieldName("http://freedesktop.org/standards/xesam/1.0/core#genre");
+ const string ID3V2ThroughAnalyzerFactory::trackNumberFieldName("http://freedesktop.org/standards/xesam/1.0/core#trackNumber");
++const string ID3V2ThroughAnalyzerFactory::albumTrackCountFieldName("http://freedesktop.org/standards/xesam/1.0/core#albumTrackCount");
+ const string ID3V2ThroughAnalyzerFactory::discNumberFieldName("http://freedesktop.org/standards/xesam/1.0/core#discNumber");
+
+ void
+@@ -42,7 +43,10 @@
+ genreField = r.registerField(genreFieldName);
+ composerField = r.registerField(composerFieldName);
+ trackNumberField = r.registerField(trackNumberFieldName); //FIXME:id3 track numbers can look like this: 1/10
++ albumTrackCountField = r.registerField(albumTrackCountFieldName);
+ discNumberField = r.registerField(discNumberFieldName); //FIXME:id3 disc numbers can looklike this: 1/2
++
++ typeField = r.typeField;
+ }
+
+ void
+@@ -87,12 +91,13 @@
+ // read the entire tag
+ nread = in->read(buf, size, size);
+ in->reset(0);
+- if (nread != size) {
++ if (nread != size || !indexable) {
+ return in;
+ }
++ indexable->addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#Music");
+ const char* p = buf + 10;
+ buf += size;
+- while (indexable && p < buf && *p) {
++ while (p < buf && *p) {
+ size = readSize((unsigned char*)p+4, async);
+ if (size < 0 || size > (buf-p)-11) {
+ // cerr << "size < 0: " << size << endl;
+--- a/src/streamanalyzer/id3v2throughanalyzer.h
++++ b/src/streamanalyzer/id3v2throughanalyzer.h
+@@ -49,6 +49,7 @@
+ static const std::string composerFieldName;
+ static const std::string genreFieldName;
+ static const std::string trackNumberFieldName;
++ static const std::string albumTrackCountFieldName;
+ static const std::string discNumberFieldName;
+ const Strigi::RegisteredField* titleField;
+ const Strigi::RegisteredField* artistField;
+@@ -56,7 +57,9 @@
+ const Strigi::RegisteredField* composerField;
+ const Strigi::RegisteredField* genreField;
+ const Strigi::RegisteredField* trackNumberField;
++ const Strigi::RegisteredField* albumTrackCountField;
+ const Strigi::RegisteredField* discNumberField;
++ const Strigi::RegisteredField* typeField;
+ const char* name() const {
+ return "ID3V2ThroughAnalyzer";
+ }
+--- a/src/streamanalyzer/lineplugins/CMakeLists.txt
++++ b/src/streamanalyzer/lineplugins/CMakeLists.txt
+@@ -23,3 +23,4 @@
+ ADD_STRIGILA(xpm xpmlineanalyzer.cpp)
+ ADD_STRIGILA(deb deblineanalyzer.cpp)
+ ADD_STRIGILA(cpp cpplineanalyzer.cpp)
++ADD_STRIGILA(txt txtlineanalyzer.cpp)
+\ No newline at end of file
+--- a/src/streamanalyzer/lineplugins/cpplineanalyzer.cpp
++++ b/src/streamanalyzer/lineplugins/cpplineanalyzer.cpp
+@@ -31,11 +31,13 @@
+ CppLineAnalyzerFactory::registerFields(FieldRegister& reg) {
+ includeField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#depends");
+ classField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#definesClass");
+- codeLinesField = reg.registerField("source_code.stats.code_line_count");
+- commentLinesField = reg.registerField("source_code.stats.comment_line_count");
++ codeLinesField = reg.registerField("http://strigi.sf.net/ontologies/0.9#codeLineCount");
++ commentLinesField = reg.registerField("http://strigi.sf.net/ontologies/0.9#commentLineCount");
+ totalLinesField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#lineCount");
++ programmingLanguageField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#programmingLanguage");
+ // Include count not required. Include list length is easy to obtain.
+ // includesField = reg.registerField();
++ typeField = reg.typeField;
+ }
+
+ // Analyzer
+@@ -96,6 +98,8 @@
+ analysisResult->addValue(factory->codeLinesField, (int32_t)codeLines);
+ analysisResult->addValue(factory->commentLinesField, (int32_t)commentLines);
+ analysisResult->addValue(factory->totalLinesField, (int32_t)totalLines);
++ analysisResult->addValue(factory->programmingLanguageField, "C++");
++ analysisResult->addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#SourceCode");
+ // analysisResult->addValue(factory->includesField, includes);
+ }
+ ready = true;
+--- a/src/streamanalyzer/lineplugins/cpplineanalyzer.h
++++ b/src/streamanalyzer/lineplugins/cpplineanalyzer.h
+@@ -59,6 +59,10 @@
+ const Strigi::RegisteredField* commentLinesField;
+ const Strigi::RegisteredField* totalLinesField;
+ const Strigi::RegisteredField* includesField;
++ const Strigi::RegisteredField* programmingLanguageField;
++
++ const Strigi::RegisteredField* typeField;
++
+ const char* name() const {
+ return "CppLineAnalyzer";
+ }
+--- a/src/streamanalyzer/lineplugins/deblineanalyzer.cpp
++++ b/src/streamanalyzer/lineplugins/deblineanalyzer.cpp
+@@ -45,6 +45,8 @@
+ maintainerField = r.registerField(maintainerFieldName);
+ sectionField = r.registerField(sectionFieldName);
+ dependsField = r.registerField(dependsFieldName);
++
++ typeField = r.typeField;
+ }
+
+ void
+@@ -63,9 +65,10 @@
+ // it is .deb file after all
+ result=res;
+ finished=0;
++ result->addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#SoftwarePackage");
+ }
+ void
+-DebLineAnalyzer::endAnalysis(bool /*complete*/) {
++DebLineAnalyzer::endAnalysis(bool complete) {
+ }
+
+ void
+--- a/src/streamanalyzer/lineplugins/deblineanalyzer.h
++++ b/src/streamanalyzer/lineplugins/deblineanalyzer.h
+@@ -53,6 +53,9 @@
+ const Strigi::RegisteredField* maintainerField;
+ const Strigi::RegisteredField* sectionField;
+ const Strigi::RegisteredField* dependsField;
++
++ const Strigi::RegisteredField* typeField;
++
+ const char* name() const {
+ return "DebLineAnalyzer";
+ }
+--- /dev/null
++++ b/src/streamanalyzer/lineplugins/txtlineanalyzer.cpp
+@@ -0,0 +1,114 @@
++/* This file is part of Strigi Desktop Search
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++
++#include "txtlineanalyzer.h"
++#include <strigi/strigiconfig.h>
++#include "analysisresult.h"
++#include "fieldtypes.h"
++
++using namespace std;
++using namespace Strigi;
++
++// AnalyzerFactory
++void
++TxtLineAnalyzerFactory::registerFields(FieldRegister& reg) {
++ totalLinesField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#lineCount");
++ //TODO: check names
++ totalWordsField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#wordsCount");
++ totalCharactersField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#charactersCount");
++ maxLineLengthField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#maxLineLength");
++ formatField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#format");
++}
++
++// Analyzer
++void
++TxtLineAnalyzer::startAnalysis(AnalysisResult* i) {
++ analysisResult = i;
++ totalLines = 0;
++ totalWords = 0;
++ totalCharacters = 0;
++ maxLineLength = 0;
++ dos = false;
++ ready = false;
++}
++void
++TxtLineAnalyzer::handleLine(const char* data, uint32_t length) {
++ bool inWord = false;
++
++ totalLines++;
++ totalCharacters += length;
++
++ if (maxLineLength < length)
++ maxLineLength = length;
++
++ // instead of using regexp use this elementary solution
++ for (unsigned int i = 0; i < length; i++) {
++ bool spacer = true;
++ if (isspace(data[i]) == 0)
++ spacer = false;
++
++ if (!spacer && !inWord) {// beginning of a word
++ totalWords++;
++ inWord = true;
++ }
++ //else if (!spacer && inWord) {/*inside word, do nothing*/}
++ else if (spacer && inWord)
++ inWord = false;
++ }
++
++ //TODO: by now it isn't possible to detect mac formatting
++ //endline should be just '\r'. I don't know if it is still true with latest
++ // versions of OSX, I have tried with tiger and I got a standard unix file.
++ if (length > 0 && data[length-1] == '\r')
++ dos = true;
++
++}
++void
++TxtLineAnalyzer::endAnalysis(bool complete) {
++ // we assume all cpp files must have includes
++ if (complete) {
++ analysisResult->addValue(factory->totalWordsField, (int32_t)totalWords);
++ analysisResult->addValue(factory->totalCharactersField, (int32_t)totalCharacters);
++ analysisResult->addValue(factory->totalLinesField, (int32_t)totalLines);
++ analysisResult->addValue(factory->maxLineLengthField, (int32_t)maxLineLength);
++ if (dos)
++ analysisResult->addValue(factory->formatField, "DOS");
++ else
++ analysisResult->addValue(factory->formatField, "UNIX");
++ }
++ ready = true;
++}
++bool
++TxtLineAnalyzer::isReadyWithStream() {
++ return ready;
++}
++
++//Factory
++class Factory : public AnalyzerFactoryFactory {
++public:
++ list<StreamLineAnalyzerFactory*>
++ streamLineAnalyzerFactories() const {
++ list<StreamLineAnalyzerFactory*> af;
++ af.push_back(new TxtLineAnalyzerFactory());
++ return af;
++ }
++};
++
++STRIGI_ANALYZER_FACTORY(Factory)
+--- /dev/null
++++ b/src/streamanalyzer/lineplugins/txtlineanalyzer.h
+@@ -0,0 +1,74 @@
++/* This file is part of Strigi Desktop Search
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++#ifndef STRIGI_TXTLINEANALYZER
++#define STRIGI_TXTLINEANALYZER
++
++#include "streamlineanalyzer.h"
++#include "analyzerplugin.h"
++
++namespace Strigi {
++ class RegisteredField;
++}
++class TxtLineAnalyzerFactory;
++
++class STRIGI_PLUGIN_API TxtLineAnalyzer
++ : public Strigi::StreamLineAnalyzer {
++private:
++ Strigi::AnalysisResult* analysisResult;
++ const TxtLineAnalyzerFactory* factory;
++ int totalWords;
++ int totalCharacters;
++ int totalLines;
++ uint32_t maxLineLength;
++ bool dos;
++ bool ready;
++public:
++ TxtLineAnalyzer(const TxtLineAnalyzerFactory* f) :factory(f) {}
++ ~TxtLineAnalyzer() {}
++ const char* name() const { return "TxtLineAnalyzer"; }
++ void startAnalysis(Strigi::AnalysisResult*);
++ void handleLine(const char* data, uint32_t length);
++ void endAnalysis(bool complete);
++ bool isReadyWithStream();
++};
++
++class TxtLineAnalyzerFactory
++ : public Strigi::StreamLineAnalyzerFactory {
++friend class TxtLineAnalyzer;
++private:
++ const Strigi::RegisteredField* totalLinesField;
++ const Strigi::RegisteredField* totalCharactersField;
++ const Strigi::RegisteredField* totalWordsField;
++ const Strigi::RegisteredField* maxLineLengthField;
++ const Strigi::RegisteredField* formatField;
++
++ const Strigi::RegisteredField* typeField;
++
++ const char* name() const {
++ return "TxtLineAnalyzer";
++ }
++ Strigi::StreamLineAnalyzer* newInstance() const {
++ return new TxtLineAnalyzer(this);
++ }
++ void registerFields(Strigi::FieldRegister&);
++};
++
++#endif
++
+--- a/src/streamanalyzer/lineplugins/xpmlineanalyzer.cpp
++++ b/src/streamanalyzer/lineplugins/xpmlineanalyzer.cpp
+@@ -33,6 +33,8 @@
+ widthField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#width");
+ heightField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#height");
+ numberOfColorsField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#colorCount");
++
++ typeField = reg.typeField;
+ }
+
+ // Analyzer
+@@ -94,6 +96,7 @@
+ return;
+
+ analysisResult->addValue(factory->numberOfColorsField, propertyValue);
++ analysisResult->addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#Image");
+ }
+ bool
+ XpmLineAnalyzer::isReadyWithStream() {
+--- a/src/streamanalyzer/lineplugins/xpmlineanalyzer.h
++++ b/src/streamanalyzer/lineplugins/xpmlineanalyzer.h
+@@ -52,6 +52,9 @@
+ const Strigi::RegisteredField* widthField;
+ const Strigi::RegisteredField* heightField;
+ const Strigi::RegisteredField* numberOfColorsField;
++
++ const Strigi::RegisteredField* typeField;
++
+ const char* name() const {
+ return "XpmLineAnalyzer";
+ }
+--- a/src/streamanalyzer/m3ustreamanalyzer.cpp
++++ b/src/streamanalyzer/m3ustreamanalyzer.cpp
+@@ -16,7 +16,7 @@
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+- * $Id: m3ustreamanalyzer.cpp 717846 2007-09-27 17:16:00Z evgeny $
++ * $Id: m3ustreamanalyzer.cpp 742095 2007-11-27 05:56:12Z evgeny $
+ */
+
+ #include "m3ustreamanalyzer.h"
+@@ -33,6 +33,8 @@
+ // tracksField = reg.registerField();
+ trackPathField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#links");
+ m3uTypeField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#formatSubtype");
++
++ typeField = reg.typeField;
+ }
+
+ // Analyzer
+@@ -80,5 +82,8 @@
+ // tracksField has not been initialized, so don't use it
+ //if (complete && extensionOk)
+ //analysisResult->addValue(factory->tracksField, count);
++ if (complete && extensionOk)
++ analysisResult->addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#AudioList");
++
+ }
+
+--- a/src/streamanalyzer/m3ustreamanalyzer.h
++++ b/src/streamanalyzer/m3ustreamanalyzer.h
+@@ -16,7 +16,7 @@
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+- * $Id: m3ustreamanalyzer.h 696786 2007-08-05 22:14:33Z vandenoever $
++ * $Id: m3ustreamanalyzer.h 742095 2007-11-27 05:56:12Z evgeny $
+ */
+
+ #ifndef M3USTREAMANALYZER_H
+@@ -60,6 +60,8 @@
+ const Strigi::RegisteredField* trackPathField; //The paths to the tracks in the playlist
+ const Strigi::RegisteredField* m3uTypeField; //The type of the m3u file, a simple list or an extended list
+
++ const Strigi::RegisteredField* typeField;
++
+ const char* name() const {
+ return "M3uLineAnalyzer";
+ }
+--- a/src/streamanalyzer/odfmimetypelineanalyzer.cpp
++++ b/src/streamanalyzer/odfmimetypelineanalyzer.cpp
+@@ -29,7 +29,8 @@
+ using namespace Strigi;
+
+ void OdfMimeTypeLineAnalyzerFactory::registerFields(FieldRegister ®) {
+- mimeTypeField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#mimeType");
++ mimeTypeField = reg.mimetypeField;
++ typeField = reg.typeField;
+ }
+
+ Strigi::StreamLineAnalyzer *OdfMimeTypeLineAnalyzerFactory::newInstance() const {
+@@ -67,6 +68,37 @@
+ m_ready = true;
+ return;
+ }
++/*
++application/vnd.oasis.opendocument.text odt
++application/vnd.oasis.opendocument.text-template ott
++application/vnd.oasis.opendocument.text-master odm
++application/vnd.oasis.opendocument.text-web oth
++application/vnd.oasis.opendocument.graphics odg
++application/vnd.oasis.opendocument.graphics-template otg
++application/vnd.oasis.opendocument.presentation odp
++application/vnd.oasis.opendocument.presentation-template otp
++application/vnd.oasis.opendocument.spreadsheet ods
++application/vnd.oasis.opendocument.spreadsheet-template ots
++application/vnd.oasis.opendocument.chart odc
++application/vnd.oasis.opendocument.chart-template otc
++application/vnd.oasis.opendocument.image odi
++application/vnd.oasis.opendocument.image-template oti
++application/vnd.oasis.opendocument.formula odf
++application/vnd.oasis.opendocument.formula-template otf
++*/
++
++ char *rdftype = NULL;
++ if( length >= (35+4) && std::strncmp(data+35, "text", 4) == 0 ) {
++ rdftype = "http://freedesktop.org/standards/xesam/1.0/core#TextDocument";
++ } else if ( length >= (35+12) && std::strncmp(data+35, "presentation", 12) == 0 ) {
++ rdftype = "http://freedesktop.org/standards/xesam/1.0/core#Presentation";
++ } else if ( length >= (35+11) && std::strncmp(data+35, "spreadsheet", 11) == 0 ) {
++ rdftype = "http://freedesktop.org/standards/xesam/1.0/core#Spreadsheet";
++ }
++
++ if(rdftype) {
++ m_result->addValue(m_factory->typeField, rdftype);
++ }
+
+ std::string mimeType;
+ mimeType.assign(data, length);
+--- a/src/streamanalyzer/odfmimetypelineanalyzer.h
++++ b/src/streamanalyzer/odfmimetypelineanalyzer.h
+@@ -27,6 +27,7 @@
+ class OdfMimeTypeLineAnalyzerFactory : public Strigi::StreamLineAnalyzerFactory {
+ public:
+ const Strigi::RegisteredField *mimeTypeField;
++ const Strigi::RegisteredField *typeField;
+
+ const char *name() const {
+ return "OdfMimeTypeLineAnalyzer";
+--- a/src/streamanalyzer/oggthroughanalyzer.cpp
++++ b/src/streamanalyzer/oggthroughanalyzer.cpp
+@@ -37,8 +37,20 @@
+ fields["genre"] = r.registerField("http://freedesktop.org/standards/xesam/1.0/core#genre");
+ fields["codec"] = r.registerField("http://freedesktop.org/standards/xesam/1.0/core#audioCodec");
+ fields["composer"] = r.registerField("http://freedesktop.org/standards/xesam/1.0/core#composer");
++ fields["performer"] = r.registerField("http://freedesktop.org/standards/xesam/1.0/core#performer");
+ fields["date"] = r.registerField("http://freedesktop.org/standards/xesam/1.0/core#contentCreated");
+ fields["description"] = r.registerField("http://freedesktop.org/standards/xesam/1.0/core#description");
++ fields["tracknumber"] = r.registerField("http://freedesktop.org/standards/xesam/1.0/core#trackNumber");
++
++
++ fields["version"] = r.registerField("http://freedesktop.org/standards/xesam/1.0/core#version");
++ fields["isrc"] = r.registerField("http://freedesktop.org/standards/xesam/1.0/core#isrc");
++ fields["copyright"] = r.registerField("http://freedesktop.org/standards/xesam/1.0/core#copyright");
++ fields["license"] = r.registerField("http://freedesktop.org/standards/xesam/1.0/core#license");
++
++// ogg spec fields left unimplemented: ORGANIZATION, LOCATION, CONTACT
++
++ fields["type"] = r.typeField;
+ }
+
+ void
+@@ -129,6 +141,8 @@
+ }
+ // set the "codec" value
+ indexable->addValue(factory->fields.find("codec")->second, "Ogg/Vorbis");
++ indexable->addValue(factory->fields.find("type")->second,
++ "http://freedesktop.org/standards/xesam/1.0/core#Music");
+ return in;
+ }
+ bool
+--- a/src/streamanalyzer/queryparser.cpp
++++ b/src/streamanalyzer/queryparser.cpp
+@@ -18,6 +18,7 @@
+ * Boston, MA 02110-1301, USA.
+ */
+ #include "queryparser.h"
++#include "fieldpropertiesdb.h"
+ #include <iostream>
+ #include <cstring>
+ using namespace std;
+@@ -125,8 +126,9 @@
+ // prepend the field names with the xesam namespace
+ // this will be elaborated once the xesam spec continues
+ vector<string>::iterator end(query.fields().end());
++ FieldPropertiesDb& db = FieldPropertiesDb::db();
+ for (vector<string>::iterator i = query.fields().begin(); i != end; ++i) {
+- *i = "http://freedesktop.org/standards/xesam/1.0/core#" + *i;
++ *i = db.propertiesByAlias(*i).uri();
+ }
+ std::vector<Query>::iterator qend(query.subQueries().end());
+ for (vector<Query>::iterator i = query.subQueries().begin(); i!=qend; ++i) {
+--- a/src/streamanalyzer/throughplugins/authroughanalyzer.cpp
++++ b/src/streamanalyzer/throughplugins/authroughanalyzer.cpp
+@@ -31,14 +31,18 @@
+ const string AuThroughAnalyzerFactory::lengthFieldName("http://freedesktop.org/standards/xesam/1.0/core#mediaDuration");
+ const string AuThroughAnalyzerFactory::sampleRateFieldName("http://freedesktop.org/standards/xesam/1.0/core#audioSampleRate");
+ const string AuThroughAnalyzerFactory::channelsFieldName("http://freedesktop.org/standards/xesam/1.0/core#audioChannels");
+-const string AuThroughAnalyzerFactory::encodingFieldName("media.sample_format");
++const string AuThroughAnalyzerFactory::sampleBitDepthFieldName("http://freedesktop.org/standards/xesam/1.0/core#audioSampleBitDepth");
++const string AuThroughAnalyzerFactory::sampleDataTypeFieldName("http://freedesktop.org/standards/xesam/1.0/core#audioSampleDataType");
+
+ void
+ AuThroughAnalyzerFactory::registerFields(FieldRegister& reg) {
+ lengthField = reg.registerField(lengthFieldName);
+ sampleRateField = reg.registerField(sampleRateFieldName);
+ channelsField = reg.registerField(channelsFieldName);
+- encodingField = reg.registerField(encodingFieldName);
++ sampleBitDepthField = reg.registerField(sampleBitDepthFieldName);
++ sampleDataTypeField = reg.registerField(sampleDataTypeFieldName);
++
++ typeField = reg.typeField;
+ }
+
+ // Analyzer
+@@ -82,48 +86,52 @@
+ uint16_t bytesPerSample = 0;
+ switch (encoding) {
+ case 1 :
+- analysisResult->addValue(factory->encodingField, "8-bit ISDN u-law");
++ analysisResult->addValue(factory->sampleDataTypeField, "ISDN u-law");
+ bytesPerSample = 1;
+ break;
+ case 2 :
+- analysisResult->addValue(factory->encodingField, "8-bit linear PCM [REF-PCM]");
++ analysisResult->addValue(factory->sampleDataTypeField, "linear PCM [REF-PCM]");
+ bytesPerSample = 1;
+ break;
+ case 3 :
+- analysisResult->addValue(factory->encodingField, "16-bit linear PCM");
++ analysisResult->addValue(factory->sampleDataTypeField, "linear PCM");
+ bytesPerSample = 2;
+ break;
+ case 4 :
+- analysisResult->addValue(factory->encodingField, "24-bit linear PCM");
++ analysisResult->addValue(factory->sampleDataTypeField, "linear PCM");
+ bytesPerSample = 3;
+ break;
+ case 5 :
+- analysisResult->addValue(factory->encodingField, "32-bit linear PCM");
++ analysisResult->addValue(factory->sampleDataTypeField, "linear PCM");
+ bytesPerSample = 4;
+ break;
+ case 6 :
+- analysisResult->addValue(factory->encodingField, "32-bit IEEE floating point");
++ analysisResult->addValue(factory->sampleDataTypeField, "IEEE floating point");
+ bytesPerSample = 4;
+ break;
+ case 7 :
+- analysisResult->addValue(factory->encodingField, "64-bit IEEE floating point");
++ analysisResult->addValue(factory->sampleDataTypeField, "IEEE floating point");
+ bytesPerSample = 8;
+ break;
+ case 23 :
+- analysisResult->addValue(factory->encodingField, "8-bit ISDN u-law compressed");
++ analysisResult->addValue(factory->sampleDataTypeField, "ISDN u-law compressed");
+ bytesPerSample = 1;
+ break;
+ default :
+- analysisResult->addValue(factory->encodingField, "Unknown");
++ analysisResult->addValue(factory->sampleDataTypeField, "Unknown");
+ bytesPerSample = 0;
+ }
+-
++ if(bytesPerSample) {
++ analysisResult->addValue(factory->sampleBitDepthField, bytesPerSample*8);
++ }
+ // work out length from bytespersample + channels + size
+ if ((0 < channels) && (0 < dataSize) && (dataSize != 0xFFFFFFFF) && (0 < bytesPerSample) && (0 < sampleRate)) {
+ uint32_t length = dataSize / channels / bytesPerSample / sampleRate;
+ analysisResult->addValue(factory->lengthField, length);
+ }
+
++ analysisResult->addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#Music");
++
+ return in;
+ }
+
+--- a/src/streamanalyzer/throughplugins/authroughanalyzer.h
++++ b/src/streamanalyzer/throughplugins/authroughanalyzer.h
+@@ -51,11 +51,17 @@
+ static const std::string lengthFieldName;
+ static const std::string sampleRateFieldName;
+ static const std::string channelsFieldName;
+- static const std::string encodingFieldName;
++ static const std::string sampleBitDepthFieldName;
++ static const std::string sampleDataTypeFieldName;
++
+ const Strigi::RegisteredField* lengthField;
+ const Strigi::RegisteredField* sampleRateField;
+ const Strigi::RegisteredField* channelsField;
+- const Strigi::RegisteredField* encodingField;
++ const Strigi::RegisteredField* sampleBitDepthField;
++ const Strigi::RegisteredField* sampleDataTypeField;
++
++ const Strigi::RegisteredField* typeField;
++
+ const char* name() const {
+ return "AuThroughAnalyzer";
+ }
+--- /dev/null
++++ b/src/streamanalyzer/throughplugins/avithroughanalyzer.cpp
+@@ -0,0 +1,553 @@
++/* This file is part of Strigi Desktop Search, ported from code of:
++ * - Shane Wright <me at shanewright.co.uk> (Copyright (C) 2002)
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++
++#include "avithroughanalyzer.h"
++#include "textutils.h"
++#include <strigi/strigiconfig.h>
++#include "analysisresult.h"
++#include "fieldtypes.h"
++#include <cstring>
++
++using namespace std;
++using namespace Strigi;
++
++// AnalyzerFactory
++
++//TODO: check values!
++const string AviThroughAnalyzerFactory::lengthFieldName( "http://freedesktop.org/standards/xesam/1.0/core#video.length" );
++const string AviThroughAnalyzerFactory::resolutionHeightFieldName( "http://freedesktop.org/standards/xesam/1.0/core#video.resolutionHeight" );
++const string AviThroughAnalyzerFactory::resolutionWidthFieldName( "http://freedesktop.org/standards/xesam/1.0/core#video.resolutionWidth" );
++const string AviThroughAnalyzerFactory::frameRateFieldName( "http://freedesktop.org/standards/xesam/1.0/core#video.framerate" );
++const string AviThroughAnalyzerFactory::videoCodecFieldName( "http://freedesktop.org/standards/xesam/1.0/core#video.videocodec" );
++const string AviThroughAnalyzerFactory::audioCodecFieldName( "http://freedesktop.org/standards/xesam/1.0/core#video.audiocodec" );
++
++void
++AviThroughAnalyzerFactory::registerFields(FieldRegister& reg) {
++ lengthField = reg.registerField(lengthFieldName);
++ resolutionHeightField = reg.registerField(resolutionHeightFieldName);
++ resolutionWidthField = reg.registerField(resolutionWidthFieldName);
++ frameRateField = reg.registerField(frameRateFieldName);
++ videoCodecField = reg.registerField(videoCodecFieldName);
++ audioCodecField = reg.registerField(audioCodecFieldName);
++}
++
++// Analyzer
++void
++AviThroughAnalyzer::setIndexable(AnalysisResult* i) {
++ analysisResult = i;
++}
++
++bool
++AviThroughAnalyzer::read_avi(InputStream* in)
++{
++ static const char sig_riff[] = "RIFF";
++ static const char sig_avi[] = "AVI ";
++ static const char sig_list[] = "LIST";
++ static const char sig_junk[] = "JUNK";
++ uint32_t charbuf1;
++ uint32_t dwbuf1;
++ const char* c;
++
++ done_avih = false;
++ done_audio = false;
++
++ // read AVI header
++ // this must be RIFF
++ if (4 != in->read ( c, 4, 4))
++ return false;
++ charbuf1 = readLittleEndianUInt32(c);
++ if (memcmp(&charbuf1, sig_riff, 4) != 0)
++ return false;
++
++ if (4 != in->read ( c, 4, 4))
++ return false;
++ dwbuf1 = readLittleEndianUInt32(c);
++
++ // this must be AVI
++ if (4 != in->read ( c, 4, 4))
++ return false;
++ charbuf1 = readLittleEndianUInt32(c);
++ if (memcmp(&charbuf1, sig_avi, 4) != 0)
++ return false;
++
++ // start reading AVI file
++ int counter = 0;
++ bool done = false;
++ do {
++ //printf ("read_avi, in position: %i\n", in->position());
++ // read header
++ if (4 != in->read ( c, 4, 4))
++ return false;
++ charbuf1 = readLittleEndianUInt32(c);
++
++ //printf ("about to handle chunk with ID: %i\n", charbuf1);
++
++ if (memcmp(&charbuf1, sig_list, 4) == 0) {
++ // if list
++ if (!read_list(in))
++ return false;
++ }
++ else if (memcmp(&charbuf1, sig_junk, 4) == 0) {
++ // if junk
++
++ // read chunk size
++ if (4 != in->read ( c, 4, 4))
++ return false;
++ dwbuf1 = readLittleEndianUInt32(c);
++
++ //printf ("Skipping junk chunk length: %i\n", dwbuf1);
++
++ // skip junk
++
++ //TODO: why reset doesn't work?! we've to use skip
++ //in->reset( in->position() + dwbuf1);
++ in->skip (dwbuf1);
++ //printf ("After skip: %i\n", in->position());
++ }
++ else {
++ // something we don't understand yet
++ //printf ("Unknown chunk header found: %i", charbuf1);
++ return false;
++ }
++
++ uint64_t curr = in->position();
++ bool atEnd = false;
++ if (1 != in->read( c, 1, 1))
++ atEnd = true;
++ in->reset ( curr);
++
++ if (((done_avih) && (strlen(handler_vids) > 0)
++ && (done_audio)) || atEnd) {
++ //printf ("We're done!\n");
++ done = true;
++ }
++
++ // make sure we don't stay here forever
++ ++counter;
++ if (counter > 10)
++ done = true;
++
++ } while (!done);
++
++ return true;
++}
++
++bool
++AviThroughAnalyzer::read_list(InputStream* in)
++{
++ //printf ("read_list, in position: %i\n", in->position());
++ const char sig_hdrl[] = "hdrl"; // header list
++ const char sig_strl[] = "strl"; // ...list
++ const char sig_movi[] = "movi"; // movie list
++
++ const char* c;
++ uint32_t dwbuf1;
++ uint32_t charbuf1;
++
++ //printf ("In read_list()\n");
++
++ // read size & list type
++ if (8 != in->read ( c, 8, 8))
++ return false;
++ dwbuf1 = readLittleEndianUInt32(c);
++ charbuf1 = readLittleEndianUInt32(c+4);
++
++ // read the relevant bits of the list
++ if (memcmp(&charbuf1, sig_hdrl, 4) == 0) {
++ // should be the main AVI header
++ if (!read_avih(in))
++ return false;
++ } else if (memcmp(&charbuf1, sig_strl, 4) == 0) {
++ // should be some stream info
++ if (!read_strl(in))
++ return false;
++ } else if (memcmp(&charbuf1, sig_movi, 4) == 0) {
++ // movie list
++ //printf ("Skipping movi chunk length: %i\n", dwbuf1);
++
++ // skip past it
++ in->reset (in->position() + dwbuf1);
++ } else {
++ // unknown list type
++ //printf ("Unknown list type found: %i\n", charbuf1);
++ }
++
++ return true;
++}
++
++bool
++AviThroughAnalyzer::read_avih(InputStream* in)
++{
++ //printf ("read_avih, in position: %i\n", in->position());
++ static const char sig_avih[] = "avih"; // header list
++
++ uint32_t dwbuf1;
++ uint32_t charbuf1;
++ const char* c;
++
++ // read header and length
++ if (8 != in->read ( c, 8, 8))
++ return false;
++ charbuf1 = readLittleEndianUInt32(c);
++ dwbuf1 = readLittleEndianUInt32(c+4);
++
++ // not a valid avih?
++ if (memcmp(&charbuf1, sig_avih, 4) != 0) {
++ //printf ("Chunk ID error, expected avih, got: %i\n", charbuf1);
++ return false;
++ }
++
++ // read all the avih fields
++ if (56 != in->read ( c, 56, 56))
++ return false;
++
++ avih_microsecperframe = readLittleEndianUInt32(c);
++ avih_maxbytespersec = readLittleEndianUInt32(c+4);
++ avih_reserved1 = readLittleEndianUInt32(c+8);
++ avih_flags = readLittleEndianUInt32(c+12);
++ avih_totalframes = readLittleEndianUInt32(c+16);
++ avih_initialframes = readLittleEndianUInt32(c+20);
++ avih_streams = readLittleEndianUInt32(c+24);
++ avih_buffersize = readLittleEndianUInt32(c+28);
++ avih_width = readLittleEndianUInt32(c+32);
++ avih_height = readLittleEndianUInt32(c+36);
++ avih_scale = readLittleEndianUInt32(c+40);
++ avih_rate = readLittleEndianUInt32(c+44);
++ avih_start = readLittleEndianUInt32(c+48);
++ avih_length = readLittleEndianUInt32(c+52);
++
++ done_avih = true;
++
++ return true;
++}
++
++bool
++AviThroughAnalyzer::read_strl(InputStream* in)
++{
++ static const char sig_strh[] = "strh";
++ static const char sig_strf[] = "strf";
++ //static const char sig_strd[] = "strd";
++ static const char sig_strn[] = "strn";
++ static const char sig_list[] = "LIST";
++ static const char sig_junk[] = "JUNK";
++
++ //printf ("in strl handler\n");
++
++ uint32_t dwbuf1; // buffer for block sizes
++ uint32_t charbuf1;
++ const char* c;
++
++ // loop through blocks
++ int counter = 0;
++ while (true) {
++ //printf ("read_strl, while start in position: %i\n", in->position());
++ // read type and size
++ if (8 != in->read ( c, 8, 8))
++ return false;
++ charbuf1 = readLittleEndianUInt32(c); // type
++ dwbuf1 = readLittleEndianUInt32(c+4); // size
++ //printf ("read_strl, dwbuf1 while initial value = %i\n",dwbuf1);
++
++ // detect type
++ if (memcmp(&charbuf1, sig_strh, 4) == 0) {
++ // got strh - stream header
++ //printf ("Found strh, calling read_strh()\n");
++ read_strh(in, dwbuf1);
++ }
++ else if (memcmp(&charbuf1, sig_strf, 4) == 0) {
++ // got strf - stream format
++ //printf ("Found strf, calling read_strf()\n");
++ read_strf(in, dwbuf1);
++ }
++ else if (memcmp(&charbuf1, sig_strn, 4) == 0) {
++ // we ignore strn, but it can be recorded incorrectly so we have
++ // to cope especially
++
++ // skip it
++ //printf ("Skipping strn chunk length: %i\n", dwbuf1);
++ in->reset (in->position() + dwbuf1);
++
++ /*
++ this is a pretty annoying hack; many AVIs incorrectly report the
++ length of the strn field by 1 byte. Its possible that strn's
++ should be word aligned, but no mention in the specs...
++
++ I'll clean/optimise this a touch soon
++ */
++
++ bool done = false;
++ unsigned char counter = 0;
++ while (!done) {
++ // read next marker
++ if (4 != in->read ( c, 4, 4))
++ return false;
++ charbuf1 = readLittleEndianUInt32(c); // type
++
++ // does it look ok?
++ if ((memcmp(&charbuf1, sig_list, 4) == 0) ||
++ (memcmp(&charbuf1, sig_junk, 4) == 0)) {
++ // yes, go back before it
++ in->reset(in->position() - 4);
++ done = true;
++ } else {
++ // no, skip one space forward from where we were
++ in->reset (in->position() - 3);
++ //printf ("Working around incorrectly marked strn length...\n");
++ }
++
++ // make sure we don't stay here too long
++ ++counter;
++ if (counter>10)
++ done = true;
++ }
++ }
++ else if ((memcmp(&charbuf1, sig_list, 4) == 0) ||
++ (memcmp(&charbuf1, sig_junk, 4) == 0)) {
++ // we have come to the end of our stay here in strl, time to leave
++ //printf ("Found LIST/JUNK, returning...\n");
++ //printf ("Found LIST/JUNK, returning from pos %i ...\n", in->position());
++ // rollback before the id and size
++ in->reset(in->position() - 8);
++
++ // return back to the main avi parser
++ return true;
++ }
++ else {
++ // we have some other unrecognised block type
++ //printf ("Skipping unrecognised block using dwbuf1 = %i\n", dwbuf1);
++ // just skip over it
++ in->reset (in->position() + dwbuf1);
++ } /* switch block type */
++
++ ++counter;
++ if (counter > 10)
++ return true;
++
++ } /* while (true) */
++
++ // we should never get here
++}
++
++bool
++AviThroughAnalyzer::read_strh(InputStream* in, uint32_t blocksize)
++{
++ //printf ("read_strh, in position: %i\n", in->position());
++ static const char sig_vids[] = "vids"; // ...video
++ static const char sig_auds[] = "auds"; // ...audio
++
++ uint32_t strh_flags;
++ uint32_t strh_reserved1;
++ uint32_t strh_initialframes;
++ uint32_t strh_scale;
++ uint32_t strh_rate;
++ uint32_t strh_start;
++ uint32_t strh_length;
++ uint32_t strh_buffersize;
++ uint32_t strh_quality;
++ uint32_t strh_samplesize;
++
++ uint32_t charbuf1;
++ uint32_t charbuf2;
++ const char* c;
++
++ // get stream info type, and handler id
++ if (8 != in->read ( c, 8, 8))
++ return false;
++ charbuf1 = readLittleEndianUInt32(c);
++ charbuf2 = readLittleEndianUInt32(c+4);
++
++ // read the strh fields
++ if (40 != in->read ( c, 40, 40))
++ return false;
++
++ strh_flags = readLittleEndianUInt32(c);
++ strh_reserved1 = readLittleEndianUInt32(c+4);
++ strh_initialframes = readLittleEndianUInt32(c+8);
++ strh_scale = readLittleEndianUInt32(c+12);
++ strh_rate = readLittleEndianUInt32(c+16);
++ strh_start = readLittleEndianUInt32(c+20);
++ strh_length = readLittleEndianUInt32(c+24);
++ strh_buffersize = readLittleEndianUInt32(c+28);
++ strh_quality = readLittleEndianUInt32(c+32);
++ strh_samplesize = readLittleEndianUInt32(c+36);
++
++ if (memcmp(&charbuf1, sig_vids, 4) == 0) { // we are video!
++ // save the handler
++ memcpy(handler_vids, &charbuf2, 4);
++ //printf ("Video handler: %s\n", handler_vids);
++ }
++ else if (memcmp(&charbuf1, sig_auds, 4) == 0) { // we are audio!
++ // save the handler
++ memcpy(handler_auds, &charbuf2, 4);
++ //printf ("Audio handler: %X\n", handler_auds);
++
++ // we want strf to get the audio codec
++ wantstrf = true;
++ } else {
++ // we are something that we don't understand
++ }
++
++ // do we need to skip ahead any more? (usually yes , contrary to
++ // the AVI specs I've read...)
++ // note: 48 is 10 * uint32_t + 2*FOURCC; the 10 fields we read above, plus the two character fields
++ //printf ("read_strh, before end position: %i\n", in->position());
++ if (blocksize > 48) {
++ in->reset (in->position() + (blocksize - 48));
++ //printf ("read_strh, new position after reset: %i\n", in->position());
++ }
++
++ return true;
++}
++
++bool
++AviThroughAnalyzer::read_strf(InputStream* in, uint32_t blocksize)
++{
++ const char* c;
++ //printf ("read_strf, in position: %i\n", in->position());
++
++ // do we want to do the strf?
++ if (wantstrf) {
++ // yes. we want the audio codec identifier out of it
++
++ // get the 16bit audio codec ID
++ if (2 != in->read ( c, 2, 2))
++ return false;
++ handler_audio = readLittleEndianUInt16(c);
++
++ //printf ("Read audio codec ID: %X\n", handler_audio);
++ // skip past the rest of the stuff here for now
++ in->reset( in->position() + blocksize - 2);
++ // we have audio
++ done_audio = true;
++
++ } else {
++ //printf ("read_strf: skipping %i\n", blocksize);
++ // no, skip the strf
++ in->reset (in->position() + blocksize);
++ }
++
++ return true;
++}
++
++const char*
++AviThroughAnalyzer::resolve_audio(uint16_t id)
++{
++ /*
++ this really wants to use some sort of KDE global
++ list. To avoid bloat for the moment it only does
++ a few common codecs
++ */
++
++ static const char codec_unknown[] = "Unknown";
++ static const char codec_01[] = "Microsoft PCM";
++ static const char codec_02[] = "Microsoft ADPCM";
++ static const char codec_50[] = "MPEG";
++ static const char codec_55[] = "MP3";
++ static const char codec_92[] = "AC3";
++ static const char codec_160[] = "WMA1";
++ static const char codec_161[] = "WMA2";
++ static const char codec_162[] = "WMA3";
++ static const char codec_2000[] = "DVM";
++ switch (id) {
++ case 0x000 : return codec_unknown; break;
++ case 0x001 : return codec_01; break;
++ case 0x002 : return codec_02; break;
++ case 0x050 : return codec_50; break;
++ case 0x055 : return codec_55; break;
++ case 0x092 : return codec_92; break;
++ case 0x160 : return codec_160; break;
++ case 0x161 : return codec_161; break;
++ case 0x162 : return codec_162; break;
++ case 0x2000 : return codec_2000; break;
++ default : return codec_unknown;
++ }
++
++ return NULL;
++}
++
++InputStream*
++AviThroughAnalyzer::connectInputStream(InputStream* in) {
++ if( !in )
++ return in;
++
++ /***************************************************/
++ // prep
++
++ memset(handler_vids, 0x00, 5);
++ memset(handler_auds, 0x00, 5);
++
++ /***************************************************/
++ // start reading stuff from it
++ wantstrf = false;
++
++ if (!read_avi(in)) {
++ //printf ("read_avi() failed!\n");
++ }
++
++ /***************************************************/
++ // set up our output
++
++ if (done_avih) {
++
++ if (0 != avih_microsecperframe)
++ analysisResult->addValue( factory->frameRateField,
++ int (1000000 / avih_microsecperframe ));
++
++ analysisResult->addValue( factory->resolutionHeightField, avih_width);
++ analysisResult->addValue( factory->resolutionHeightField, avih_height);
++
++ // work out and add length
++ uint64_t mylength = (uint64_t) ((float) avih_totalframes * (float) avih_microsecperframe / 1000000.0);
++ analysisResult->addValue( factory->lengthField, int(mylength));
++
++ if (strlen(handler_vids) > 0)
++ analysisResult->addValue( factory->videoCodecField, handler_vids);
++ else
++ analysisResult->addValue( factory->videoCodecField, "Unknown");
++
++ if (done_audio)
++ analysisResult->addValue( factory->audioCodecField,
++ resolve_audio(handler_audio));
++ else
++ analysisResult->addValue( factory->audioCodecField, "None");
++ }
++
++ in->reset(0); // rewind to the start of the stream
++ return in;
++}
++
++bool
++AviThroughAnalyzer::isReadyWithStream() {
++ return true;
++}
++
++//Factory
++class Factory : public AnalyzerFactoryFactory {
++public:
++ list<StreamThroughAnalyzerFactory*>
++ streamThroughAnalyzerFactories() const {
++ list<StreamThroughAnalyzerFactory*> af;
++ af.push_back(new AviThroughAnalyzerFactory());
++ return af;
++ }
++};
++
++STRIGI_ANALYZER_FACTORY(Factory)
+--- /dev/null
++++ b/src/streamanalyzer/throughplugins/avithroughanalyzer.h
+@@ -0,0 +1,105 @@
++/* This file is part of Strigi Desktop Search
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++#ifndef STRIGI_AVITHROUGHANALYZER
++#define STRIGI_AVITHROUGHANALYZER
++
++#include "streamthroughanalyzer.h"
++#include "analyzerplugin.h"
++
++#include <string>
++
++namespace Strigi {
++ class RegisteredField;
++}
++class AviThroughAnalyzerFactory;
++
++class STRIGI_PLUGIN_API AviThroughAnalyzer
++ : public Strigi::StreamThroughAnalyzer {
++private:
++ Strigi::AnalysisResult* analysisResult;
++ const AviThroughAnalyzerFactory* factory;
++
++ bool done_avih;
++ uint32_t avih_microsecperframe;
++ uint32_t avih_maxbytespersec;
++ uint32_t avih_reserved1;
++ uint32_t avih_flags;
++ uint32_t avih_totalframes;
++ uint32_t avih_initialframes;
++ uint32_t avih_streams;
++ uint32_t avih_buffersize;
++ uint32_t avih_width;
++ uint32_t avih_height;
++ uint32_t avih_scale;
++ uint32_t avih_rate;
++ uint32_t avih_start;
++ uint32_t avih_length;
++
++ char handler_vids[5]; // leave room for trailing \0
++ char handler_auds[5];
++ uint16_t handler_audio; // the ID of the audio codec
++ bool done_audio;
++
++ bool wantstrf;
++
++ bool read_avi(Strigi::InputStream* in);
++ bool read_strf(Strigi::InputStream* in, uint32_t blocksize);
++ bool read_strh(Strigi::InputStream* in, uint32_t blocksize);
++ bool read_strl(Strigi::InputStream* in);
++ bool read_avih(Strigi::InputStream* in);
++ bool read_list(Strigi::InputStream* in);
++ const char* resolve_audio(uint16_t id);
++
++public:
++ AviThroughAnalyzer(const AviThroughAnalyzerFactory* f) :factory(f) {}
++ ~AviThroughAnalyzer() {}
++ void setIndexable(Strigi::AnalysisResult* i);
++ Strigi::InputStream *connectInputStream(Strigi::InputStream *in);
++ bool isReadyWithStream();
++ const char* name() const { return "AviThroughAnalyzer"; }
++};
++
++class AviThroughAnalyzerFactory
++ : public Strigi::StreamThroughAnalyzerFactory {
++friend class AviThroughAnalyzer;
++private:
++ static const std::string lengthFieldName;
++ static const std::string resolutionHeightFieldName;
++ static const std::string resolutionWidthFieldName;
++ static const std::string frameRateFieldName;
++ static const std::string videoCodecFieldName;
++ static const std::string audioCodecFieldName;
++ const Strigi::RegisteredField* lengthField;
++ const Strigi::RegisteredField* resolutionHeightField;
++ const Strigi::RegisteredField* resolutionWidthField;
++ const Strigi::RegisteredField* frameRateField;
++ const Strigi::RegisteredField* videoCodecField;
++ const Strigi::RegisteredField* audioCodecField;
++
++ const char* name() const {
++ return "AviThroughAnalyzer";
++ }
++ Strigi::StreamThroughAnalyzer* newInstance() const {
++ return new AviThroughAnalyzer(this);
++ }
++ void registerFields(Strigi::FieldRegister&);
++};
++
++#endif
+--- a/src/streamanalyzer/throughplugins/CMakeLists.txt
++++ b/src/streamanalyzer/throughplugins/CMakeLists.txt
+@@ -30,6 +30,12 @@
+ ADD_STRIGITA(xbm xbmthroughanalyzer.cpp)
+ ADD_STRIGITA(au authroughanalyzer.cpp)
+ ADD_STRIGITA(gif gifthroughanalyzer.cpp)
++ADD_STRIGITA(ico icothroughanalyzer.cpp)
++ADD_STRIGITA(sid sidthroughanalyzer.cpp)
++ADD_STRIGITA(rgb rgbthroughanalyzer.cpp)
++ADD_STRIGITA(dds ddsthroughanalyzer.cpp)
++ADD_STRIGITA(wav wavthroughanalyzer.cpp)
++ADD_STRIGITA(avi avithroughanalyzer.cpp)
+
+ # disabled until a good check for making sure the file is a tga
+ #ADD_STRIGITA(tga tgathroughanalyzer.cpp)
+--- /dev/null
++++ b/src/streamanalyzer/throughplugins/ddsthroughanalyzer.cpp
+@@ -0,0 +1,400 @@
++/* This file is part of Strigi Desktop Search, ported from code of:
++ * - Ignacio Castaño <castano at ludicon.com> (Copyright (C) 2002)
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++
++#include "ddsthroughanalyzer.h"
++#include "textutils.h"
++#include <strigi/strigiconfig.h>
++#include "analysisresult.h"
++#include "fieldtypes.h"
++#include <cstring>
++
++using namespace std;
++using namespace Strigi;
++
++
++namespace {// Private.
++#if !defined(MAKEFOURCC)
++# define MAKEFOURCC(ch0, ch1, ch2, ch3) \
++ (uint32_t(uint8_t(ch0)) | (uint32_t(uint8_t(ch1)) << 8) | \
++ (uint32_t(uint8_t(ch2)) << 16) | (uint32_t(uint8_t(ch3)) << 24 ))
++#endif
++
++ static const uint32_t FOURCC_DDS = MAKEFOURCC('D', 'D', 'S', ' ');
++ static const uint32_t FOURCC_DXT1 = MAKEFOURCC('D', 'X', 'T', '1');
++ static const uint32_t FOURCC_DXT2 = MAKEFOURCC('D', 'X', 'T', '2');
++ static const uint32_t FOURCC_DXT3 = MAKEFOURCC('D', 'X', 'T', '3');
++ static const uint32_t FOURCC_DXT4 = MAKEFOURCC('D', 'X', 'T', '4');
++ static const uint32_t FOURCC_DXT5 = MAKEFOURCC('D', 'X', 'T', '5');
++ static const uint32_t FOURCC_RXGB = MAKEFOURCC('R', 'X', 'G', 'B');
++
++ static const uint32_t DDSD_CAPS = 0x00000001l;
++ static const uint32_t DDSD_PIXELFORMAT = 0x00001000l;
++ static const uint32_t DDSD_WIDTH = 0x00000004l;
++ static const uint32_t DDSD_HEIGHT = 0x00000002l;
++ static const uint32_t DDSD_PITCH = 0x00000008l;
++
++ static const uint32_t DDSCAPS_TEXTURE = 0x00001000l;
++ static const uint32_t DDSCAPS2_VOLUME = 0x00200000l;
++ static const uint32_t DDSCAPS2_CUBEMAP = 0x00000200l;
++
++ static const uint32_t DDPF_RGB = 0x00000040l;
++ static const uint32_t DDPF_FOURCC = 0x00000004l;
++ static const uint32_t DDPF_ALPHAPIXELS = 0x00000001l;
++
++ enum DDSType {
++ DDS_A8R8G8B8 = 0,
++ DDS_A1R5G5B5 = 1,
++ DDS_A4R4G4B4 = 2,
++ DDS_R8G8B8 = 3,
++ DDS_R5G6B5 = 4,
++ DDS_DXT1 = 5,
++ DDS_DXT2 = 6,
++ DDS_DXT3 = 7,
++ DDS_DXT4 = 8,
++ DDS_DXT5 = 9,
++ DDS_RXGB = 10,
++ DDS_UNKNOWN
++ };
++
++
++ struct DDSPixelFormat {
++ uint32_t size;
++ uint32_t flags;
++ uint32_t fourcc;
++ uint32_t bitcount;
++ uint32_t rmask;
++ uint32_t gmask;
++ uint32_t bmask;
++ uint32_t amask;
++ };
++
++ struct DDSCaps {
++ uint32_t caps1;
++ uint32_t caps2;
++ uint32_t caps3;
++ uint32_t caps4;
++ };
++
++ struct DDSHeader {
++ uint32_t size;
++ uint32_t flags;
++ uint32_t height;
++ uint32_t width;
++ uint32_t pitch;
++ uint32_t depth;
++ uint32_t mipmapcount;
++ uint32_t reserved[11];
++ DDSPixelFormat pf;
++ DDSCaps caps;
++ uint32_t notused;
++ };
++
++ static bool IsValid( const DDSHeader & header )
++ {
++ if( header.size != 124 ) {
++ return false;
++ }
++ const uint32_t required = (DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT);
++ if( (header.flags & required) != required ) {
++ return false;
++ }
++ if( header.pf.size != 32 ) {
++ return false;
++ }
++ if( !(header.caps.caps1 & DDSCAPS_TEXTURE) ) {
++ return false;
++ }
++ return true;
++ }
++
++ bool
++ readPixelFormat ( InputStream* in, DDSPixelFormat & pf )
++ {
++ const char* c;
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ pf.size = readLittleEndianUInt32(c);
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ pf.flags = readLittleEndianUInt32(c);
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ pf.fourcc = readLittleEndianUInt32(c);
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ pf.bitcount = readLittleEndianUInt32(c);
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ pf.rmask = readLittleEndianUInt32(c);
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ pf.gmask = readLittleEndianUInt32(c);
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ pf.bmask = readLittleEndianUInt32(c);
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ pf.amask = readLittleEndianUInt32(c);
++
++ return true;
++ }
++
++ bool
++ readCaps ( InputStream* in, DDSCaps & caps )
++ {
++ const char* c;
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ caps.caps1 = readLittleEndianUInt32(c);
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ caps.caps2 = readLittleEndianUInt32(c);
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ caps.caps3 = readLittleEndianUInt32(c);
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ caps.caps4 = readLittleEndianUInt32(c);
++
++ return true;
++ }
++
++ bool
++ readHeader(InputStream* in, DDSHeader& header)
++ {
++ const char *c;
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ header.size = readLittleEndianUInt32(c);
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ header.flags = readLittleEndianUInt32(c);
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ header.height = readLittleEndianUInt32(c);
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ header.width = readLittleEndianUInt32(c);
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ header.pitch = readLittleEndianUInt32(c);
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ header.depth = readLittleEndianUInt32(c);
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ header.mipmapcount = readLittleEndianUInt32(c);
++
++ for( int i = 0; i < 11; i++ ) {
++ if (4 != in->read(c, 4, 4))
++ return false;
++ header.reserved[i] = readLittleEndianUInt32(c);
++ }
++
++ if (!readPixelFormat( in, header.pf))
++ return false;
++
++
++ if (!readCaps ( in, header.caps))
++ return false;
++
++ if (4 != in->read(c, 4, 4))
++ return false;
++ header.notused = readLittleEndianUInt32(c);
++
++ return true;
++ }
++} // namespace
++
++
++// AnalyzerFactory
++
++const string DdsThroughAnalyzerFactory::widthFieldName( "image.width" );
++const string DdsThroughAnalyzerFactory::heightFieldName( "image.height" );
++
++//TODO: check values!
++const string DdsThroughAnalyzerFactory::depthFieldName( "image.color_depth" );
++const string DdsThroughAnalyzerFactory::bitDepthFieldName( "document.stats.image_bit_depth" );
++const string DdsThroughAnalyzerFactory::mipmapCountFieldName ( "document.stats.image_mipmap_count" );
++const string DdsThroughAnalyzerFactory::typeFieldName ( "document.stats.image_type" );
++const string DdsThroughAnalyzerFactory::colorModeFieldName ( "document.stats.image_color_mode" );
++const string DdsThroughAnalyzerFactory::compressionFieldName ( "document.stats.image_compression" );
++
++void
++DdsThroughAnalyzerFactory::registerFields(FieldRegister& reg) {
++ widthField = reg.registerField(widthFieldName);
++ heightField = reg.registerField(heightFieldName);
++ depthField = reg.registerField(depthFieldName);
++ bitDepthField = reg.registerField(bitDepthFieldName);
++ mipmapCountField = reg.registerField(mipmapCountFieldName);
++ typeField = reg.registerField(typeFieldName);
++ colorModeField = reg.registerField(colorModeFieldName);
++ compressionField = reg.registerField(compressionFieldName);
++}
++
++// Analyzer
++void
++DdsThroughAnalyzer::setIndexable(AnalysisResult* i) {
++ analysisResult = i;
++}
++
++InputStream*
++DdsThroughAnalyzer::connectInputStream(InputStream* in) {
++ if( !in )
++ return in;
++
++ const char *c;
++
++ //Remember: dds files are little-endian
++ //read the beginning of the stream and make sure it looks ok
++
++ if (4 != in->read(c, 4, 4)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++
++ uint32_t fourcc = readLittleEndianUInt32(c);
++ if (fourcc != FOURCC_DDS) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++
++ // Read image header.
++ DDSHeader header;
++ if (!readHeader (in, header)) {
++ in->reset(0);
++ return in;
++ }
++
++ // Check image file format.
++ if(( in->read(c, 1, 1) != 1 ) || !IsValid( header ) ) {
++ in->reset(0);
++ return in;
++ }
++
++ // Set file info
++ analysisResult->addValue( factory->widthField, header.width );
++ analysisResult->addValue( factory->heightField, header.height );
++ analysisResult->addValue( factory->mipmapCountField, header.mipmapcount );
++
++ // Set file type.
++ if( header.caps.caps2 & DDSCAPS2_CUBEMAP ) {
++ analysisResult->addValue( factory->typeField, "Cube Map Texture");
++ }
++ else if( header.caps.caps2 & DDSCAPS2_VOLUME ) {
++ analysisResult->addValue( factory->typeField, "Volume Texture");
++ analysisResult->addValue( factory->depthField, header.depth);
++ }
++ else {
++ analysisResult->addValue( factory->typeField, "2D Texture");
++ }
++
++ // Set file color depth and compression.
++ if( header.pf.flags & DDPF_RGB ) {
++ analysisResult->addValue( factory->bitDepthField,header.pf.bitcount);
++ analysisResult->addValue( factory->compressionField, "Uncompressed");
++ if( header.pf.flags & DDPF_ALPHAPIXELS )
++ analysisResult->addValue( factory->colorModeField, "RGB/Alpha");
++ else
++ analysisResult->addValue( factory->colorModeField, "RGB");
++ }
++ else if( header.pf.flags & DDPF_FOURCC ) {
++ switch( header.pf.fourcc ) {
++ case FOURCC_DXT1:
++ analysisResult->addValue( factory->bitDepthField, 4);
++ analysisResult->addValue( factory->compressionField, "DXT1");
++ analysisResult->addValue( factory->colorModeField, "RGB");
++ break;
++ case FOURCC_DXT2:
++ analysisResult->addValue( factory->bitDepthField, 16);
++ analysisResult->addValue( factory->compressionField, "DXT2");
++ analysisResult->addValue( factory->colorModeField, "RGB/Alpha");
++ break;
++ case FOURCC_DXT3:
++ analysisResult->addValue( factory->bitDepthField, 16);
++ analysisResult->addValue( factory->compressionField, "DXT3");
++ analysisResult->addValue( factory->colorModeField, "RGB/Alpha");
++ break;
++ case FOURCC_DXT4:
++ analysisResult->addValue( factory->bitDepthField, 16);
++ analysisResult->addValue( factory->compressionField, "DXT4");
++ analysisResult->addValue( factory->colorModeField, "RGB/Alpha");
++ break;
++ case FOURCC_DXT5:
++ analysisResult->addValue( factory->bitDepthField, 16);
++ analysisResult->addValue( factory->compressionField, "DXT5");
++ analysisResult->addValue( factory->colorModeField, "RGB/Alpha");
++ break;
++ case FOURCC_RXGB:
++ analysisResult->addValue( factory->bitDepthField, 16);
++ analysisResult->addValue( factory->compressionField, "RXGB");
++ analysisResult->addValue( factory->colorModeField, "RGB");
++ break;
++ default:
++ analysisResult->addValue( factory->compressionField, "Unknown");
++ break;
++ }
++ }
++ else {
++ analysisResult->addValue( factory->compressionField, "Unknown");
++ }
++
++
++ in->reset(0); // rewind to the start of the stream
++ return in;
++}
++
++bool
++DdsThroughAnalyzer::isReadyWithStream() {
++ return true;
++}
++
++//Factory
++class Factory : public AnalyzerFactoryFactory {
++public:
++ list<StreamThroughAnalyzerFactory*>
++ streamThroughAnalyzerFactories() const {
++ list<StreamThroughAnalyzerFactory*> af;
++ af.push_back(new DdsThroughAnalyzerFactory());
++ return af;
++ }
++};
++
++STRIGI_ANALYZER_FACTORY(Factory)
+--- /dev/null
++++ b/src/streamanalyzer/throughplugins/ddsthroughanalyzer.h
+@@ -0,0 +1,78 @@
++/* This file is part of Strigi Desktop Search
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++#ifndef STRIGI_DDSTHROUGHANALYZER
++#define STRIGI_DDSTHROUGHANALYZER
++
++#include "streamthroughanalyzer.h"
++#include "analyzerplugin.h"
++
++#include <string>
++
++namespace Strigi {
++ class RegisteredField;
++}
++class DdsThroughAnalyzerFactory;
++
++class STRIGI_PLUGIN_API DdsThroughAnalyzer
++ : public Strigi::StreamThroughAnalyzer {
++private:
++ Strigi::AnalysisResult* analysisResult;
++ const DdsThroughAnalyzerFactory* factory;
++public:
++ DdsThroughAnalyzer(const DdsThroughAnalyzerFactory* f) :factory(f) {}
++ ~DdsThroughAnalyzer() {}
++ void setIndexable(Strigi::AnalysisResult* i);
++ Strigi::InputStream *connectInputStream(Strigi::InputStream *in);
++ bool isReadyWithStream();
++ const char* name() const { return "DdsThroughAnalyzer"; }
++};
++
++class DdsThroughAnalyzerFactory
++ : public Strigi::StreamThroughAnalyzerFactory {
++friend class DdsThroughAnalyzer;
++private:
++ static const std::string widthFieldName;
++ static const std::string heightFieldName;
++ static const std::string depthFieldName;
++ static const std::string bitDepthFieldName;
++ static const std::string mipmapCountFieldName;
++ static const std::string typeFieldName;
++ static const std::string colorModeFieldName;
++ static const std::string compressionFieldName;
++
++ const Strigi::RegisteredField* widthField;
++ const Strigi::RegisteredField* heightField;
++ const Strigi::RegisteredField* depthField;
++ const Strigi::RegisteredField* bitDepthField;
++ const Strigi::RegisteredField* mipmapCountField;
++ const Strigi::RegisteredField* typeField;
++ const Strigi::RegisteredField* colorModeField;
++ const Strigi::RegisteredField* compressionField;
++
++ const char* name() const {
++ return "DdsThroughAnalyzer";
++ }
++ Strigi::StreamThroughAnalyzer* newInstance() const {
++ return new DdsThroughAnalyzer(this);
++ }
++ void registerFields(Strigi::FieldRegister&);
++};
++
++#endif
+--- a/src/streamanalyzer/throughplugins/gifthroughanalyzer.cpp
++++ b/src/streamanalyzer/throughplugins/gifthroughanalyzer.cpp
+@@ -59,6 +59,9 @@
+ const Strigi::RegisteredField* colorDepthField;
+ const Strigi::RegisteredField* widthField;
+ const Strigi::RegisteredField* heightField;
++
++ const Strigi::RegisteredField* typeField;
++
+ const char* name() const {
+ return "GifThroughAnalyzer";
+ }
+@@ -75,6 +78,8 @@
+ colorDepthField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#pixelDataBitDepth");
+ widthField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#width");
+ heightField = reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#height");
++
++ typeField = reg.typeField;
+ }
+
+ // Analyzer
+@@ -105,6 +110,8 @@
+ analysisResult->addValue(factory->colorDepthField, (uint32_t)colorDepth);
+ }
+
++ analysisResult->addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#Image");
++
+ return in;
+ }
+
+--- /dev/null
++++ b/src/streamanalyzer/throughplugins/icothroughanalyzer.cpp
+@@ -0,0 +1,174 @@
++/* This file is part of Strigi Desktop Search, ported from code of:
++ * - Shane Wright <me at shanewright.co.uk> (Copyright (C) 2002)
++ * - Matthias Lechner <matthias at lmme.de> (Copyright (C) 2007)
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++
++#include "icothroughanalyzer.h"
++#include "textutils.h"
++#include <strigi/strigiconfig.h>
++#include "analysisresult.h"
++#include "fieldtypes.h"
++#include <cstring>
++
++using namespace std;
++using namespace Strigi;
++
++// AnalyzerFactory
++
++const string IcoThroughAnalyzerFactory::widthFieldName( "image.width" );
++const string IcoThroughAnalyzerFactory::heightFieldName( "image.height" );
++
++//TODO: check values!
++const string IcoThroughAnalyzerFactory::numberFieldName( "document.stats.image_count" );
++const string IcoThroughAnalyzerFactory::bitsPerPixelFieldName( "image.color_depth" );
++const string IcoThroughAnalyzerFactory::colorCountFieldName( "image.color_count" );
++
++void
++IcoThroughAnalyzerFactory::registerFields(FieldRegister& reg) {
++ widthField = reg.registerField(widthFieldName);
++ heightField = reg.registerField(heightFieldName);
++ numberField = reg.registerField(numberFieldName);
++ bitsPerPixelField = reg.registerField(bitsPerPixelFieldName);
++ colorCountField = reg.registerField(colorCountFieldName);
++}
++
++// Analyzer
++void
++IcoThroughAnalyzer::setIndexable(AnalysisResult* i) {
++ analysisResult = i;
++}
++
++InputStream*
++IcoThroughAnalyzer::connectInputStream(InputStream* in) {
++ if( !in )
++ return in;
++
++ const char *c;
++ int32_t n;
++
++ // Remember: ICO files are little-endian
++ // read the beginning of the stream and make sure it looks ok
++
++ n = in->read(c, 6, 6);
++ if (n != 6) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ uint16_t ico_reserved = readLittleEndianUInt16(c);
++ uint16_t ico_type = readLittleEndianUInt16(c+2);
++ uint16_t ico_count = readLittleEndianUInt16(c+4);
++
++ if ((ico_reserved != 0) || (ico_type != 1) || (ico_count < 1)) {
++ in->reset(0);
++ return in;
++ }
++
++ // now loop through each of the icon entries
++ n = in->read(c, 1, 1);
++ if (n != 1) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ uint8_t icoe_width = (uint8_t)*c;
++
++ n = in->read(c, 1, 1);
++ if (n != 1) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ uint8_t icoe_height = (uint8_t)*c;
++
++ n = in->read(c, 1, 1);
++ if (n != 1) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ uint8_t icoe_colorcount = (uint8_t)*c;
++
++ n = in->read(c, 1, 1);
++ if (n != 1) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ uint8_t icoe_reserved = (uint8_t)*c;
++
++ n = in->read(c, 2, 2);
++ if (n != 2) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ uint16_t icoe_planes = readLittleEndianUInt16(c);
++
++ n = in->read(c, 2, 2);
++ if (n != 2) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ uint16_t icoe_bitcount = readLittleEndianUInt16(c);
++
++ n = in->read(c, 4, 4);
++ if (n != 4) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ uint32_t icoe_bytesinres = readLittleEndianUInt32(c);
++
++ n = in->read(c, 4, 4);
++ if (n != 4) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ uint32_t icoe_imageoffset = readLittleEndianUInt32(c);
++
++ // read the data on the 1st icon
++ analysisResult->addValue( factory->numberField, ico_count );
++
++ analysisResult->addValue( factory->widthField, icoe_width );
++ analysisResult->addValue( factory->heightField, icoe_height );
++
++ if (icoe_bitcount > 0)
++ analysisResult->addValue( factory->bitsPerPixelField, icoe_bitcount );
++
++ if (icoe_colorcount > 0)
++ analysisResult->addValue( factory->colorCountField, icoe_colorcount );
++ else if (icoe_bitcount > 0)
++ analysisResult->addValue( factory->colorCountField, 2 ^ icoe_bitcount );
++
++ in->reset(0); // rewind to the start of the stream
++ return in;
++}
++
++bool
++IcoThroughAnalyzer::isReadyWithStream() {
++ return true;
++}
++
++//Factory
++class Factory : public AnalyzerFactoryFactory {
++public:
++ list<StreamThroughAnalyzerFactory*>
++ streamThroughAnalyzerFactories() const {
++ list<StreamThroughAnalyzerFactory*> af;
++ af.push_back(new IcoThroughAnalyzerFactory());
++ return af;
++ }
++};
++
++STRIGI_ANALYZER_FACTORY(Factory)
+--- /dev/null
++++ b/src/streamanalyzer/throughplugins/icothroughanalyzer.h
+@@ -0,0 +1,71 @@
++/* This file is part of Strigi Desktop Search
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++#ifndef STRIGI_ICOTHROUGHANALYZER
++#define STRIGI_ICOTHROUGHANALYZER
++
++#include "streamthroughanalyzer.h"
++#include "analyzerplugin.h"
++
++#include <string>
++
++namespace Strigi {
++ class RegisteredField;
++}
++class IcoThroughAnalyzerFactory;
++
++class STRIGI_PLUGIN_API IcoThroughAnalyzer
++ : public Strigi::StreamThroughAnalyzer {
++private:
++ Strigi::AnalysisResult* analysisResult;
++ const IcoThroughAnalyzerFactory* factory;
++public:
++ IcoThroughAnalyzer(const IcoThroughAnalyzerFactory* f) :factory(f) {}
++ ~IcoThroughAnalyzer() {}
++ void setIndexable(Strigi::AnalysisResult* i);
++ Strigi::InputStream *connectInputStream(Strigi::InputStream *in);
++ bool isReadyWithStream();
++ const char* name() const { return "IcoThroughAnalyzer"; }
++};
++
++class IcoThroughAnalyzerFactory
++ : public Strigi::StreamThroughAnalyzerFactory {
++friend class IcoThroughAnalyzer;
++private:
++ static const std::string numberFieldName;
++ static const std::string widthFieldName;
++ static const std::string heightFieldName;
++ static const std::string bitsPerPixelFieldName;
++ static const std::string colorCountFieldName;
++ const Strigi::RegisteredField* numberField;
++ const Strigi::RegisteredField* widthField;
++ const Strigi::RegisteredField* heightField;
++ const Strigi::RegisteredField* bitsPerPixelField;
++ const Strigi::RegisteredField* colorCountField;
++
++ const char* name() const {
++ return "IcoThroughAnalyzer";
++ }
++ Strigi::StreamThroughAnalyzer* newInstance() const {
++ return new IcoThroughAnalyzer(this);
++ }
++ void registerFields(Strigi::FieldRegister&);
++};
++
++#endif
+--- a/src/streamanalyzer/throughplugins/pcxthroughanalyzer.cpp
++++ b/src/streamanalyzer/throughplugins/pcxthroughanalyzer.cpp
+@@ -38,13 +38,14 @@
+
+ void
+ PcxThroughAnalyzerFactory::registerFields(FieldRegister& reg) {
+- compressionField = reg.registerField(compressionFieldName,
+- FieldRegister::stringType, 1, 0);
++ compressionField = reg.registerField(compressionFieldName);
+ widthField = reg.registerField(widthFieldName);
+ heightField = reg.registerField(heightFieldName);
+ hResolutionField = reg.registerField(hResolutionFieldName);
+ vResolutionField = reg.registerField(vResolutionFieldName);
+ colorDepthField = reg.registerField(colorDepthFieldName);
++
++ typeField = reg.typeField;
+ }
+
+ void
+@@ -84,6 +85,7 @@
+ indexable->addValue(factory->hResolutionField, (int32_t)readLittleEndianUInt16(header+12));
+ indexable->addValue(factory->vResolutionField, (int32_t)readLittleEndianUInt16(header+14));
+
++ indexable->addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#Image");
+ return in;
+ }
+ bool
+--- a/src/streamanalyzer/throughplugins/pcxthroughanalyzer.h
++++ b/src/streamanalyzer/throughplugins/pcxthroughanalyzer.h
+@@ -55,6 +55,9 @@
+ const Strigi::RegisteredField* compressionField;
+ const Strigi::RegisteredField* hResolutionField;
+ const Strigi::RegisteredField* vResolutionField;
++
++ const Strigi::RegisteredField* typeField;
++
+ const char* name() const {
+ return "PcxThroughAnalyzer";
+ }
+--- /dev/null
++++ b/src/streamanalyzer/throughplugins/rgbthroughanalyzer.cpp
+@@ -0,0 +1,238 @@
++/* This file is part of Strigi Desktop Search, ported from code of:
++ * - Melchior FRANZ <mfranz at kde.org> (Copyright (C) 2004)
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++
++#include "rgbthroughanalyzer.h"
++#include "textutils.h"
++#include <strigi/strigiconfig.h>
++#include "analysisresult.h"
++#include "fieldtypes.h"
++#include <cstring>
++#include <map>
++#include "config.h"
++
++using namespace std;
++using namespace Strigi;
++
++
++// AnalyzerFactory
++
++const string RgbThroughAnalyzerFactory::widthFieldName( "image.width" );
++const string RgbThroughAnalyzerFactory::heightFieldName( "image.height" );
++
++//TODO: check values!
++const string RgbThroughAnalyzerFactory::bitDepthFieldName( "document.stats.image_bit_depth" );
++const string RgbThroughAnalyzerFactory::imageNameFieldName ( "document.stats.image_name" );
++const string RgbThroughAnalyzerFactory::sharedRowsFieldName ( "document.stats.image_shared_rows" );
++const string RgbThroughAnalyzerFactory::colorModeFieldName ( "document.stats.image_color_mode" );
++const string RgbThroughAnalyzerFactory::compressionFieldName ( "document.stats.image_compression" );
++
++void
++RgbThroughAnalyzerFactory::registerFields(FieldRegister& reg) {
++ widthField = reg.registerField(widthFieldName);
++ heightField = reg.registerField(heightFieldName);
++ bitDepthField = reg.registerField(bitDepthFieldName);
++ imageNameField = reg.registerField(imageNameFieldName);
++ sharedRowsField = reg.registerField(sharedRowsFieldName);
++ colorModeField = reg.registerField(colorModeFieldName);
++ compressionField = reg.registerField(compressionFieldName);
++}
++
++// Analyzer
++void
++RgbThroughAnalyzer::setIndexable(AnalysisResult* i) {
++ analysisResult = i;
++}
++
++InputStream*
++RgbThroughAnalyzer::connectInputStream(InputStream* in) {
++ if( !in )
++ return in;
++
++ const char *c;
++ uint16_t magic;
++ uint8_t storage;
++ uint8_t bpc;
++ uint16_t dimension;
++ uint16_t xsize;
++ uint16_t ysize;
++ uint16_t zsize;
++ uint32_t pixmin;
++ uint32_t pixmax;
++ uint32_t dummy;
++ uint32_t colormap;
++
++ //Remember: dds files are big-endian
++ //read the beginning of the stream and make sure it looks ok
++
++ if (2 != in->read(c, 2, 2)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ magic = readBigEndianUInt16(c);
++
++ if (magic != 474) {
++ in->reset(0);
++ return in;
++ }
++
++ if (1 != in->read(c, 1, 1)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ storage = (uint8_t)*c;
++
++ if (1 != in->read(c, 1, 1)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ bpc = (uint8_t)*c;
++
++ if (8 != in->read(c, 8, 8)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ dimension = readBigEndianUInt16(c);
++ xsize = readBigEndianUInt16(c+2);
++ ysize = readBigEndianUInt16(c+4);
++ zsize = readBigEndianUInt16(c+6);
++
++ if (12 != in->read(c, 12, 12)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ pixmin = readBigEndianUInt32(c);
++ pixmax = readBigEndianUInt32(c+4);
++ dummy = readBigEndianUInt32(c+8);
++
++ if (80 != in->read(c, 80, 80)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ string imagename (c, 80);
++ //TODO: discover why imagename is reported as a NON utf8 string
++ imagename[79] = '\0';
++
++ if (4 != in->read(c, 4, 4)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ colormap = readBigEndianUInt32(c);
++
++ if (404 != in->skip( 404)) {
++ in->reset (0);
++ return in;
++ }
++
++ if (dimension == 1)
++ ysize = 1;
++
++ //determine fileSize
++ uint64_t fileSize;
++ uint64_t pos;
++ pos = in->position();
++ fileSize = pos;
++ uint64_t skipped;
++ do{
++ skipped = in->skip( 8192);
++ fileSize += skipped;
++ } while (skipped != 0);
++
++ // restore file position
++ in->reset (pos);
++
++ analysisResult->addValue( factory->widthField, xsize );
++ analysisResult->addValue( factory->heightField, ysize );
++ analysisResult->addValue( factory->bitDepthField, zsize * 8 * bpc );
++ analysisResult->addValue( factory->imageNameField, imagename);
++
++ if (zsize == 1)
++ analysisResult->addValue( factory->colorModeField, "Grayscale");
++ else if (zsize == 2)
++ analysisResult->addValue( factory->colorModeField, "Grayscale/Alpha");
++ else if (zsize == 3)
++ analysisResult->addValue( factory->colorModeField, "RGB");
++ else if (zsize == 4)
++ analysisResult->addValue( factory->colorModeField, "RGB/Alpha");
++
++ if (!storage)
++ analysisResult->addValue( factory->compressionField, "Uncompressed");
++ else if (storage == 1) {
++ long compressed = fileSize - 512;
++ long verbatim = xsize * ysize * zsize;
++ char buff[50];
++ snprintf (buff, 50, "%.1f", compressed * 100.0 / verbatim);
++ analysisResult->addValue( factory->compressionField,
++ string ("Runlength Encoded, ") + buff);
++
++ long k;
++ uint32_t offs;
++ map<uint32_t, uint> pixelMap;
++ map<uint32_t, uint>::iterator it;
++ map<uint32_t, uint>::iterator end = pixelMap.end();
++ for (k = 0; k < (ysize * zsize); k++) {
++
++ if (4 != in->read(c, 4, 4)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ offs = readBigEndianUInt32 (c);
++
++ if ((it = pixelMap.find(offs)) != end)
++ it->second = it->second + 1;
++ else
++ pixelMap[offs] = 0;
++ }
++
++ for (k = 0, it = pixelMap.begin(); it != end; ++it){
++ k += it->second;
++ }
++ if (k) {
++ snprintf (buff, 50, "%.1f", k * 100.0 / (ysize * zsize));
++ analysisResult->addValue( factory->sharedRowsField, buff);
++ }
++ else
++ analysisResult->addValue( factory->sharedRowsField, "None");
++ } else
++ analysisResult->addValue( factory->compressionField, "Unknown");
++
++
++
++ in->reset(0); // rewind to the start of the stream
++ return in;
++}
++
++bool
++RgbThroughAnalyzer::isReadyWithStream() {
++ return true;
++}
++
++//Factory
++class Factory : public AnalyzerFactoryFactory {
++public:
++ list<StreamThroughAnalyzerFactory*>
++ streamThroughAnalyzerFactories() const {
++ list<StreamThroughAnalyzerFactory*> af;
++ af.push_back(new RgbThroughAnalyzerFactory());
++ return af;
++ }
++};
++
++STRIGI_ANALYZER_FACTORY(Factory)
+--- /dev/null
++++ b/src/streamanalyzer/throughplugins/rgbthroughanalyzer.h
+@@ -0,0 +1,76 @@
++/* This file is part of Strigi Desktop Search
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++#ifndef STRIGI_RGBTHROUGHANALYZER
++#define STRIGI_RGBTHROUGHANALYZER
++
++#include "streamthroughanalyzer.h"
++#include "analyzerplugin.h"
++
++#include <string>
++
++namespace Strigi {
++ class RegisteredField;
++}
++class RgbThroughAnalyzerFactory;
++
++class STRIGI_PLUGIN_API RgbThroughAnalyzer
++ : public Strigi::StreamThroughAnalyzer {
++private:
++ Strigi::AnalysisResult* analysisResult;
++ const RgbThroughAnalyzerFactory* factory;
++public:
++ RgbThroughAnalyzer(const RgbThroughAnalyzerFactory* f) :factory(f) {}
++ ~RgbThroughAnalyzer() {}
++ void setIndexable(Strigi::AnalysisResult* i);
++ Strigi::InputStream *connectInputStream(Strigi::InputStream *in);
++ bool isReadyWithStream();
++ const char* name() const { return "RgbThroughAnalyzer"; }
++};
++
++class RgbThroughAnalyzerFactory
++ : public Strigi::StreamThroughAnalyzerFactory {
++friend class RgbThroughAnalyzer;
++private:
++ static const std::string widthFieldName;
++ static const std::string heightFieldName;
++ static const std::string bitDepthFieldName;
++ static const std::string imageNameFieldName;
++ static const std::string sharedRowsFieldName;
++ static const std::string colorModeFieldName;
++ static const std::string compressionFieldName;
++
++ const Strigi::RegisteredField* widthField;
++ const Strigi::RegisteredField* heightField;
++ const Strigi::RegisteredField* bitDepthField;
++ const Strigi::RegisteredField* imageNameField;
++ const Strigi::RegisteredField* sharedRowsField;
++ const Strigi::RegisteredField* colorModeField;
++ const Strigi::RegisteredField* compressionField;
++
++ const char* name() const {
++ return "RgbThroughAnalyzer";
++ }
++ Strigi::StreamThroughAnalyzer* newInstance() const {
++ return new RgbThroughAnalyzer(this);
++ }
++ void registerFields(Strigi::FieldRegister&);
++};
++
++#endif
+--- /dev/null
++++ b/src/streamanalyzer/throughplugins/sidthroughanalyzer.cpp
+@@ -0,0 +1,166 @@
++/* This file is part of Strigi Desktop Search, ported from code of:
++ * - Rolf Magnus <ramagnus at kde.org> (2003)
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++
++#include "sidthroughanalyzer.h"
++#include "textutils.h"
++#include <strigi/strigiconfig.h>
++#include "analysisresult.h"
++#include "fieldtypes.h"
++#include <cstring>
++
++#include <iostream>
++
++using namespace std;
++using namespace Strigi;
++
++// AnalyzerFactory
++
++const string SidThroughAnalyzerFactory::titleFieldName("http://freedesktop.org/standards/xesam/1.0/core#title");
++const string SidThroughAnalyzerFactory::artistFieldName("http://freedesktop.org/standards/xesam/1.0/core#artist");
++
++//TODO: check values!
++const string SidThroughAnalyzerFactory::trackNumberFieldName("http://freedesktop.org/standards/xesam/1.0/core#albumTrackCount");
++const string SidThroughAnalyzerFactory::versionFieldName( "document.stats.version" );
++const string SidThroughAnalyzerFactory::copyrightFieldName( "document.stats.copyright" );
++
++void
++SidThroughAnalyzerFactory::registerFields(FieldRegister& reg) {
++ titleField = reg.registerField(titleFieldName);
++ artistField = reg.registerField(artistFieldName);
++ trackNumberField = reg.registerField(trackNumberFieldName);
++ versionField = reg.registerField(versionFieldName);
++ copyrightField = reg.registerField(copyrightFieldName);
++}
++
++// Analyzer
++void
++SidThroughAnalyzer::setIndexable(AnalysisResult* i) {
++ analysisResult = i;
++}
++
++InputStream*
++SidThroughAnalyzer::connectInputStream(InputStream* in) {
++ if( !in )
++ return in;
++
++ const char *c;
++ int version;
++ int num_songs;
++ int start_song;
++ string title;
++ string artist;
++ string copyright;
++
++ // read the beginning of the stream and make sure it looks ok
++ if (4 != in->read(c, 4, 4)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ uint32_t header1 = readLittleEndianUInt32(c);
++
++ if (strncmp((char*) &header1, "PSID", 4)) {
++ in->reset(0);
++ cout << "reset\n";
++ return in;
++ }
++
++ //read version
++ if (2 != in->read(c, 2, 2)) {
++ in->reset(0);
++ return in;
++ }
++ version = readBigEndianUInt16(c);
++
++ //jump to 0xE
++ if (8 != in->skip(8)) {
++ in->reset(0);
++ return in;
++ }
++
++ //read number of songs
++ if (2 != in->read(c, 2, 2)) {
++ in->reset(0);
++ return in;
++ }
++ num_songs = readBigEndianUInt16(c);
++
++ //start song
++ if (2 != in->read(c, 2, 2)) {
++ in->reset(0);
++ return in;
++ }
++ start_song = readBigEndianUInt16(c);
++
++ //jump to 0x16
++ if (4 != in->skip(4)) {
++ in->reset(0);
++ return in;
++ }
++
++ //title
++ if (32 != in->read(c, 32, 32)) {
++ in->reset(0);
++ return in;
++ }
++ title = c;
++
++ //artist
++ if (32 != in->read(c, 32, 32)) {
++ in->reset(0);
++ return in;
++ }
++ artist = c;
++
++ //copyright
++ if (32 != in->read(c, 32, 32)) {
++ in->reset(0);
++ return in;
++ }
++ copyright = c;
++
++ // read the data on the 1st icon
++ analysisResult->addValue( factory->artistField, artist );
++ analysisResult->addValue( factory->titleField, title );
++ analysisResult->addValue( factory->copyrightField, copyright );
++ analysisResult->addValue( factory->trackNumberField, num_songs );
++ analysisResult->addValue( factory->versionField, version );
++
++ in->reset(0); // rewind to the start of the stream
++ return in;
++}
++
++bool
++SidThroughAnalyzer::isReadyWithStream() {
++ return true;
++}
++
++//Factory
++class Factory : public AnalyzerFactoryFactory {
++public:
++ list<StreamThroughAnalyzerFactory*>
++ streamThroughAnalyzerFactories() const {
++ list<StreamThroughAnalyzerFactory*> af;
++ af.push_back(new SidThroughAnalyzerFactory());
++ return af;
++ }
++};
++
++STRIGI_ANALYZER_FACTORY(Factory)
+--- /dev/null
++++ b/src/streamanalyzer/throughplugins/sidthroughanalyzer.h
+@@ -0,0 +1,71 @@
++/* This file is part of Strigi Desktop Search
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++#ifndef STRIGI_SIDTHROUGHANALYZER
++#define STRIGI_SIDTHROUGHANALYZER
++
++#include "streamthroughanalyzer.h"
++#include "analyzerplugin.h"
++
++#include <string>
++
++namespace Strigi {
++ class RegisteredField;
++}
++class SidThroughAnalyzerFactory;
++
++class STRIGI_PLUGIN_API SidThroughAnalyzer
++ : public Strigi::StreamThroughAnalyzer {
++private:
++ Strigi::AnalysisResult* analysisResult;
++ const SidThroughAnalyzerFactory* factory;
++public:
++ SidThroughAnalyzer(const SidThroughAnalyzerFactory* f) :factory(f) {}
++ ~SidThroughAnalyzer() {}
++ void setIndexable(Strigi::AnalysisResult* i);
++ Strigi::InputStream *connectInputStream(Strigi::InputStream *in);
++ bool isReadyWithStream();
++ const char* name() const { return "SidThroughAnalyzer"; }
++};
++
++class SidThroughAnalyzerFactory
++ : public Strigi::StreamThroughAnalyzerFactory {
++friend class SidThroughAnalyzer;
++private:
++ static const std::string titleFieldName;
++ static const std::string artistFieldName;
++ static const std::string copyrightFieldName;
++ static const std::string versionFieldName;
++ static const std::string trackNumberFieldName; //TODO: is this the right definition of the total number of songs available?
++ const Strigi::RegisteredField* titleField;
++ const Strigi::RegisteredField* artistField;
++ const Strigi::RegisteredField* copyrightField;
++ const Strigi::RegisteredField* versionField;
++ const Strigi::RegisteredField* trackNumberField;
++
++ const char* name() const {
++ return "SidThroughAnalyzer";
++ }
++ Strigi::StreamThroughAnalyzer* newInstance() const {
++ return new SidThroughAnalyzer(this);
++ }
++ void registerFields(Strigi::FieldRegister&);
++};
++
++#endif
+--- a/src/streamanalyzer/throughplugins/tgathroughanalyzer.cpp
++++ b/src/streamanalyzer/throughplugins/tgathroughanalyzer.cpp
+@@ -28,7 +28,7 @@
+ using namespace Strigi;
+
+ // AnalyzerFactory
+-const string TgaThroughAnalyzerFactory::compressionFieldName("compressed.compression_algorithm");
++const string TgaThroughAnalyzerFactory::compressionFieldName("http://freedesktop.org/standards/xesam/1.0/core#compressionAlgorithm");
+ const string TgaThroughAnalyzerFactory::colorDepthFieldName("http://freedesktop.org/standards/xesam/1.0/core#pixelDataBitDepth");
+ const string TgaThroughAnalyzerFactory::colorModeFieldName("http://freedesktop.org/standards/xesam/1.0/core#colorSpace");
+ const string TgaThroughAnalyzerFactory::widthFieldName("http://freedesktop.org/standards/xesam/1.0/core#width");
+@@ -41,6 +41,8 @@
+ colorModeField = reg.registerField(colorModeFieldName);
+ widthField = reg.registerField(widthFieldName);
+ heightField = reg.registerField(heightFieldName);
++
++ typeField = reg.typeField;
+ }
+
+ // Analyzer
+@@ -110,6 +112,7 @@
+
+ uint8_t colorDepth = *(buf+16);
+ analysisResult->addValue(factory->colorDepthField, colorDepth);
++ analysisResult->addValue(factory->typeField, "http://freedesktop.org/standards/xesam/1.0/core#Image");
+
+ return in;
+ }
+--- a/src/streamanalyzer/throughplugins/tgathroughanalyzer.h
++++ b/src/streamanalyzer/throughplugins/tgathroughanalyzer.h
+@@ -55,6 +55,9 @@
+ const Strigi::RegisteredField* compressionField;
+ const Strigi::RegisteredField* widthField;
+ const Strigi::RegisteredField* heightField;
++
++ const Strigi::RegisteredField* typeField;
++
+ const char* name() const {
+ return "TgaThroughAnalyzer";
+ }
+--- /dev/null
++++ b/src/streamanalyzer/throughplugins/wavthroughanalyzer.cpp
+@@ -0,0 +1,213 @@
++/* This file is part of Strigi Desktop Search, ported from code of:
++ * - Ryan Cumming <bodnar42 at phalynx.dhs.org> (Copyright (C) 2002)
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++
++#include "wavthroughanalyzer.h"
++#include "textutils.h"
++#include <strigi/strigiconfig.h>
++#include "analysisresult.h"
++#include "fieldtypes.h"
++#include <cstring>
++
++using namespace std;
++using namespace Strigi;
++
++// AnalyzerFactory
++
++
++//TODO: check values!
++const string WavThroughAnalyzerFactory::sampleSizeFieldName( "audio.sampleSize" );
++const string WavThroughAnalyzerFactory::sampleRateFieldName( "audio.sampleRate" );
++const string WavThroughAnalyzerFactory::channelsFieldName( "audio.channels" );
++const string WavThroughAnalyzerFactory::lengthFieldName ("audio.length");
++
++
++void
++WavThroughAnalyzerFactory::registerFields(FieldRegister& reg) {
++ sampleSizeField = reg.registerField(sampleSizeFieldName);
++ sampleRateField = reg.registerField(sampleRateFieldName);
++ channelsField = reg.registerField(channelsFieldName);
++ lengthField = reg.registerField(lengthFieldName);
++}
++
++// Analyzer
++void
++WavThroughAnalyzer::setIndexable(AnalysisResult* i) {
++ analysisResult = i;
++}
++
++InputStream*
++WavThroughAnalyzer::connectInputStream(InputStream* in) {
++ if( !in )
++ return in;
++
++ const char *c;
++ uint32_t format_size;
++ uint16_t format_tag;
++ uint16_t channel_count;
++ uint32_t sample_rate;
++ uint32_t bytes_per_second;
++ uint16_t bytes_per_sample;
++ uint16_t sample_size;
++ uint32_t data_size;
++ uint32_t unknown_chunk_size;
++ uint16_t unknown_chunk16;
++ bool have_fmt = false;
++ bool have_data = false;
++ bool eof = false;
++
++ static const char riff_signature[] = "RIFF";
++ static const char wav_signature[] = "WAVE";
++ static const char fmt_signature[] = "fmt ";
++ static const char data_signature[] = "data";
++ uint32_t signature_buffer;
++
++ // Remember: WAV files are little-endian
++ // Read and verify the RIFF signature
++ if (4 != in->read(c, 4, 4)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ signature_buffer = readLittleEndianUInt32(c);
++
++ if (memcmp(&signature_buffer, riff_signature, 4)) {
++ in->reset(0);
++ return in;
++ }
++
++ // Skip the next bit (total file size, pretty useless)
++ if (4 != in->skip (4)) {
++ in->reset(0);
++ return in;
++ }
++
++ // Read and verify the WAVE signature
++ if (4 != in->read(c, 4, 4)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ signature_buffer = readLittleEndianUInt32(c);
++
++ if (memcmp(&signature_buffer, wav_signature, 4)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++
++ // pretty dumb scanner, but better than what we had!
++ do
++ {
++ if (4 != in->read(c, 4, 4)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ signature_buffer = readLittleEndianUInt32(c);
++
++ if (!memcmp(&signature_buffer, fmt_signature, 4)) {
++ if (20 != in->read(c, 20, 20)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++
++ format_size = readLittleEndianUInt32 (c);
++ format_tag = readLittleEndianUInt16 (c+4);
++ channel_count = readLittleEndianUInt16 (c+6);
++ sample_rate = readLittleEndianUInt32 (c+8);
++ bytes_per_second = readLittleEndianUInt32 (c+12);
++ bytes_per_sample = readLittleEndianUInt16 (c+16);
++ sample_size = readLittleEndianUInt16 (c+18);
++ have_fmt = true;
++ if ( format_size > 16 ) {
++ for (unsigned int i = 0; i < (format_size-16+1)/2; i++) {
++ if (2 != in->skip(2)) {
++ in->reset (0);
++ return in;
++ }
++ }
++ }
++ }
++ else if (!memcmp(&signature_buffer, data_signature, 4)) {
++ if (4 != in->read(c, 4, 4)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ data_size = readLittleEndianUInt32 (c);
++ have_data = true;
++ }
++ else {
++ if (4 != in->read(c, 4, 4)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ unknown_chunk_size = readLittleEndianUInt32 (c);
++ for (unsigned int i = 0; i < (unknown_chunk_size+1)/2; i++) {
++ if (in->skip (2)) {
++ in->reset(0); // rewind to the start of the stream
++ return in;
++ }
++ }
++ }
++ if (have_data && have_fmt)
++ break;
++
++ if (1 != in->read(c, 1, 1))
++ eof = true;
++ else
++ in->reset(in->position() - 1);
++
++ } while (!eof);
++
++ if ( (!have_data) || (!have_fmt) ) {
++ in->reset (0);
++ return in;
++ }
++
++ // These values are downright illegal
++ if ((!channel_count) || (!bytes_per_second)) {
++ in->reset (0);
++ return in;
++ }
++
++ analysisResult->addValue( factory->sampleSizeField, sample_size );
++ analysisResult->addValue( factory->sampleRateField, sample_rate);
++ analysisResult->addValue( factory->channelsField, channel_count);
++ unsigned int wav_seconds = data_size / bytes_per_second;
++ analysisResult->addValue( factory->lengthField, wav_seconds);
++
++ in->reset(0); // rewind to the start of the stream
++ return in;
++}
++
++bool
++WavThroughAnalyzer::isReadyWithStream() {
++ return true;
++}
++
++//Factory
++class Factory : public AnalyzerFactoryFactory {
++public:
++ list<StreamThroughAnalyzerFactory*>
++ streamThroughAnalyzerFactories() const {
++ list<StreamThroughAnalyzerFactory*> af;
++ af.push_back(new WavThroughAnalyzerFactory());
++ return af;
++ }
++};
++
++STRIGI_ANALYZER_FACTORY(Factory)
+--- /dev/null
++++ b/src/streamanalyzer/throughplugins/wavthroughanalyzer.h
+@@ -0,0 +1,69 @@
++/* This file is part of Strigi Desktop Search
++ *
++ * Copyright (C) 2007 Flavio Castelli <flavio.castelli at gmail.com>
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Library General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Library General Public License for more details.
++ *
++ * You should have received a copy of the GNU Library General Public License
++ * along with this library; see the file COPYING.LIB. If not, write to
++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
++ * Boston, MA 02110-1301, USA.
++ */
++#ifndef STRIGI_WAVTHROUGHANALYZER
++#define STRIGI_WAVTHROUGHANALYZER
++
++#include "streamthroughanalyzer.h"
++#include "analyzerplugin.h"
++
++#include <string>
++
++namespace Strigi {
++ class RegisteredField;
++}
++class WavThroughAnalyzerFactory;
++
++class STRIGI_PLUGIN_API WavThroughAnalyzer
++ : public Strigi::StreamThroughAnalyzer {
++private:
++ Strigi::AnalysisResult* analysisResult;
++ const WavThroughAnalyzerFactory* factory;
++public:
++ WavThroughAnalyzer(const WavThroughAnalyzerFactory* f) :factory(f) {}
++ ~WavThroughAnalyzer() {}
++ void setIndexable(Strigi::AnalysisResult* i);
++ Strigi::InputStream *connectInputStream(Strigi::InputStream *in);
++ bool isReadyWithStream();
++ const char* name() const { return "WavThroughAnalyzer"; }
++};
++
++class WavThroughAnalyzerFactory
++ : public Strigi::StreamThroughAnalyzerFactory {
++friend class WavThroughAnalyzer;
++private:
++ static const std::string sampleSizeFieldName;
++ static const std::string sampleRateFieldName;
++ static const std::string channelsFieldName;
++ static const std::string lengthFieldName;
++ const Strigi::RegisteredField* sampleSizeField;
++ const Strigi::RegisteredField* sampleRateField;
++ const Strigi::RegisteredField* channelsField;
++ const Strigi::RegisteredField* lengthField;
++
++ const char* name() const {
++ return "WavThroughAnalyzer";
++ }
++ Strigi::StreamThroughAnalyzer* newInstance() const {
++ return new WavThroughAnalyzer(this);
++ }
++ void registerFields(Strigi::FieldRegister&);
++};
++
++#endif
+--- a/src/streamanalyzer/throughplugins/xbmthroughanalyzer.cpp
++++ b/src/streamanalyzer/throughplugins/xbmthroughanalyzer.cpp
+@@ -30,19 +30,17 @@
+ // AnalyzerFactory
+ const string XbmThroughAnalyzerFactory::widthFieldName("http://freedesktop.org/standards/xesam/1.0/core#width");
+ const string XbmThroughAnalyzerFactory::heightFieldName("http://freedesktop.org/standards/xesam/1.0/core#height");
+-const string XbmThroughAnalyzerFactory::xHotFieldName("cursor.hot_spot.x");
+-const string XbmThroughAnalyzerFactory::yHotFieldName("cursor.hot_spot.y");
++const string XbmThroughAnalyzerFactory::xHotFieldName("http://strigi.sf.net/ontologies/0.9#hotSpotX");
++const string XbmThroughAnalyzerFactory::yHotFieldName("http://strigi.sf.net/ontologies/0.9#hotSpotY");
+
+ void
+ XbmThroughAnalyzerFactory::registerFields(FieldRegister& reg) {
+- widthField = reg.registerField(widthFieldName,
+- FieldRegister::integerType, 1, 0);
+- heightField = reg.registerField(heightFieldName,
+- FieldRegister::integerType, 1, 0);
+- xHotField = reg.registerField(xHotFieldName,
+- FieldRegister::integerType, 1, 0);
+- yHotField = reg.registerField(yHotFieldName,
+- FieldRegister::integerType, 1, 0);
++ widthField = reg.registerField(widthFieldName);
++ heightField = reg.registerField(heightFieldName);
++ xHotField = reg.registerField(xHotFieldName);
++ yHotField = reg.registerField(yHotFieldName);
++
++ typeField = reg.typeField;
+ }
+
+ // Analyzer
+@@ -141,6 +139,8 @@
+ }
+ }
+
++ analysisResult->addValue(factory->typeField, "http://strigi.sf.net/ontologies/0.9#Cursor");
++
+ return in;
+ }
+
+--- a/src/streamanalyzer/throughplugins/xbmthroughanalyzer.h
++++ b/src/streamanalyzer/throughplugins/xbmthroughanalyzer.h
+@@ -56,6 +56,9 @@
+ const Strigi::RegisteredField* heightField;
+ const Strigi::RegisteredField* xHotField;
+ const Strigi::RegisteredField* yHotField;
++
++ const Strigi::RegisteredField* typeField;
++
+ const char* name() const {
+ return "XbmThroughAnalyzer";
+ }
+--- a/src/streamanalyzer/variant.cpp
++++ b/src/streamanalyzer/variant.cpp
+@@ -64,7 +64,9 @@
+ Variant::Variant(const Variant& v) :p(new VariantPrivate(*v.p)) {
+ }
+ Variant::~Variant() {
++ delete p;
+ }
++
+ Variant::Type
+ Variant::type() const { return p->vartype; }
+ const Variant&
+--- a/src/streamanalyzer/xesam/StrigiQueryBuilder.cc
++++ b/src/streamanalyzer/xesam/StrigiQueryBuilder.cc
+@@ -139,7 +139,7 @@
+
+ STRIGI_LOG_DEBUG ("StrigiQueryBuilder.on_selection", msg.str())
+
+- msg.clear();
++ msg.str("");
+ msg << "field names are ";
+
+ for (set<string>::const_iterator iter = field_names.begin();
+@@ -163,11 +163,11 @@
+ //TODO: handle modifiers.m_phrase
+ parsedQuery.term().setSlack(modifiers.m_slack);
+
+- msg.clear();
++ msg.str("");
+ msg << "there're " << field_values.size() << " field values";
+ STRIGI_LOG_DEBUG ("StrigiQueryBuilder.on_selection", msg.str())
+
+- msg.clear();
++ msg.str("");
+ msg << "field values are ";
+
+ for (vector<string>::const_iterator valueIter = field_values.begin();
+@@ -184,7 +184,7 @@
+
+ parsedQuery.setNegate(modifiers.m_negate);
+
+- msg.clear();
++ msg.str("");
+ msg << "collector is | "
+ << (m_collector.m_collector == And ? "AND" : "OR" ) << " |";
+ STRIGI_LOG_DEBUG ("StrigiQueryBuilder.on_selection", msg.str())
+--- a/src/streamanalyzer/xesam/XesamQLParser.cc
++++ b/src/streamanalyzer/xesam/XesamQLParser.cc
+@@ -212,7 +212,7 @@
+ return true;
+ }
+
+- msg.clear();
++ msg.str("");
+ msg << pLocalName << " " << xmlTextReaderHasValue(reader);
+ STRIGI_LOG_DEBUG ("XesamQLParser.process_node",msg.str())
+
+@@ -220,7 +220,7 @@
+ {
+ if (xmlStrncmp(pLocalName, BAD_CAST"request", 7) != 0)
+ {
+- msg.clear();
++ msg.str("");
+ msg << "expected request, found " << pLocalName;
+ STRIGI_LOG_ERROR ("XesamQLParser.process_node", msg.str())
+
+--- a/src/strigicmd/strigicmd.cpp
++++ b/src/strigicmd/strigicmd.cpp
+@@ -45,39 +45,6 @@
+ map<char, string> options;
+ vector<string> arguments;
+
+-AnalyzerConfiguration::FieldType
+-operator|(AnalyzerConfiguration::FieldType a, AnalyzerConfiguration::FieldType b){
+- return static_cast<AnalyzerConfiguration::FieldType>((int)a|(int)b);
+-}
+-
+-class CustomAnalyzerConfiguration: public AnalyzerConfiguration {
+-public:
+-
+-// None = 0x0000 /**< No hint. */,
+-// Binary = 0x0001 /**< The field should be stored as binary data. */,
+-// Compressed = 0x0002 /**< If the field is stored, the data
+-// should be compressed. */,
+-// Indexed = 0x0004 /**< The field should be indexed. */,
+-// Stored = 0x0020 /**< The field should be stored. */,
+-// Tokenized = 0x0040 /**< If the field contains text, it
+-// should be tokenized. */
+- AnalyzerConfiguration::FieldType indexType(const RegisteredField* field) const {
+- if (string("chemistry.inchi").compare(field->key()) == 0) {
+- return Stored|Indexed;
+- } else if (string("chemistry.molecular_formula").compare(field->key()) == 0) {
+- return Stored|Indexed;
+-/* } else if (string("chemistry.").compare(field->key()) == 0) {
+- return Binary|Stored|Indexed;
+- } else if (string("chemistry.").compare(field->key()) == 0) {
+- return Binary|Stored|Indexed;
+- } else if (string("chemistry.").compare(field->key()) == 0) {
+- return Binary|Stored|Indexed; */
+- } else {
+- return Tokenized|Stored|Indexed;
+- }
+- }
+-};
+-
+ void
+ parseArguments(int argc, char** argv) {
+ // parse arguments
+@@ -127,7 +94,7 @@
+ pe(" %s create [-j num] -t backend -d indexdir [-i include] [-x exclude] files/dirs\n", cmd);
+ pe(" %s deindex -t backend -d indexdir files/dirs\n", cmd);
+ pe(" %s get -t backend -d indexdir files\n", cmd);
+- pe(" %s listFiles -t backend -d indexdir\n", cmd);
++ pe(" %s listFiles -t backend -d indexdir [parent dir]\n", cmd);
+ pe(" %s listFields -t backend -d indexdir\n", cmd);
+ //TODO: find a better definition for query?
+ pe(" %s query -t backend -d indexdir queries\n", cmd);
+@@ -278,7 +245,6 @@
+ filters.push_back(make_pair<bool,string>(true, included_filter));
+ filters.push_back(make_pair<bool,string>(false, excluded_filter));
+
+- //CustomAnalyzerConfiguration config;
+ AnalyzerConfiguration config;
+ config.setFilters(filters);
+
+@@ -368,8 +334,17 @@
+ if (manager == 0) {
+ return usage(argc, argv);
+ }
++
+ IndexReader* reader = manager->indexReader();
+- listFiles(reader, "");
++ if (arguments.empty())
++ listFiles(reader, "");
++
++ for (vector<string>::iterator iter = arguments.begin();
++ iter != arguments.end(); iter++) {
++ cout << "indexed files under " << *iter << endl;
++ listFiles(reader, *iter);
++ }
++
+ IndexPluginLoader::deleteIndexManager(manager);
+ return 0;
+ }
+--- a/src/xmlindexer/xmlindexwriter.h
++++ b/src/xmlindexer/xmlindexwriter.h
+@@ -79,11 +79,14 @@
+ out << "<";
+ } else if (c == '>') {
+ out << ">";
+- } else if (isspace(c)) {
+- if (!lastwhite) {
++ } else if (isspace(c) != 0) {
++ // we've to handle dos formatting
++ //'\r' char is ignored, it isn't wroten to out and doesn't
++ //change lastwhite value (so the following '\n' will be handled)
++ if (!lastwhite && (c!= '\r')) {
+ out.put(c);
++ lastwhite = true;
+ }
+- lastwhite = true;
+ } else {
+ lastwhite = false;
+ out.put(c);
+--- a/src/xsd/CMakeLists.txt
++++ b/src/xsd/CMakeLists.txt
+@@ -20,7 +20,8 @@
+ endif (LIBXML2_FOUND)
+
+ # java is required to compile the xsd parser
+-find_package(Java REQUIRED)
++OPTION(ENABLE_REGENERATEXSD "regenerate xsd parser from *.xsd files(not normally required)" ON)
++FIND_OPTIONAL_DEP(Java ENABLE_REGENERATEXSD JAVA_COMPILE "generating xsd parser from *.xsd files")
+
+ # loop over all xsd files, the next line does not work everywhere,
+ # so we dont use it currently
+@@ -57,4 +58,3 @@
+ target_link_libraries(${XSDNAME}test ${XSDNAME} xmlstream)
+
+ endforeach (XSDFILE ${XSDFILES})
+-
+--- a/tests/CMakeLists.txt
++++ b/tests/CMakeLists.txt
+@@ -8,7 +8,9 @@
+
+ # cppunit requires exception support
+ IF(NOT WIN32)
+- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions")
++ IF (CMAKE_COMPILER_IS_GNUCXX)
++ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions")
++ ENDIF(CMAKE_COMPILER_IS_GNUCXX)
+ ENDIF(NOT WIN32)
+
+ # a convenience library that adds a unified way of build a test executable
+--- a/tests/indextesters/indextest.cpp
++++ b/tests/indextesters/indextest.cpp
+@@ -17,6 +17,9 @@
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
++
++#include <stdlib.h>
++
+ #include "indextest.h"
+ #include "indexmanager.h"
+ #include "indexpluginloader.h"
+--- a/tests/streamanalyzer/xesam/xesam2strigitest.cpp
++++ b/tests/streamanalyzer/xesam/xesam2strigitest.cpp
+@@ -25,6 +25,7 @@
+ #include <fstream>
+ #include <stdlib.h>
+ #include <unistd.h>
++#include <string.h>
+
+ using namespace std;
+ using namespace strigiunittest;
+--- a/tests/test_runner.cpp
++++ b/tests/test_runner.cpp
+@@ -20,13 +20,19 @@
+
+ #include <cppunit/TestCaller.h>
+ #include <cppunit/extensions/TestFactoryRegistry.h>
++#include <cppunit/CompilerOutputter.h>
++#include <cppunit/TextTestProgressListener.h>
+ #include <cppunit/TestResult.h>
+ #include <cppunit/TextTestRunner.h>
++#include <cppunit/TestResultCollector.h>
++#include <stdexcept>
+
+ #include "strigilogging.h"
+ #include "config.h"
+
+ #include <iostream>
++#include <stdlib.h>
++
+ using namespace std;
+
+ int main() {
+@@ -41,24 +47,48 @@
+ BINARYDIR"/src/estraierindexer:"
+ BINARYDIR"/src/sqliteindexer", 1);
+
+-cerr << BINARYDIR << endl;
++ cerr << BINARYDIR << endl;
+
+ STRIGI_LOG_INIT_BASIC()
+
+ // Get the top level suite from the registry
+- CppUnit::Test *suite = CppUnit::TestFactoryRegistry::getRegistry().makeTest();
++ CppUnit::Test *suite;
++ suite = CppUnit::TestFactoryRegistry::getRegistry().makeTest();
+
+ // Adds the test to the list of test to run
+ CppUnit::TextTestRunner runner;
+ runner.addTest( suite );
++
++ // Create the event manager and test controller
++ CppUnit::TestResult controller;
++
++ // Add a listener that colllects test result
++ CppUnit::TestResultCollector result;
++ controller.addListener( &result );
++
++ // Add a listener that print dots as test run.
++ CppUnit::TextTestProgressListener progress;
++ controller.addListener( &progress );
++
++ try
++ {
++// std::cout << "Running " << testPath;
++ runner.run( controller);
++
++ std::cerr << std::endl;
++
++ // Print test in a compiler compatible format.
++ CppUnit::CompilerOutputter outputter( &result, std::cerr );
++ outputter.write();
++ }
++ catch ( std::invalid_argument &e ) // Test path not resolved
++ {
++ std::cerr << std::endl
++ << "ERROR: " << e.what()
++ << std::endl;
++ return 0;
++ }
+
+- // Change the default outputter to a compiler error format outputter
+-// runner.setOutputter( new CppUnit::CompilerOutputter( &runner.result(),
+-// std::cerr ) );
+- // Run the tests.
+- bool wasSucessful = runner.run();
+-
+- // Return error code 1 if the one of test failed.
+- return wasSucessful ? 0 : 1;
++ return result.wasSuccessful() ? 0 : 1;
+ }
+
Modified: kde-extras/strigi/trunk/debian/patches/series
===================================================================
--- kde-extras/strigi/trunk/debian/patches/series 2008-01-13 01:38:36 UTC (rev 8941)
+++ kde-extras/strigi/trunk/debian/patches/series 2008-01-13 11:15:45 UTC (rev 8942)
@@ -1 +1 @@
-01_strigi_branch_r729946.diff
+01_strigi_branch_r760102.diff
Modified: kde-extras/strigi/trunk/debian/rules
===================================================================
--- kde-extras/strigi/trunk/debian/rules 2008-01-13 01:38:36 UTC (rev 8941)
+++ kde-extras/strigi/trunk/debian/rules 2008-01-13 11:15:45 UTC (rev 8942)
@@ -4,14 +4,22 @@
UPFILENAME = $(DEB_SOURCE_PACKAGE)-$(shell echo $(DEB_UPSTREAM_VERSION) | sed 's/~/-/').tar.bz2
URL = http://www.vandenoever.info/software/strigi/$(UPFILENAME)
-include debian/cmake.mk
+include /usr/share/cdbs/1/class/cmake.mk
include /usr/share/cdbs/1/rules/debhelper.mk
include /usr/share/cdbs/1/rules/patchsys-quilt.mk
include /usr/share/cdbs/1/rules/utils.mk
-#DEB_CMAKE_EXTRA_FLAGS = -DCMAKE_BUILD_TYPE=debugfull -DENABLE_DBUS:BOOL=ON -DENABLE_INOTIFY:BOOL=OFF -DENABLE_LOG4CXX:BOOL=OFF
-DEB_DH_INSTALL_ARGS = --sourcedir=debian/tmp
-#DEB_STRIP_EXCLUDE = so
+CURRENTVERSION := $(shell head -1 debian/changelog | sed 's/[^(]*(\([^)]*\)).*/\1/')
+
+#DEB_CMAKE_EXTRA_FLAGS := -DCMAKE_BUILD_TYPE=debugfull -DENABLE_DBUS:BOOL=ON -DENABLE_INOTIFY:BOOL=OFF -DENABLE_LOG4CXX:BOOL=OFF
+DEB_CMAKE_EXTRA_FLAGS += \
+ -DCMAKE_CXX_COMPILER:FILEPATH="g++" \
+ -DCMAKE_SHARED_LINKER_FLAGS="-Wl,--no-undefined -Wl,--as-needed" \
+ -DCMAKE_MODULE_LINKER_FLAGS="-Wl,--no-undefined -Wl,--as-needed" \
+ -DCMAKE_EXE_LINKER_FLAGS="-Wl,--no-undefined -Wl,--as-needed"
+DEB_DH_INSTALL_ARGS := --sourcedir=debian/tmp
+DEB_DH_MAKESHLIBS_ARGS_ALL := -V
+#DEB_STRIP_EXCLUDE := so
#DEB_STRIP_EXCLUDE += strigi
binary-install/deskbar-plugins-strigi::
@@ -19,6 +27,10 @@
debian/deskbar-plugins-strigi/usr/lib/deskbar-applet/handlers/strigi.py
dh_pysupport /usr/lib/deskbar-applet/handlers
+clean::
+ # generate on build
+ rm -f debian/shlibs.local
+
get-orig-source:
@@dh_testdir
@@[ -d ../tarballs/. ]||mkdir -p ../tarballs
@@ -27,3 +39,9 @@
@@echo Converting $(UPFILENAME) to $(FILENAME)
@@bzcat ../tarballs/$(UPFILENAME) | gzip -9 > ../tarballs/$(FILENAME)
+# Generate shlibs local files
+$(patsubst %,binary-fixup/%,$(DEB_ALL_PACKAGES)) :: binary-fixup/%: binary-strip/%
+ if test -e debian/$(cdbs_curpkg)/DEBIAN/shlibs ; then \
+ sed 's/>=[^)]*/= $(CURRENTVERSION)/' debian/$(cdbs_curpkg)/DEBIAN/shlibs >> debian/shlibs.local ;\
+ fi
+
More information about the pkg-kde-commits
mailing list