<commit_before>#ifndef KISSFFT_CLASS_HH
#define KISSFFT_CLASS_HH
#include <complex>
#include <vector>

template <typename T_Scalar,
         typename T_Complex=std::complex<T_Scalar>
         >
class kissfft
{
    public:
        typedef T_Scalar scalar_type;
        typedef T_Complex cpx_type;

        kissfft( std::size_t nfft,
                 bool inverse )
            :_nfft(nfft)
            ,_inverse(inverse)
        {
            // fill twiddle factors
            _twiddles.resize(_nfft);
            const scalar_type phinc =  (_inverse?2:-2)* acos( (scalar_type) -1)  / _nfft;
            for (std::size_t i=0;i<_nfft;++i)
                _twiddles[i] = exp( cpx_type(0,i*phinc) );

            //factorize
            //start factoring out 4's, then 2's, then 3,5,7,9,...
            std::size_t n= _nfft;
            std::size_t p=4;
            do {
                while (n % p) {
                    switch (p) {
                        case 4: p = 2; break;
                        case 2: p = 3; break;
                        default: p += 2; break;
                    }
                    if (p*p>n)
                        p = n;// no more factors
                }
                n /= p;
                _stageRadix.push_back(p);
                _stageRemainder.push_back(n);
            }while(n>1);
        }

        /// Calculates the complex Discrete Fourier Transform.
        ///
        /// The size of the passed arrays must be passed in the constructor.
        /// The sum of the squares of the absolute values in the @c dst
        /// array will be @c N times the sum of the squares of the absolute
        /// values in the @c src array, where @c N is the size of the array.
        /// In other words, the l_2 norm of the resulting array will be
        /// @c sqrt(N) times as big as the l_2 norm of the input array.
        /// This is also the case when the inverse flag is set in the
        /// constructor. Hence when applying the same transform twice, but with
        /// the inverse flag changed the second time, then the result will
        /// be equal to the original input times @c N.
        void transform( const cpx_type * src,
                        cpx_type * dst ) const
        {
            kf_work(0, dst, src, 1,1);
        }

        /// Calculates the Discrete Fourier Transform (DFT) of a real input
        /// of size @c 2*N.
        ///
        /// The 0-th and N-th value of the DFT are real numbers. These are
        /// stored in @c dst[0].real() and @c dst[1].imag() respectively.
        /// The remaining DFT values up to the index N-1 are stored in
        /// @c dst[1] to @c dst[N-1].
        /// The other half of the DFT values can be calculated from the
        /// symmetry relation
        ///     @code
        ///         DFT(src)[2*N-k] == conj( DFT(src)[k] );
        ///     @endcode
        /// The same scaling factors as in @c transform() apply.
        ///
        /// @note For this to work, the types @c scalar_type and @c cpx_type
        /// must fulfill the following requirements:
        ///
        /// For any object @c z of type @c cpx_type,
        /// @c reinterpret_cast<scalar_type(&)[2]>(z)[0] is the real part of @c z and
        /// @c reinterpret_cast<scalar_type(&)[2]>(z)[1] is the imaginary part of @c z.
        /// For any pointer to an element of an array of @c cpx_type named @c p
        /// and any valid array index @c i, @c reinterpret_cast<T*>(p)[2*i]
        /// is the real part of the complex number @c p[i], and
        /// @c reinterpret_cast<T*>(p)[2*i+1] is the imaginary part of the
        /// complex number @c p[i].
        ///
        /// Since C++11, these requirements are guaranteed to be satisfied for
        /// @c scalar_types being @c float, @c double or @c long @c double
        /// together with @c cpx_type being @c std::complex<scalar_type>.
        void transform_real( const scalar_type * src,
                             cpx_type * dst ) const
        {
            const std::size_t N = _nfft;
            if ( N == 0 )
              return;

            // perform complex FFT
            transform( reinterpret_cast<const cpx_type*>(src), dst );

            // post processing for k = 0 and k = N
            dst[0] = cpx_type( dst[0].real() + dst[0].imag(),
                               dst[0].real() - dst[0].imag() );

            // post processing for all the other k = 1, 2, ..., N-1
            const scalar_type pi = acos( (scalar_type) -1);
            const scalar_type half_phi_inc = ( _inverse ? pi : -pi ) / N;
            const cpx_type twiddle_mul = exp( cpx_type(0, half_phi_inc) );
            for ( std::size_t k = 1; 2*k < N; ++k )
            {
              const cpx_type w = 0.5 * cpx_type(
                   dst[k].real() + dst[N-k].real(),
                   dst[k].imag() - dst[N-k].imag() );
              const cpx_type z = 0.5 * cpx_type(
                   dst[k].imag() + dst[N-k].imag(),
                  -dst[k].real() + dst[N-k].real() );
              const cpx_type twiddle =
                  k % 2 == 0 ?
                  _twiddles[k/2] :
                  _twiddles[k/2] * twiddle_mul;
              dst[  k] =       w + twiddle * z;
              dst[N-k] = conj( w - twiddle * z );
            }
            if ( N % 2 == 0 )
              dst[N/2] = conj( dst[N/2] );
        }

    private:
        void kf_work( std::size_t stage,
                      cpx_type * Fout,
                      const cpx_type * f,
                      std::size_t fstride,
                      std::size_t in_stride) const
        {
            const std::size_t p = _stageRadix[stage];
            const std::size_t m = _stageRemainder[stage];
            cpx_type * const Fout_beg = Fout;
            cpx_type * const Fout_end = Fout + p*m;

            if (m==1) {
                do{
                    *Fout = *f;
                    f += fstride*in_stride;
                }while(++Fout != Fout_end );
            }else{
                do{
                    // recursive call:
                    // DFT of size m*p performed by doing
                    // p instances of smaller DFTs of size m,
                    // each one takes a decimated version of the input
                    kf_work(stage+1, Fout , f, fstride*p,in_stride);
                    f += fstride*in_stride;
                }while( (Fout += m) != Fout_end );
            }

            Fout=Fout_beg;

            // recombine the p smaller DFTs
            switch (p) {
                case 2: kf_bfly2(Fout,fstride,m); break;
                case 3: kf_bfly3(Fout,fstride,m); break;
                case 4: kf_bfly4(Fout,fstride,m); break;
                case 5: kf_bfly5(Fout,fstride,m); break;
                default: kf_bfly_generic(Fout,fstride,m,p); break;
            }
        }

        void kf_bfly2( cpx_type * Fout, const size_t fstride, std::size_t m) const
        {
            for (std::size_t k=0;k<m;++k) {
                const cpx_type t = Fout[m+k] * _twiddles[k*fstride];
                Fout[m+k] = Fout[k] - t;
                Fout[k] += t;
            }
        }

        void kf_bfly4( cpx_type * Fout, const std::size_t fstride, const std::size_t m) const
        {
            cpx_type scratch[7];
            const scalar_type negative_if_inverse = _inverse ? -1 : +1;
            for (std::size_t k=0;k<m;++k) {
                scratch[0] = Fout[k+  m] * _twiddles[k*fstride  ];
                scratch[1] = Fout[k+2*m] * _twiddles[k*fstride*2];
                scratch[2] = Fout[k+3*m] * _twiddles[k*fstride*3];
                scratch[5] = Fout[k] - scratch[1];

                Fout[k] += scratch[1];
                scratch[3] = scratch[0] + scratch[2];
                scratch[4] = scratch[0] - scratch[2];
                scratch[4] = cpx_type( scratch[4].imag()*negative_if_inverse ,
                                      -scratch[4].real()*negative_if_inverse );

                Fout[k+2*m]  = Fout[k] - scratch[3];
                Fout[k    ]+= scratch[3];
                Fout[k+  m] = scratch[5] + scratch[4];
                Fout[k+3*m] = scratch[5] - scratch[4];
            }
        }

        void kf_bfly3( cpx_type * Fout, const std::size_t fstride, const std::size_t m) const
        {
            std::size_t k=m;
            const std::size_t m2 = 2*m;
            const cpx_type *tw1,*tw2;
            cpx_type scratch[5];
            const cpx_type epi3 = _twiddles[fstride*m];

            tw1=tw2=&_twiddles[0];

            do{
                scratch[1] = Fout[m]  * *tw1;
                scratch[2] = Fout[m2] * *tw2;

                scratch[3] = scratch[1] + scratch[2];
                scratch[0] = scratch[1] - scratch[2];
                tw1 += fstride;
                tw2 += fstride*2;

                Fout[m] = Fout[0] - scratch[3]*scalar_type(0.5);
                scratch[0] *= epi3.imag();

                Fout[0] += scratch[3];

                Fout[m2] = cpx_type(  Fout[m].real() + scratch[0].imag() , Fout[m].imag() - scratch[0].real() );

                Fout[m] += cpx_type( -scratch[0].imag(),scratch[0].real() );
                ++Fout;
            }while(--k);
        }

        void kf_bfly5( cpx_type * Fout, const std::size_t fstride, const std::size_t m) const
        {
            cpx_type *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
            cpx_type scratch[13];
            const cpx_type ya = _twiddles[fstride*m];
            const cpx_type yb = _twiddles[fstride*2*m];

            Fout0=Fout;
            Fout1=Fout0+m;
            Fout2=Fout0+2*m;
            Fout3=Fout0+3*m;
            Fout4=Fout0+4*m;

            for ( std::size_t u=0; u<m; ++u ) {
                scratch[0] = *Fout0;

                scratch[1] = *Fout1 * _twiddles[  u*fstride];
                scratch[2] = *Fout2 * _twiddles[2*u*fstride];
                scratch[3] = *Fout3 * _twiddles[3*u*fstride];
                scratch[4] = *Fout4 * _twiddles[4*u*fstride];

                scratch[7] = scratch[1] + scratch[4];
                scratch[10]= scratch[1] - scratch[4];
                scratch[8] = scratch[2] + scratch[3];
                scratch[9] = scratch[2] - scratch[3];

                *Fout0 += scratch[7];
                *Fout0 += scratch[8];

                scratch[5] = scratch[0] + cpx_type(
                        scratch[7].real()*ya.real() + scratch[8].real()*yb.real(),
                        scratch[7].imag()*ya.real() + scratch[8].imag()*yb.real()
                        );

                scratch[6] =  cpx_type(
                         scratch[10].imag()*ya.imag() + scratch[9].imag()*yb.imag(),
                        -scratch[10].real()*ya.imag() - scratch[9].real()*yb.imag()
                        );

                *Fout1 = scratch[5] - scratch[6];
                *Fout4 = scratch[5] + scratch[6];

                scratch[11] = scratch[0] +
                    cpx_type(
                            scratch[7].real()*yb.real() + scratch[8].real()*ya.real(),
                            scratch[7].imag()*yb.real() + scratch[8].imag()*ya.real()
                            );

                scratch[12] = cpx_type(
                        -scratch[10].imag()*yb.imag() + scratch[9].imag()*ya.imag(),
                         scratch[10].real()*yb.imag() - scratch[9].real()*ya.imag()
                        );

                *Fout2 = scratch[11] + scratch[12];
                *Fout3 = scratch[11] - scratch[12];

                ++Fout0;
                ++Fout1;
                ++Fout2;
                ++Fout3;
                ++Fout4;
            }
        }

        /* perform the butterfly for one stage of a mixed radix FFT */
        void kf_bfly_generic(
                cpx_type * Fout,
                const size_t fstride,
                std::size_t m,
                std::size_t p
                ) const
        {
            const cpx_type * twiddles = &_twiddles[0];
            cpx_type scratchbuf[p];

            for ( std::size_t u=0; u<m; ++u ) {
                std::size_t k = u;
                for ( std::size_t q1=0 ; q1<p ; ++q1 ) {
                    scratchbuf[q1] = Fout[ k  ];
                    k += m;
                }

                k=u;
                for ( std::size_t q1=0 ; q1<p ; ++q1 ) {
                    std::size_t twidx=0;
                    Fout[ k ] = scratchbuf[0];
                    for ( std::size_t q=1;q<p;++q ) {
                        twidx += fstride * k;
                        if (twidx>=_nfft)
                          twidx-=_nfft;
                        Fout[ k ] += scratchbuf[q] * twiddles[twidx];
                    }
                    k += m;
                }
            }
        }

        std::size_t _nfft;
        bool _inverse;
        std::vector<cpx_type> _twiddles;
        std::vector<std::size_t> _stageRadix;
        std::vector<std::size_t> _stageRemainder;
};
#endif
<commit_msg>Normalized identation in new code.<commit_after>#ifndef KISSFFT_CLASS_HH
#define KISSFFT_CLASS_HH
#include <complex>
#include <vector>

template <typename T_Scalar,
         typename T_Complex=std::complex<T_Scalar>
         >
class kissfft
{
    public:
        typedef T_Scalar scalar_type;
        typedef T_Complex cpx_type;

        kissfft( std::size_t nfft,
                 bool inverse )
            :_nfft(nfft)
            ,_inverse(inverse)
        {
            // fill twiddle factors
            _twiddles.resize(_nfft);
            const scalar_type phinc =  (_inverse?2:-2)* acos( (scalar_type) -1)  / _nfft;
            for (std::size_t i=0;i<_nfft;++i)
                _twiddles[i] = exp( cpx_type(0,i*phinc) );

            //factorize
            //start factoring out 4's, then 2's, then 3,5,7,9,...
            std::size_t n= _nfft;
            std::size_t p=4;
            do {
                while (n % p) {
                    switch (p) {
                        case 4: p = 2; break;
                        case 2: p = 3; break;
                        default: p += 2; break;
                    }
                    if (p*p>n)
                        p = n;// no more factors
                }
                n /= p;
                _stageRadix.push_back(p);
                _stageRemainder.push_back(n);
            }while(n>1);
        }

        /// Calculates the complex Discrete Fourier Transform.
        ///
        /// The size of the passed arrays must be passed in the constructor.
        /// The sum of the squares of the absolute values in the @c dst
        /// array will be @c N times the sum of the squares of the absolute
        /// values in the @c src array, where @c N is the size of the array.
        /// In other words, the l_2 norm of the resulting array will be
        /// @c sqrt(N) times as big as the l_2 norm of the input array.
        /// This is also the case when the inverse flag is set in the
        /// constructor. Hence when applying the same transform twice, but with
        /// the inverse flag changed the second time, then the result will
        /// be equal to the original input times @c N.
        void transform( const cpx_type * src,
                        cpx_type * dst ) const
        {
            kf_work(0, dst, src, 1,1);
        }

        /// Calculates the Discrete Fourier Transform (DFT) of a real input
        /// of size @c 2*N.
        ///
        /// The 0-th and N-th value of the DFT are real numbers. These are
        /// stored in @c dst[0].real() and @c dst[1].imag() respectively.
        /// The remaining DFT values up to the index N-1 are stored in
        /// @c dst[1] to @c dst[N-1].
        /// The other half of the DFT values can be calculated from the
        /// symmetry relation
        ///     @code
        ///         DFT(src)[2*N-k] == conj( DFT(src)[k] );
        ///     @endcode
        /// The same scaling factors as in @c transform() apply.
        ///
        /// @note For this to work, the types @c scalar_type and @c cpx_type
        /// must fulfill the following requirements:
        ///
        /// For any object @c z of type @c cpx_type,
        /// @c reinterpret_cast<scalar_type(&)[2]>(z)[0] is the real part of @c z and
        /// @c reinterpret_cast<scalar_type(&)[2]>(z)[1] is the imaginary part of @c z.
        /// For any pointer to an element of an array of @c cpx_type named @c p
        /// and any valid array index @c i, @c reinterpret_cast<T*>(p)[2*i]
        /// is the real part of the complex number @c p[i], and
        /// @c reinterpret_cast<T*>(p)[2*i+1] is the imaginary part of the
        /// complex number @c p[i].
        ///
        /// Since C++11, these requirements are guaranteed to be satisfied for
        /// @c scalar_types being @c float, @c double or @c long @c double
        /// together with @c cpx_type being @c std::complex<scalar_type>.
        void transform_real( const scalar_type * src,
                             cpx_type * dst ) const
        {
            const std::size_t N = _nfft;
            if ( N == 0 )
                return;

            // perform complex FFT
            transform( reinterpret_cast<const cpx_type*>(src), dst );

            // post processing for k = 0 and k = N
            dst[0] = cpx_type( dst[0].real() + dst[0].imag(),
                               dst[0].real() - dst[0].imag() );

            // post processing for all the other k = 1, 2, ..., N-1
            const scalar_type pi = acos( (scalar_type) -1);
            const scalar_type half_phi_inc = ( _inverse ? pi : -pi ) / N;
            const cpx_type twiddle_mul = exp( cpx_type(0, half_phi_inc) );
            for ( std::size_t k = 1; 2*k < N; ++k )
            {
                const cpx_type w = 0.5 * cpx_type(
                     dst[k].real() + dst[N-k].real(),
                     dst[k].imag() - dst[N-k].imag() );
                const cpx_type z = 0.5 * cpx_type(
                     dst[k].imag() + dst[N-k].imag(),
                    -dst[k].real() + dst[N-k].real() );
                const cpx_type twiddle =
                    k % 2 == 0 ?
                    _twiddles[k/2] :
                    _twiddles[k/2] * twiddle_mul;
                dst[  k] =       w + twiddle * z;
                dst[N-k] = conj( w - twiddle * z );
            }
            if ( N % 2 == 0 )
                dst[N/2] = conj( dst[N/2] );
        }

    private:
        void kf_work( std::size_t stage,
                      cpx_type * Fout,
                      const cpx_type * f,
                      std::size_t fstride,
                      std::size_t in_stride) const
        {
            const std::size_t p = _stageRadix[stage];
            const std::size_t m = _stageRemainder[stage];
            cpx_type * const Fout_beg = Fout;
            cpx_type * const Fout_end = Fout + p*m;

            if (m==1) {
                do{
                    *Fout = *f;
                    f += fstride*in_stride;
                }while(++Fout != Fout_end );
            }else{
                do{
                    // recursive call:
                    // DFT of size m*p performed by doing
                    // p instances of smaller DFTs of size m,
                    // each one takes a decimated version of the input
                    kf_work(stage+1, Fout , f, fstride*p,in_stride);
                    f += fstride*in_stride;
                }while( (Fout += m) != Fout_end );
            }

            Fout=Fout_beg;

            // recombine the p smaller DFTs
            switch (p) {
                case 2: kf_bfly2(Fout,fstride,m); break;
                case 3: kf_bfly3(Fout,fstride,m); break;
                case 4: kf_bfly4(Fout,fstride,m); break;
                case 5: kf_bfly5(Fout,fstride,m); break;
                default: kf_bfly_generic(Fout,fstride,m,p); break;
            }
        }

        void kf_bfly2( cpx_type * Fout, const size_t fstride, std::size_t m) const
        {
            for (std::size_t k=0;k<m;++k) {
                const cpx_type t = Fout[m+k] * _twiddles[k*fstride];
                Fout[m+k] = Fout[k] - t;
                Fout[k] += t;
            }
        }

        void kf_bfly4( cpx_type * Fout, const std::size_t fstride, const std::size_t m) const
        {
            cpx_type scratch[7];
            const scalar_type negative_if_inverse = _inverse ? -1 : +1;
            for (std::size_t k=0;k<m;++k) {
                scratch[0] = Fout[k+  m] * _twiddles[k*fstride  ];
                scratch[1] = Fout[k+2*m] * _twiddles[k*fstride*2];
                scratch[2] = Fout[k+3*m] * _twiddles[k*fstride*3];
                scratch[5] = Fout[k] - scratch[1];

                Fout[k] += scratch[1];
                scratch[3] = scratch[0] + scratch[2];
                scratch[4] = scratch[0] - scratch[2];
                scratch[4] = cpx_type( scratch[4].imag()*negative_if_inverse ,
                                      -scratch[4].real()*negative_if_inverse );

                Fout[k+2*m]  = Fout[k] - scratch[3];
                Fout[k    ]+= scratch[3];
                Fout[k+  m] = scratch[5] + scratch[4];
                Fout[k+3*m] = scratch[5] - scratch[4];
            }
        }

        void kf_bfly3( cpx_type * Fout, const std::size_t fstride, const std::size_t m) const
        {
            std::size_t k=m;
            const std::size_t m2 = 2*m;
            const cpx_type *tw1,*tw2;
            cpx_type scratch[5];
            const cpx_type epi3 = _twiddles[fstride*m];

            tw1=tw2=&_twiddles[0];

            do{
                scratch[1] = Fout[m]  * *tw1;
                scratch[2] = Fout[m2] * *tw2;

                scratch[3] = scratch[1] + scratch[2];
                scratch[0] = scratch[1] - scratch[2];
                tw1 += fstride;
                tw2 += fstride*2;

                Fout[m] = Fout[0] - scratch[3]*scalar_type(0.5);
                scratch[0] *= epi3.imag();

                Fout[0] += scratch[3];

                Fout[m2] = cpx_type(  Fout[m].real() + scratch[0].imag() , Fout[m].imag() - scratch[0].real() );

                Fout[m] += cpx_type( -scratch[0].imag(),scratch[0].real() );
                ++Fout;
            }while(--k);
        }

        void kf_bfly5( cpx_type * Fout, const std::size_t fstride, const std::size_t m) const
        {
            cpx_type *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
            cpx_type scratch[13];
            const cpx_type ya = _twiddles[fstride*m];
            const cpx_type yb = _twiddles[fstride*2*m];

            Fout0=Fout;
            Fout1=Fout0+m;
            Fout2=Fout0+2*m;
            Fout3=Fout0+3*m;
            Fout4=Fout0+4*m;

            for ( std::size_t u=0; u<m; ++u ) {
                scratch[0] = *Fout0;

                scratch[1] = *Fout1 * _twiddles[  u*fstride];
                scratch[2] = *Fout2 * _twiddles[2*u*fstride];
                scratch[3] = *Fout3 * _twiddles[3*u*fstride];
                scratch[4] = *Fout4 * _twiddles[4*u*fstride];

                scratch[7] = scratch[1] + scratch[4];
                scratch[10]= scratch[1] - scratch[4];
                scratch[8] = scratch[2] + scratch[3];
                scratch[9] = scratch[2] - scratch[3];

                *Fout0 += scratch[7];
                *Fout0 += scratch[8];

                scratch[5] = scratch[0] + cpx_type(
                        scratch[7].real()*ya.real() + scratch[8].real()*yb.real(),
                        scratch[7].imag()*ya.real() + scratch[8].imag()*yb.real()
                        );

                scratch[6] =  cpx_type(
                         scratch[10].imag()*ya.imag() + scratch[9].imag()*yb.imag(),
                        -scratch[10].real()*ya.imag() - scratch[9].real()*yb.imag()
                        );

                *Fout1 = scratch[5] - scratch[6];
                *Fout4 = scratch[5] + scratch[6];

                scratch[11] = scratch[0] +
                    cpx_type(
                            scratch[7].real()*yb.real() + scratch[8].real()*ya.real(),
                            scratch[7].imag()*yb.real() + scratch[8].imag()*ya.real()
                            );

                scratch[12] = cpx_type(
                        -scratch[10].imag()*yb.imag() + scratch[9].imag()*ya.imag(),
                         scratch[10].real()*yb.imag() - scratch[9].real()*ya.imag()
                        );

                *Fout2 = scratch[11] + scratch[12];
                *Fout3 = scratch[11] - scratch[12];

                ++Fout0;
                ++Fout1;
                ++Fout2;
                ++Fout3;
                ++Fout4;
            }
        }

        /* perform the butterfly for one stage of a mixed radix FFT */
        void kf_bfly_generic(
                cpx_type * Fout,
                const size_t fstride,
                std::size_t m,
                std::size_t p
                ) const
        {
            const cpx_type * twiddles = &_twiddles[0];
            cpx_type scratchbuf[p];

            for ( std::size_t u=0; u<m; ++u ) {
                std::size_t k = u;
                for ( std::size_t q1=0 ; q1<p ; ++q1 ) {
                    scratchbuf[q1] = Fout[ k  ];
                    k += m;
                }

                k=u;
                for ( std::size_t q1=0 ; q1<p ; ++q1 ) {
                    std::size_t twidx=0;
                    Fout[ k ] = scratchbuf[0];
                    for ( std::size_t q=1;q<p;++q ) {
                        twidx += fstride * k;
                        if (twidx>=_nfft)
                          twidx-=_nfft;
                        Fout[ k ] += scratchbuf[q] * twiddles[twidx];
                    }
                    k += m;
                }
            }
        }

        std::size_t _nfft;
        bool _inverse;
        std::vector<cpx_type> _twiddles;
        std::vector<std::size_t> _stageRadix;
        std::vector<std::size_t> _stageRemainder;
};
#endif
<|endoftext|>


<commit_before>/* This file is part of Strigi Desktop Search
 *
 * Copyright (C) 2007 Jos van den Oever <jos@vandenoever.info>
 *
 * 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 "strigiconfig.h"
#include "oleinputstream.h"
#include "oleendanalyzer.h"
#include "subinputstream.h"
#include "analysisresult.h"
#include "fieldtypes.h"
#include "textutils.h"
#include <sstream>
using namespace Strigi;
using namespace std;

void
OleEndAnalyzerFactory::registerFields(FieldRegister& reg) {
    static const char summaryKey[] = {0xE0,0x85,0x9F,0xF2,0xF9,0x4F,0x68,0x10,
        0xAB,0x91,0x08,0x00,0x2B,0x27,0xB3,0xD9};
    static const char docSummaryKey[]= {0x02,0xD5,0xCD,0xD5,0x9C,0x2E,0x1B,0x10,
        0x93,0x97,0x08,0x00,0x2B,0x2C,0xF9,0xAE};
    const RegisteredField* r;
    string key;
    map<int,const RegisteredField*>* m;

    // register the fields for the Summary Information Stream
    key.assign(summaryKey, 16);
    m = &fieldsMaps[key];
    r = reg.registerField("content.title", FieldRegister::stringType, 1, 0);
    if (r) (*m)[2] = r;
    r = reg.registerField("content.subject", FieldRegister::stringType, -1, 0);
    if (r) (*m)[3] = r;
    r = reg.registerField("content.author", FieldRegister::stringType, -1, 0);
    if (r) (*m)[4] = r;
    r = reg.registerField("content.keyword", FieldRegister::stringType, -1, 0);
    if (r) (*m)[5] = r;
    r = reg.registerField("content.comment", FieldRegister::stringType, -1, 0);
    if (r) (*m)[6] = r;

    // register the fields for the Document Summary Information Stream
    key.assign(docSummaryKey, 16);
    m = &fieldsMaps[key];
    r = reg.registerField("category", FieldRegister::stringType, 1, 0);
    if (r) (*m)[2] = r;
    r = reg.registerField("presentationtarget",FieldRegister::stringType, 1, 0);
    if (r) (*m)[3] = r;
    r = reg.registerField("manager", FieldRegister::stringType, 1, 0);
    if (r) (*m)[14] = r;
    r = reg.registerField("company", FieldRegister::stringType, 1, 0);
    if (r) (*m)[15] = r;
}
const map<int, const RegisteredField*>*
OleEndAnalyzerFactory::getFieldMap(const string& key) const {
    map<string, map<int,const RegisteredField*> >::const_iterator i
        = fieldsMaps.find(key);
    return (i == fieldsMaps.end()) ?0 :&i->second;
}
// parse with info from http://www.wotsit.org/getfile.asp?file=wword8&sc=230027800
bool
tryFIB(AnalysisResult& ar, InputStream* in) {
    const char* d;
    int32_t size = 34;
    int32_t nread = in->read(d, size, size);
    in->reset(0);
    if (nread != size
            || (unsigned char)d[0] != 0xec || (unsigned char)d[1] != 0xa5) {
        return false;
    }
    bool complex = d[10] & 4;
    if (complex) return false;
    int32_t fcMin = readLittleEndianInt32(d+24);
    int32_t fcMac = readLittleEndianInt32(d+28);
    // for some reason we need to add 512 here. No clue why.
    fcMin += 512;
    fcMac += 512;
    uint16_t csw = readLittleEndianUInt16(d+32);
    size += 2*csw + 2;
    nread = in->read(d, size, size);
    in->reset(0);
    if (nread != size) return false;
    csw = readLittleEndianUInt16(d+size-2);
    size += 4*csw + 2;
    nread = in->read(d, size, size);
    in->reset(0);
    if (nread != size) return false;
    csw = readLittleEndianUInt16(d+size-2);
    size += 8*csw;

    nread = in->read(d, fcMac, fcMac);
    in->reset(0);
    if (nread != fcMac) {
        return false;
    }
    const char *end = d+fcMac;
    d += fcMin;
    const char *p = d;
    string text;
    while (p < end) {
        switch (*p) {
        case 7: // cell/row end
        case 12: // Page break / Section mark
        case 13: // Paragraph end
        case 14: // Column end
        case 19: // Field start
            if (p > d) {
                text.append(d, p-d);
	        ar.addText(text.c_str(), text.size());
	    }
            text.assign("");
            d = p+1;
	    break;
        case 21: // Field end
            text.assign("");
            d = p+1;
	    break;
        case 30: // Non-breaken hyphen
            if (p > d) text.append(d, p-d);
            text.append("-");
            d = p+1;
        case 31: // Non-required hyphen
            if (p > d) text.append(d, p-d);
            d = p+1;
	    break;
        case 160: // Non-breaking space
            if (p > d) text.append(d, p-d);
            text.append(" ");
            d = p+1;
        default:;
        }
	++p;
    }
    return true;
}
bool
OleEndAnalyzer::checkHeader(const char* header, int32_t headersize) const {
    return OleInputStream::checkHeader(header, headersize);
}
bool
tryThumbsdbEntry(const string& name, AnalysisResult& ar, InputStream* in) {
    static const char magic[] = {0x0c, 0, 0, 0, 0x01, 0, 0, 0};
    const char* d;
    uint32_t nread = in->read(d, 12, 12);
    if (nread != 12 || memcmp(magic, d, 8)) {
        in->reset(0);
        return false;
    }
    SubInputStream thumb(in, in->size()-12);
    ar.indexChild(name, 0, &thumb);
    return true;
}
/**
 * Exctract images from a 'Pictures' field from a ppt file.
 * http://jakarta.apache.org/poi/apidocs/org/apache/poi/hslf/model/Picture.html
 **/
void
tryPictures(AnalysisResult& ar, InputStream* in) {
    const char* d;
    int32_t nread = in->read(d, 25, 25);
    ostringstream s;
    int pos = 1;
    while (nread == 25) {
        uint32_t size = readLittleEndianInt32(d+4)-17;
        SubInputStream sub(in, size);
        s << "Pictures/" << pos++;
        ar.indexChild(s.str(), 0, &sub);
        s.str("");
        nread = in->read(d, 25, 25);
    }
}
// format description: http://jakarta.apache.org/poi/hpsf/internals.html
bool
OleEndAnalyzer::tryPropertyStream(AnalysisResult& idx,
        InputStream* in) {
    static const char magic[] = {0xfe, 0xff, 0, 0};
    const char* d;
    uint32_t nread = in->read(d, 28, 28);
    in->reset(0);
    if (nread != 28 || memcmp(magic, d, 4)) {
        return false;
    }
    // read all the data
    nread = in->read(d, in->size(), in->size());
    if (nread != in->size()) {
        return false;
    }
    int32_t n = readLittleEndianUInt32(d+24);
    if (in->size() < 28+n*20) {
        return false;
    }
    for (int32_t i=0; i<n; ++i) {
        const char* key = d + 28 + i*20;
        int32_t offset = readLittleEndianInt32(key+16);
        if (offset >= in->size()) {
            return false;
        }
        handlePropertyStream(key, d+offset, d+in->size());
    }
    return true;
}
void
OleEndAnalyzer::handleProperty(const RegisteredField* field, const char* data) {
    int32_t datatype = readLittleEndianInt32(data);
    // currently we only support null-terminated strings
    if (datatype == 30) {
        int32_t len = readLittleEndianInt32(data+4);
        if (len > 0) {
            result->addValue(field, data+8, len-1);
        }
    }
}
void
OleEndAnalyzer::handlePropertyStream(const char* key, const char* data,
        const char* end) {
    // get the fieldtable
    string k(key, 16);
    const map<int, const RegisteredField*>* table = factory->getFieldMap(k);
    if (table == 0) {
        return;
    }

    int32_t len = readLittleEndianInt32(data);
    const char* p = data + 8;
    const char* n = data + readLittleEndianInt32(data+4)*4 + 8;
    if (len < 0 || (len > end-data) || n > end) {
        return;
    }
    map<int, const RegisteredField*>::const_iterator field;
    while (p < n) {
        int32_t id = readLittleEndianInt32(p);
        field = table->find(id);
        if (field != table->end()) {
            int32_t offset = readLittleEndianInt32(p+4);
            handleProperty(field->second, data+offset);
        }
        p += 8;
    }
}
char
OleEndAnalyzer::analyze(AnalysisResult& ar, InputStream* in) {
    if(!in)
        return -1;

    result = &ar;
    OleInputStream ole(in);
    InputStream *s = ole.nextEntry();
    if (ole.status()) {
        fprintf(stderr, "error: %s\n", ole.error());
	return -1;
    }
    while (s) {
        const string& name = ole.entryInfo().filename;
        if (name.size()) {
	    if (tryFIB(ar, s)) {
            } else if (tryThumbsdbEntry(name, ar, s)) {
            } else if (name[0] == 5) {
                // todo: handle property stream
                tryPropertyStream(ar, s);
            } else if (name == "Pictures") {
                tryPictures(ar, s);
            } else {
                ar.indexChild(name, ole.entryInfo().mtime,
                    s);
            }
        }
        s = ole.nextEntry();
    }
    if (ole.status() == Error) {
        m_error = ole.error();
        return -1;
    } else {
        m_error.resize(0);
    }
    return 0;
}
<commit_msg>small fixes to avoid extracting special characters<commit_after>/* This file is part of Strigi Desktop Search
 *
 * Copyright (C) 2007 Jos van den Oever <jos@vandenoever.info>
 *
 * 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 "strigiconfig.h"
#include "oleinputstream.h"
#include "oleendanalyzer.h"
#include "subinputstream.h"
#include "analysisresult.h"
#include "fieldtypes.h"
#include "textutils.h"
#include <sstream>
using namespace Strigi;
using namespace std;

void
OleEndAnalyzerFactory::registerFields(FieldRegister& reg) {
    static const char summaryKey[] = {0xE0,0x85,0x9F,0xF2,0xF9,0x4F,0x68,0x10,
        0xAB,0x91,0x08,0x00,0x2B,0x27,0xB3,0xD9};
    static const char docSummaryKey[]= {0x02,0xD5,0xCD,0xD5,0x9C,0x2E,0x1B,0x10,
        0x93,0x97,0x08,0x00,0x2B,0x2C,0xF9,0xAE};
    const RegisteredField* r;
    string key;
    map<int,const RegisteredField*>* m;

    // register the fields for the Summary Information Stream
    key.assign(summaryKey, 16);
    m = &fieldsMaps[key];
    r = reg.registerField("content.title", FieldRegister::stringType, 1, 0);
    if (r) (*m)[2] = r;
    r = reg.registerField("content.subject", FieldRegister::stringType, -1, 0);
    if (r) (*m)[3] = r;
    r = reg.registerField("content.author", FieldRegister::stringType, -1, 0);
    if (r) (*m)[4] = r;
    r = reg.registerField("content.keyword", FieldRegister::stringType, -1, 0);
    if (r) (*m)[5] = r;
    r = reg.registerField("content.comment", FieldRegister::stringType, -1, 0);
    if (r) (*m)[6] = r;

    // register the fields for the Document Summary Information Stream
    key.assign(docSummaryKey, 16);
    m = &fieldsMaps[key];
    r = reg.registerField("category", FieldRegister::stringType, 1, 0);
    if (r) (*m)[2] = r;
    r = reg.registerField("presentationtarget",FieldRegister::stringType, 1, 0);
    if (r) (*m)[3] = r;
    r = reg.registerField("manager", FieldRegister::stringType, 1, 0);
    if (r) (*m)[14] = r;
    r = reg.registerField("company", FieldRegister::stringType, 1, 0);
    if (r) (*m)[15] = r;
}
const map<int, const RegisteredField*>*
OleEndAnalyzerFactory::getFieldMap(const string& key) const {
    map<string, map<int,const RegisteredField*> >::const_iterator i
        = fieldsMaps.find(key);
    return (i == fieldsMaps.end()) ?0 :&i->second;
}
// parse with info from http://www.wotsit.org/getfile.asp?file=wword8&sc=230027800
bool
tryFIB(AnalysisResult& ar, InputStream* in) {
    const char* d;
    int32_t size = 34;
    int32_t nread = in->read(d, size, size);
    in->reset(0);
    if (nread != size
            || (unsigned char)d[0] != 0xec || (unsigned char)d[1] != 0xa5) {
        return false;
    }
    bool complex = d[10] & 4;
    if (complex) return false;
    int32_t fcMin = readLittleEndianInt32(d+24);
    int32_t fcMac = readLittleEndianInt32(d+28);
    // for some reason we need to add 512 here. No clue why.
    fcMin += 512;
    fcMac += 512;
    uint16_t csw = readLittleEndianUInt16(d+32);
    size += 2*csw + 2;
    nread = in->read(d, size, size);
    in->reset(0);
    if (nread != size) return false;
    csw = readLittleEndianUInt16(d+size-2);
    size += 4*csw + 2;
    nread = in->read(d, size, size);
    in->reset(0);
    if (nread != size) return false;
    csw = readLittleEndianUInt16(d+size-2);
    size += 8*csw;

    nread = in->read(d, fcMac, fcMac);
    in->reset(0);
    if (nread != fcMac) {
        return false;
    }
    const char *end = d+fcMac;
    d += fcMin;
    const char *p = d;
    string text;
    while (p < end) {
        switch (*p) {
        case 7: // cell/row end
        case 11: // ?
        case 12: // Page break / Section mark
        case 13: // Paragraph end
        case 14: // Column end
        case 19: // Field start
        case 20: // ?
            if (p > d) {
                text.append(d, p-d);
	        ar.addText(text.c_str(), text.size());
	    }
            text.assign("");
            d = p+1;
	    break;
        case 21: // Field end
            text.assign("");
            d = p+1;
	    break;
        case 30: // Non-breaken hyphen
            if (p > d) text.append(d, p-d);
            text.append("-");
            d = p+1;
        case 31: // Non-required hyphen
            if (p > d) text.append(d, p-d);
            d = p+1;
	    break;
        case 160: // Non-breaking space
            if (p > d) text.append(d, p-d);
            text.append(" ");
            d = p+1;
        default:;
        }
	++p;
    }
    return true;
}
bool
OleEndAnalyzer::checkHeader(const char* header, int32_t headersize) const {
    return OleInputStream::checkHeader(header, headersize);
}
bool
tryThumbsdbEntry(const string& name, AnalysisResult& ar, InputStream* in) {
    static const char magic[] = {0x0c, 0, 0, 0, 0x01, 0, 0, 0};
    const char* d;
    uint32_t nread = in->read(d, 12, 12);
    if (nread != 12 || memcmp(magic, d, 8)) {
        in->reset(0);
        return false;
    }
    SubInputStream thumb(in, in->size()-12);
    ar.indexChild(name, 0, &thumb);
    return true;
}
/**
 * Exctract images from a 'Pictures' field from a ppt file.
 * http://jakarta.apache.org/poi/apidocs/org/apache/poi/hslf/model/Picture.html
 **/
void
tryPictures(AnalysisResult& ar, InputStream* in) {
    const char* d;
    int32_t nread = in->read(d, 25, 25);
    ostringstream s;
    int pos = 1;
    while (nread == 25) {
        uint32_t size = readLittleEndianInt32(d+4)-17;
        SubInputStream sub(in, size);
        s << "Pictures/" << pos++;
        ar.indexChild(s.str(), 0, &sub);
        s.str("");
        nread = in->read(d, 25, 25);
    }
}
// format description: http://jakarta.apache.org/poi/hpsf/internals.html
bool
OleEndAnalyzer::tryPropertyStream(AnalysisResult& idx,
        InputStream* in) {
    static const char magic[] = {0xfe, 0xff, 0, 0};
    const char* d;
    uint32_t nread = in->read(d, 28, 28);
    in->reset(0);
    if (nread != 28 || memcmp(magic, d, 4)) {
        return false;
    }
    // read all the data
    nread = in->read(d, in->size(), in->size());
    if (nread != in->size()) {
        return false;
    }
    int32_t n = readLittleEndianUInt32(d+24);
    if (in->size() < 28+n*20) {
        return false;
    }
    for (int32_t i=0; i<n; ++i) {
        const char* key = d + 28 + i*20;
        int32_t offset = readLittleEndianInt32(key+16);
        if (offset >= in->size()) {
            return false;
        }
        handlePropertyStream(key, d+offset, d+in->size());
    }
    return true;
}
void
OleEndAnalyzer::handleProperty(const RegisteredField* field, const char* data) {
    int32_t datatype = readLittleEndianInt32(data);
    // currently we only support null-terminated strings
    if (datatype == 30) {
        int32_t len = readLittleEndianInt32(data+4);
        if (len > 0) {
            result->addValue(field, data+8, len-1);
        }
    }
}
void
OleEndAnalyzer::handlePropertyStream(const char* key, const char* data,
        const char* end) {
    // get the fieldtable
    string k(key, 16);
    const map<int, const RegisteredField*>* table = factory->getFieldMap(k);
    if (table == 0) {
        return;
    }

    int32_t len = readLittleEndianInt32(data);
    const char* p = data + 8;
    const char* n = data + readLittleEndianInt32(data+4)*4 + 8;
    if (len < 0 || (len > end-data) || n > end) {
        return;
    }
    map<int, const RegisteredField*>::const_iterator field;
    while (p < n) {
        int32_t id = readLittleEndianInt32(p);
        field = table->find(id);
        if (field != table->end()) {
            int32_t offset = readLittleEndianInt32(p+4);
            handleProperty(field->second, data+offset);
        }
        p += 8;
    }
}
char
OleEndAnalyzer::analyze(AnalysisResult& ar, InputStream* in) {
    if(!in)
        return -1;

    result = &ar;
    OleInputStream ole(in);
    InputStream *s = ole.nextEntry();
    if (ole.status()) {
        fprintf(stderr, "error: %s\n", ole.error());
	return -1;
    }
    while (s) {
        const string& name = ole.entryInfo().filename;
        if (name.size()) {
	    if (tryFIB(ar, s)) {
            } else if (tryThumbsdbEntry(name, ar, s)) {
            } else if (name[0] == 5) {
                // todo: handle property stream
                tryPropertyStream(ar, s);
            } else if (name == "Pictures") {
                tryPictures(ar, s);
            } else {
                ar.indexChild(name, ole.entryInfo().mtime,
                    s);
            }
        }
        s = ole.nextEntry();
    }
    if (ole.status() == Error) {
        m_error = ole.error();
        return -1;
    } else {
        m_error.resize(0);
    }
    return 0;
}
<|endoftext|>


<commit_before>/* This file is part of Strigi Desktop Search
 *
 * Copyright (C) 2001, 2002 Rolf Magnus <ramagnus@kde.org>
 * Copyright (C) 2006 Jos van den Oever <jos@vandenoever.info>
 * Copyright (C) 2007 Vincent Ricard <magic@magicninja.org>
 *
 * 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 "jstreamsconfig.h"
#include <time.h>

#include "pngendanalyzer.h"
#include "analysisresult.h"
#include "textendanalyzer.h"
#include "subinputstream.h"
#include "fieldtypes.h"
#include "gzipinputstream.h"
#include "textutils.h"
using namespace std;
using namespace Strigi;

const string PngEndAnalyzerFactory::widthFieldName("width");
const string PngEndAnalyzerFactory::heightFieldName("height");
const string PngEndAnalyzerFactory::colorDepthFieldName("colorDepth");
const string PngEndAnalyzerFactory::colorModeFieldName("colorMode");
const string PngEndAnalyzerFactory::compressionFieldName("compression");
const string PngEndAnalyzerFactory::interlaceModeFieldName("interlaceMode");
const string PngEndAnalyzerFactory::lastModificationTimeFieldName("lastModificationTime");
const string PngEndAnalyzerFactory::titleFieldName("title");
const string PngEndAnalyzerFactory::authorFieldName("author");
const string PngEndAnalyzerFactory::descriptionFieldName("description");
const string PngEndAnalyzerFactory::copyrightFieldName("copyright");
const string PngEndAnalyzerFactory::creationTimeFieldName("creationTime");
const string PngEndAnalyzerFactory::softwareFieldName("software");
const string PngEndAnalyzerFactory::disclaimerFieldName("disclaimer");
const string PngEndAnalyzerFactory::warningFieldName("warning");
const string PngEndAnalyzerFactory::sourceFieldName("source");
const string PngEndAnalyzerFactory::commentFieldName("comment");

// and for the colors
static const char* colors[] = {
  "Grayscale",
  "Unknown",
  "RGB",
  "Palette",
  "Grayscale/Alpha",
  "Unknown",
  "RGB/Alpha"
};

static const char* interlaceModes[] = {
  "None",
  "Adam7"
};

void
PngEndAnalyzerFactory::registerFields(FieldRegister& reg) {
    widthField = reg.registerField(widthFieldName,
        FieldRegister::integerType, 1, 0);
    heightField = reg.registerField(heightFieldName,
        FieldRegister::integerType, 1, 0);
    colorDepthField = reg.registerField(colorDepthFieldName,
        FieldRegister::integerType, 1, 0);
    colorModeField = reg.registerField(colorModeFieldName,
        FieldRegister::integerType, 1, 0);
    compressionField = reg.registerField(compressionFieldName,
        FieldRegister::integerType, 1, 0);
    interlaceModeField = reg.registerField(interlaceModeFieldName,
        FieldRegister::integerType, 1, 0);
    lastModificationTimeField = reg.registerField(lastModificationTimeFieldName,
        FieldRegister::integerType, 1, 0);
    titleField = reg.registerField(titleFieldName,
        FieldRegister::stringType, 1, 0);
    authorField = reg.registerField(authorFieldName,
        FieldRegister::stringType, 1, 0);
    descriptionField = reg.registerField(descriptionFieldName,
        FieldRegister::stringType, 1, 0);
    copyrightField = reg.registerField(copyrightFieldName,
        FieldRegister::stringType, 1, 0);
    creationTimeField = reg.registerField(creationTimeFieldName,
        FieldRegister::integerType, 1, 0);
    softwareField = reg.registerField(softwareFieldName,
        FieldRegister::stringType, 1, 0);
    disclaimerField = reg.registerField(disclaimerFieldName,
        FieldRegister::stringType, 1, 0);
    warningField = reg.registerField(warningFieldName,
        FieldRegister::stringType, 1, 0);
    sourceField = reg.registerField(sourceFieldName,
        FieldRegister::stringType, 1, 0);
    commentField = reg.registerField(commentFieldName,
        FieldRegister::stringType, 1, 0);
}

PngEndAnalyzer::PngEndAnalyzer(const PngEndAnalyzerFactory* f) :factory(f) {
    // XXX hack to workaround mktime
    // which takes care of the local time zone
    struct tm timeZone;
    timeZone.tm_sec = 0;
    timeZone.tm_min = 0;
    timeZone.tm_hour = 0;
    timeZone.tm_mday = 1;
    timeZone.tm_mon = 0;
    timeZone.tm_year = 70;
    timeZone.tm_isdst = 0;
    timeZoneOffset = mktime(&timeZone);
}
bool
PngEndAnalyzer::checkHeader(const char* header, int32_t headersize) const {
    static const unsigned char pngmagic[]
        = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
    return headersize >= 29 && memcmp(header, pngmagic, 8) == 0;
}
char
PngEndAnalyzer::analyze(AnalysisResult& as, InputStream* in) {
    const char* c;
    int32_t nread = in->read(c, 12, 12);
    if (nread != 12) {
        // file is too small to be a png
        return -1;
    }

    // read chunksize and include the size of the type and crc (4 + 4)
    uint32_t chunksize = readBigEndianUInt32(c+8) + 8;
    if (chunksize > 1048576) {
        fprintf(stderr,"chunk too big: %u\n",chunksize);
        return -1;
    }
    nread = in->read(c, chunksize, chunksize);
    // the IHDR chunk should be the first
    if (nread != (int32_t)chunksize || strncmp(c, "IHDR", 4)) {
        return -1;
    }

    // read the png dimensions
    uint32_t width = readBigEndianUInt32(c+4);
    uint32_t height = readBigEndianUInt32(c+8);
    as.addValue(factory->widthField, width);
    as.addValue(factory->heightField, height);

    uint16_t type = c[13];
    uint16_t bpp = c[12];

    // the bpp are only per channel, so we need to multiply the with
    // the channel count
    switch (type) {
        case 0: break;           // Grayscale
        case 2: bpp *= 3; break; // RGB
        case 3: break;           // palette
        case 4: bpp *= 2; break; // grayscale w. alpha
        case 6: bpp *= 4; break; // RGBA

        default: // we don't get any sensible value here
            bpp = 0;
    }

    as.addValue(factory->colorDepthField, (uint32_t)bpp);
    as.addValue(factory->colorModeField,
        (type < sizeof(colors)/sizeof(colors[0]))
                   ? colors[type] : "Unknown");

    as.addValue(factory->compressionField,
        (c[14] == 0) ? "Deflate" : "Unknown");

    as.addValue(factory->interlaceModeField,
        ((uint)c[16] < sizeof(interlaceModes)/sizeof(interlaceModes[0]))
                   ? interlaceModes[(int)c[16]] : "Unknown");

    // read the rest of the chunks
    // TODO: check if we want a fast or complete analysis
    // read the type
    nread = in->read(c, 8, 8);
    while (nread == 8 && strncmp("IEND", c+4, 4)) {
        // get the size of the data block
        chunksize = readBigEndianUInt32(c);

        // TODO read http://tools.ietf.org/html/rfc2083 to handle
        // predefined text values and map them to good semantic fields

        if (strncmp("tEXt", c+4, 4) == 0) {
            // TODO convert latin1 to utf8 and analyze the format properly
            SubInputStream sub(in, chunksize);
            analyzeText(as, &sub);
            sub.skip(chunksize);
        } else if (strncmp("zTXt", c+4, 4) == 0) {
            SubInputStream sub(in, chunksize);
            analyzeZText(as, &sub);
            sub.skip(chunksize);
        } else if (strncmp("iTXt", c+4, 4) == 0) {
            SubInputStream sub(in, chunksize);
            analyzeText(as, &sub);
            sub.skip(chunksize);
        } else if (strncmp("tIME", c+4, 4) == 0) {
            // the chunck size has to be 7
            if (chunksize != 7) {
                return -1;
            }
            nread = in->read(c, chunksize, chunksize);
            if (nread != (int32_t)chunksize) {
                return -1;
            }
            int32_t time = extractTime(c);
            if (time == -1) {
                return -1;
            }
            as.addValue(factory->lastModificationTimeField, (uint32_t)time);
        } else {
            nread = (int32_t)in->skip(chunksize);
            if (nread != (int32_t)chunksize) {
                fprintf(stderr, "could not skip chunk size %u\n", chunksize);
                return -1;
            }
        }
        in->skip(4); // skip crc
        nread = in->read(c, 8, 8);
    }
    if (nread != 8) {
        // invalid file or error
        fprintf(stderr, "bad end in %s\n", as.path().c_str());
        return -1;
    }

    return 0;
}
char
PngEndAnalyzer::analyzeText(Strigi::AnalysisResult& as,
        InputStream* in) {
    const char* c;
    int32_t nread = in->read(c, 80, 80);
    if (nread < 1) {
         return -1;
    }
    // find the \0
    int32_t nlen = 0;
    while (nlen < nread && c[nlen]) nlen++;
    if (nlen == nread) return -1;
    const string name(c, nlen); // do something with the name!
    in->reset(nlen+1);
    addMetaData(name, as, in);
    TextEndAnalyzer tea;
    return tea.analyze(as, in);
}
char
PngEndAnalyzer::analyzeZText(Strigi::AnalysisResult& as,
        InputStream* in) {
    const char* c;
    int32_t nread = in->read(c, 81, 81);
    if (nread < 1) {
         return -1;
    }
    // find the \0
    int32_t nlen = 0;
    while (nlen < nread && c[nlen]) nlen++;
    if (nlen == nread) return -1;
    const string name(c, nlen); // do something with the name!
    in->reset(nlen+2);
    GZipInputStream z(in, GZipInputStream::ZLIBFORMAT);
    addMetaData(name, as, &z);
    TextEndAnalyzer tea;
    return tea.analyze(as, &z);
}
int32_t
PngEndAnalyzer::extractTime(const char* chunck) {
    int16_t year = readBigEndianInt16(chunck);
    int8_t month = *(chunck+2);
    int8_t day = *(chunck+3);
    int8_t hour = *(chunck+4);
    int8_t minute = *(chunck+5);
    int8_t second = *(chunck+6);
    // check the data (the leap second is allowed)
    if (!(1 <= month && month <= 12
            && 1 <= day && day <= 31
            && 0 <= hour && hour <= 23
            && 0 <= minute && minute <= 59
            && 0 <= second && second <= 60)) {
        return -1;
    }
    // we want to store the date/time as a number of
    // seconds since Epoch (1970-01-01T00:00:00)
    struct tm dateTime;
    dateTime.tm_sec = second;
    dateTime.tm_min = minute;
    dateTime.tm_hour = hour;
    dateTime.tm_mday = day;
    dateTime.tm_mon = month-1;
    dateTime.tm_year = year-1900;
    dateTime.tm_isdst = 0;

    time_t sinceEpoch = mktime(&dateTime);
    if (sinceEpoch == (time_t)-1) {
        fprintf(stderr, "could not compute the date/time\n");
        return -1;
    }

    // FIXME the chunck is UTC but mktime use the local timezone :-(
    // so i have to add the offset of the local time zone
    // If someone has a better solution...
    return sinceEpoch + timeZoneOffset;
}
void
PngEndAnalyzer::addMetaData(const string& key,
        Strigi::AnalysisResult& as, InputStream* in) {
    const char* b;
    // try to store the whole buffer
    int32_t nread = in->read(b, 0, -1);
    if (0 < nread) {
        const string value(b, nread);
        if ("Title" == key) {
            as.addValue(factory->titleField, value);
        } else if ("Author" == key) {
            as.addValue(factory->authorField, value);
        } else if ("Description" == key) {
            as.addValue(factory->descriptionField, value);
        } else if ("Copyright" == key) {
            as.addValue(factory->copyrightField, value);
        } else if ("Creation Time" == key) {
            // TODO we need to parse the date time
            // "[...]the date format defined in section 5.2.14 of RFC 1123[...]"
        } else if ("Software" == key) {
            as.addValue(factory->softwareField, value);
        } else if ("Disclaimer" == key) {
            as.addValue(factory->disclaimerField, value);
        } else if ("Warning" == key) {
            as.addValue(factory->warningField, value);
        } else if ("Source" == key) {
            as.addValue(factory->sourceField, value);
        } else if ("Comment" == key) {
            as.addValue(factory->commentField, value);
        }
    }
}
<commit_msg>reset the InputStream to not break the full text index<commit_after>/* This file is part of Strigi Desktop Search
 *
 * Copyright (C) 2001, 2002 Rolf Magnus <ramagnus@kde.org>
 * Copyright (C) 2006 Jos van den Oever <jos@vandenoever.info>
 * Copyright (C) 2007 Vincent Ricard <magic@magicninja.org>
 *
 * 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 "jstreamsconfig.h"
#include <time.h>

#include "pngendanalyzer.h"
#include "analysisresult.h"
#include "textendanalyzer.h"
#include "subinputstream.h"
#include "fieldtypes.h"
#include "gzipinputstream.h"
#include "textutils.h"
using namespace std;
using namespace Strigi;

const string PngEndAnalyzerFactory::widthFieldName("width");
const string PngEndAnalyzerFactory::heightFieldName("height");
const string PngEndAnalyzerFactory::colorDepthFieldName("colorDepth");
const string PngEndAnalyzerFactory::colorModeFieldName("colorMode");
const string PngEndAnalyzerFactory::compressionFieldName("compression");
const string PngEndAnalyzerFactory::interlaceModeFieldName("interlaceMode");
const string PngEndAnalyzerFactory::lastModificationTimeFieldName("lastModificationTime");
const string PngEndAnalyzerFactory::titleFieldName("title");
const string PngEndAnalyzerFactory::authorFieldName("author");
const string PngEndAnalyzerFactory::descriptionFieldName("description");
const string PngEndAnalyzerFactory::copyrightFieldName("copyright");
const string PngEndAnalyzerFactory::creationTimeFieldName("creationTime");
const string PngEndAnalyzerFactory::softwareFieldName("software");
const string PngEndAnalyzerFactory::disclaimerFieldName("disclaimer");
const string PngEndAnalyzerFactory::warningFieldName("warning");
const string PngEndAnalyzerFactory::sourceFieldName("source");
const string PngEndAnalyzerFactory::commentFieldName("comment");

// and for the colors
static const char* colors[] = {
  "Grayscale",
  "Unknown",
  "RGB",
  "Palette",
  "Grayscale/Alpha",
  "Unknown",
  "RGB/Alpha"
};

static const char* interlaceModes[] = {
  "None",
  "Adam7"
};

void
PngEndAnalyzerFactory::registerFields(FieldRegister& reg) {
    widthField = reg.registerField(widthFieldName,
        FieldRegister::integerType, 1, 0);
    heightField = reg.registerField(heightFieldName,
        FieldRegister::integerType, 1, 0);
    colorDepthField = reg.registerField(colorDepthFieldName,
        FieldRegister::integerType, 1, 0);
    colorModeField = reg.registerField(colorModeFieldName,
        FieldRegister::integerType, 1, 0);
    compressionField = reg.registerField(compressionFieldName,
        FieldRegister::integerType, 1, 0);
    interlaceModeField = reg.registerField(interlaceModeFieldName,
        FieldRegister::integerType, 1, 0);
    lastModificationTimeField = reg.registerField(lastModificationTimeFieldName,
        FieldRegister::integerType, 1, 0);
    titleField = reg.registerField(titleFieldName,
        FieldRegister::stringType, 1, 0);
    authorField = reg.registerField(authorFieldName,
        FieldRegister::stringType, 1, 0);
    descriptionField = reg.registerField(descriptionFieldName,
        FieldRegister::stringType, 1, 0);
    copyrightField = reg.registerField(copyrightFieldName,
        FieldRegister::stringType, 1, 0);
    creationTimeField = reg.registerField(creationTimeFieldName,
        FieldRegister::integerType, 1, 0);
    softwareField = reg.registerField(softwareFieldName,
        FieldRegister::stringType, 1, 0);
    disclaimerField = reg.registerField(disclaimerFieldName,
        FieldRegister::stringType, 1, 0);
    warningField = reg.registerField(warningFieldName,
        FieldRegister::stringType, 1, 0);
    sourceField = reg.registerField(sourceFieldName,
        FieldRegister::stringType, 1, 0);
    commentField = reg.registerField(commentFieldName,
        FieldRegister::stringType, 1, 0);
}

PngEndAnalyzer::PngEndAnalyzer(const PngEndAnalyzerFactory* f) :factory(f) {
    // XXX hack to workaround mktime
    // which takes care of the local time zone
    struct tm timeZone;
    timeZone.tm_sec = 0;
    timeZone.tm_min = 0;
    timeZone.tm_hour = 0;
    timeZone.tm_mday = 1;
    timeZone.tm_mon = 0;
    timeZone.tm_year = 70;
    timeZone.tm_isdst = 0;
    timeZoneOffset = mktime(&timeZone);
}
bool
PngEndAnalyzer::checkHeader(const char* header, int32_t headersize) const {
    static const unsigned char pngmagic[]
        = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
    return headersize >= 29 && memcmp(header, pngmagic, 8) == 0;
}
char
PngEndAnalyzer::analyze(AnalysisResult& as, InputStream* in) {
    const char* c;
    int32_t nread = in->read(c, 12, 12);
    if (nread != 12) {
        // file is too small to be a png
        return -1;
    }

    // read chunksize and include the size of the type and crc (4 + 4)
    uint32_t chunksize = readBigEndianUInt32(c+8) + 8;
    if (chunksize > 1048576) {
        fprintf(stderr,"chunk too big: %u\n",chunksize);
        return -1;
    }
    nread = in->read(c, chunksize, chunksize);
    // the IHDR chunk should be the first
    if (nread != (int32_t)chunksize || strncmp(c, "IHDR", 4)) {
        return -1;
    }

    // read the png dimensions
    uint32_t width = readBigEndianUInt32(c+4);
    uint32_t height = readBigEndianUInt32(c+8);
    as.addValue(factory->widthField, width);
    as.addValue(factory->heightField, height);

    uint16_t type = c[13];
    uint16_t bpp = c[12];

    // the bpp are only per channel, so we need to multiply the with
    // the channel count
    switch (type) {
        case 0: break;           // Grayscale
        case 2: bpp *= 3; break; // RGB
        case 3: break;           // palette
        case 4: bpp *= 2; break; // grayscale w. alpha
        case 6: bpp *= 4; break; // RGBA

        default: // we don't get any sensible value here
            bpp = 0;
    }

    as.addValue(factory->colorDepthField, (uint32_t)bpp);
    as.addValue(factory->colorModeField,
        (type < sizeof(colors)/sizeof(colors[0]))
                   ? colors[type] : "Unknown");

    as.addValue(factory->compressionField,
        (c[14] == 0) ? "Deflate" : "Unknown");

    as.addValue(factory->interlaceModeField,
        ((uint)c[16] < sizeof(interlaceModes)/sizeof(interlaceModes[0]))
                   ? interlaceModes[(int)c[16]] : "Unknown");

    // read the rest of the chunks
    // TODO: check if we want a fast or complete analysis
    // read the type
    nread = in->read(c, 8, 8);
    while (nread == 8 && strncmp("IEND", c+4, 4)) {
        // get the size of the data block
        chunksize = readBigEndianUInt32(c);

        // TODO read http://tools.ietf.org/html/rfc2083 to handle
        // predefined text values and map them to good semantic fields

        if (strncmp("tEXt", c+4, 4) == 0) {
            // TODO convert latin1 to utf8 and analyze the format properly
            SubInputStream sub(in, chunksize);
            analyzeText(as, &sub);
            sub.skip(chunksize);
        } else if (strncmp("zTXt", c+4, 4) == 0) {
            SubInputStream sub(in, chunksize);
            analyzeZText(as, &sub);
            sub.skip(chunksize);
        } else if (strncmp("iTXt", c+4, 4) == 0) {
            SubInputStream sub(in, chunksize);
            analyzeText(as, &sub);
            sub.skip(chunksize);
        } else if (strncmp("tIME", c+4, 4) == 0) {
            // the chunck size has to be 7
            if (chunksize != 7) {
                return -1;
            }
            nread = in->read(c, chunksize, chunksize);
            if (nread != (int32_t)chunksize) {
                return -1;
            }
            int32_t time = extractTime(c);
            if (time == -1) {
                return -1;
            }
            as.addValue(factory->lastModificationTimeField, (uint32_t)time);
        } else {
            nread = (int32_t)in->skip(chunksize);
            if (nread != (int32_t)chunksize) {
                fprintf(stderr, "could not skip chunk size %u\n", chunksize);
                return -1;
            }
        }
        in->skip(4); // skip crc
        nread = in->read(c, 8, 8);
    }
    if (nread != 8) {
        // invalid file or error
        fprintf(stderr, "bad end in %s\n", as.path().c_str());
        return -1;
    }

    return 0;
}
char
PngEndAnalyzer::analyzeText(Strigi::AnalysisResult& as,
        InputStream* in) {
    const char* c;
    int32_t nread = in->read(c, 80, 80);
    if (nread < 1) {
         return -1;
    }
    // find the \0
    int32_t nlen = 0;
    while (nlen < nread && c[nlen]) nlen++;
    if (nlen == nread) return -1;
    const string name(c, nlen); // do something with the name!
    in->reset(nlen+1);
    addMetaData(name, as, in);
    TextEndAnalyzer tea;
    return tea.analyze(as, in);
}
char
PngEndAnalyzer::analyzeZText(Strigi::AnalysisResult& as,
        InputStream* in) {
    const char* c;
    int32_t nread = in->read(c, 81, 81);
    if (nread < 1) {
         return -1;
    }
    // find the \0
    int32_t nlen = 0;
    while (nlen < nread && c[nlen]) nlen++;
    if (nlen == nread) return -1;
    const string name(c, nlen); // do something with the name!
    in->reset(nlen+2);
    GZipInputStream z(in, GZipInputStream::ZLIBFORMAT);
    addMetaData(name, as, &z);
    TextEndAnalyzer tea;
    return tea.analyze(as, &z);
}
int32_t
PngEndAnalyzer::extractTime(const char* chunck) {
    int16_t year = readBigEndianInt16(chunck);
    int8_t month = *(chunck+2);
    int8_t day = *(chunck+3);
    int8_t hour = *(chunck+4);
    int8_t minute = *(chunck+5);
    int8_t second = *(chunck+6);
    // check the data (the leap second is allowed)
    if (!(1 <= month && month <= 12
            && 1 <= day && day <= 31
            && 0 <= hour && hour <= 23
            && 0 <= minute && minute <= 59
            && 0 <= second && second <= 60)) {
        return -1;
    }
    // we want to store the date/time as a number of
    // seconds since Epoch (1970-01-01T00:00:00)
    struct tm dateTime;
    dateTime.tm_sec = second;
    dateTime.tm_min = minute;
    dateTime.tm_hour = hour;
    dateTime.tm_mday = day;
    dateTime.tm_mon = month-1;
    dateTime.tm_year = year-1900;
    dateTime.tm_isdst = 0;

    time_t sinceEpoch = mktime(&dateTime);
    if (sinceEpoch == (time_t)-1) {
        fprintf(stderr, "could not compute the date/time\n");
        return -1;
    }

    // FIXME the chunck is UTC but mktime use the local timezone :-(
    // so i have to add the offset of the local time zone
    // If someone has a better solution...
    return sinceEpoch + timeZoneOffset;
}
void
PngEndAnalyzer::addMetaData(const string& key,
        Strigi::AnalysisResult& as, InputStream* in) {
    const char* b;
    int64_t pos = in->position();
    // try to store the whole buffer
    int32_t nread = in->read(b, 0, -1);
    if (0 < nread) {
        const string value(b, nread);
        if ("Title" == key) {
            as.addValue(factory->titleField, value);
        } else if ("Author" == key) {
            as.addValue(factory->authorField, value);
        } else if ("Description" == key) {
            as.addValue(factory->descriptionField, value);
        } else if ("Copyright" == key) {
            as.addValue(factory->copyrightField, value);
        } else if ("Creation Time" == key) {
            // TODO we need to parse the date time
            // "[...]the date format defined in section 5.2.14 of RFC 1123[...]"
        } else if ("Software" == key) {
            as.addValue(factory->softwareField, value);
        } else if ("Disclaimer" == key) {
            as.addValue(factory->disclaimerField, value);
        } else if ("Warning" == key) {
            as.addValue(factory->warningField, value);
        } else if ("Source" == key) {
            as.addValue(factory->sourceField, value);
        } else if ("Comment" == key) {
            as.addValue(factory->commentField, value);
        }
        in->reset(pos);
    }
}
<|endoftext|>


<commit_before>/*
    Persons Model Contact Item
    Represents person's contact item in the model
    Copyright (C) 2012  Martin Klapetek <martin.klapetek@gmail.com>
    Copyright (C) 2012  Aleix Pol Gonzalez <aleixpol@blue-systems.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "contact-item.h"

#include <QAction>

#include <KIcon>
#include <KDebug>

#include <Nepomuk2/Resource>
#include <Nepomuk2/Variant>
#include <Nepomuk2/ResourceManager>

#include <Nepomuk2/Vocabulary/NCO>
#include <Nepomuk2/Vocabulary/NIE>
#include <Soprano/Vocabulary/NAO>
#include <Soprano/Model>
#include <Soprano/QueryResultIterator>

class ContactItemPrivate {
public:
    QUrl uri;
    QHash<int, QVariant> data;

    ///@returns a list of the roles that we'll expect to be a QVariantList
    static QSet<int> listRoles() {
        static QSet<int> s_listRoles;
        if(s_listRoles.isEmpty()) {
            //TODO: document this in persons-model.h
            s_listRoles.insert(PersonsModel::PhotoRole);
            s_listRoles.insert(PersonsModel::EmailRole);
            s_listRoles.insert(PersonsModel::PhoneRole);
        }
        return s_listRoles;
    }
};

ContactItem::ContactItem(const QUrl &uri)
    : d_ptr(new ContactItemPrivate)
{
//     setData(uri, PersonsModel::UriRole);
    d_ptr->uri = uri;
}

ContactItem::~ContactItem()
{
    delete d_ptr;
}

void ContactItem::setContactData(int role, const QVariant &v)
{
    Q_D(ContactItem);
    QHash< int, QVariant >::iterator it = d->data.find(role);
    QVariant value = d->listRoles().contains(role) ? (QVariantList() << v) : v;
    if (it == d->data.end()) {
        d->data.insert(role, value);
        emitDataChanged();
    } else if (*it != value) {
        Q_ASSERT(value.type() == it->type());
        *it = value;
        emitDataChanged();
    }
}

void ContactItem::addContactData(int role, const QVariant &value)
{
    Q_D(ContactItem);
    QHash<int, QVariant>::iterator it = d->data.find(role);
    if (!d->listRoles().contains(role)) {
        setContactData(role, value);
    } else {
        if (it == d->data.end()) {
            d->data.insert(role, QVariantList() << value);
            emitDataChanged();
        } else if (*it != value) {
            Q_ASSERT(it->type() == QVariant::List);
            QVariantList current = it->toList();
            Q_ASSERT(current.isEmpty() || current.first().type()==value.type());
            current.append(value);
            *it = current;
            emitDataChanged();
        }
    }
}

void ContactItem::removeData(int role)
{
    Q_D(ContactItem);
    if (d->data.remove(role)) {
        emitDataChanged();
    }
}

QUrl ContactItem::uri() const
{
    Q_D(const ContactItem);
    return d->uri;
}

QVariant ContactItem::data(int role) const
{
    Q_D(const ContactItem);

    QHash<int, QVariant>::const_iterator it = d->data.constFind(role);
    if (it!=d->data.constEnd()) {
        return *it;
    }

    switch(role) {
        case Qt::DisplayRole:
            if (!data(PersonsModel::NickRole).toString().isEmpty()) {
                return data(PersonsModel::NickRole);
            }
            if (!data(PersonsModel::LabelRole).toString().isEmpty()) {
                return data(PersonsModel::LabelRole);
            }
            if (!data(PersonsModel::IMRole).toString().isEmpty()) {
                return data(PersonsModel::IMRole);
            }
            if (!data(PersonsModel::EmailRole).toString().isEmpty()) {
                return data(PersonsModel::EmailRole);
            }
            if (!data(PersonsModel::PhoneRole).toString().isEmpty()) {
                return data(PersonsModel::PhoneRole);
            }

            return QString("Unknown person"); //FIXME: temporary
        case PersonsModel::UriRole: return d->uri; break;
        case PersonsModel::StatusRole: return QLatin1String("unknown"); //return unknown presence, for real presence use PersonsPresenceModel
        case PersonsModel::ContactsCountRole: return 1;
        case PersonsModel::ResourceTypeRole:
            return PersonsModel::Contact;
        case Qt::DecorationRole: {
            QVariantList photos = d->data.value(PersonsModel::PhotoRole).toList();
            return photos.isEmpty() ? KIcon("im-user") : KIcon(photos.first().toUrl().toLocalFile());
        }
    }
    return QStandardItem::data(role);
}

void ContactItem::loadData()
{
    Q_D(ContactItem);

    QHash<QString, PersonsModel::Role> bindingRoleMap;
    bindingRoleMap.insert("nco_imNickname", PersonsModel::NickRole);
    bindingRoleMap.insert("nao_prefLabel", PersonsModel::LabelRole);
    bindingRoleMap.insert("nco_imID", PersonsModel::IMRole);
    bindingRoleMap.insert("nco_imImAcountType", PersonsModel::IMAccountTypeRole);
    bindingRoleMap.insert("nco_hasIMAccount", PersonsModel::IMAccountUriRole);
    bindingRoleMap.insert("nco_contactGroupName", PersonsModel::ContactGroupsRole);
    bindingRoleMap.insert("nie_url", PersonsModel::PhotoRole);
    bindingRoleMap.insert("nco_emailAddress", PersonsModel::EmailRole);

    QString query = QString::fromUtf8(
            "select DISTINCT ?nco_hasIMAccount "
            "?nco_imNickname ?nco_imID ?nco_imAccountType ?nco_hasEmailAddress "
            "?nie_url ?nao_prefLabel ?nco_contactGroupName "

                "WHERE { "
                    "OPTIONAL { %1  nao:prefLabel  ?nao_prefLabel. }"

//                    "OPTIONAL { "
                        "%1                                    nco:hasIMAccount            ?nco_hasIMAccount. "
                        "OPTIONAL { ?nco_hasIMAccount          nco:imNickname              ?nco_imNickname. } "
                        "OPTIONAL { ?nco_hasIMAccount          nco:imID                    ?nco_imID. } "
                        "OPTIONAL { ?nco_hasIMAccount          nco:imAccountType           ?nco_imAccountType. } "
//                    " } "
                    "OPTIONAL { "
                        "%1 nco:belongsToGroup ?nco_belongsToGroup . "
                        "?nco_belongsToGroup nco:contactGroupName ?nco_contactGroupName . "
                    " }"
                    "OPTIONAL {"
                        "%1                         nco:photo                   ?phRes. "
                        "?phRes                     nie:url                     ?nie_url. "
                    " } "
                    "OPTIONAL { "
                        "%1                         nco:hasEmailAddress         ?nco_hasEmailAddress. "
                        "?nco_hasEmailAddress       nco:emailAddress            ?nco_emailAddress. "
                    " } "
                "}")
            .arg(Soprano::Node::resourceToN3(d->uri));

    Soprano::Model *model = Nepomuk2::ResourceManager::instance()->mainModel();
    Soprano::QueryResultIterator it = model->executeQuery( query, Soprano::Query::QueryLanguageSparql );
    // FIXME: The result might come multiple times
    d->data.clear();
    if( it.next() ) {

        //iterate over the results and add the wanted properties into the contact
        foreach (const QString& bName, it.bindingNames()) {
            if (!bindingRoleMap.contains(bName))
                continue;

            PersonsModel::Role role = bindingRoleMap.value(bName);
            QString value = it.binding(bName).toString();
            if (!value.isEmpty()) {
                addContactData(role, value);
            }
        }
        emitDataChanged();
    }
}
<commit_msg>Add missing NameRole querying into Qt::DisplayRole data<commit_after>/*
    Persons Model Contact Item
    Represents person's contact item in the model
    Copyright (C) 2012  Martin Klapetek <martin.klapetek@gmail.com>
    Copyright (C) 2012  Aleix Pol Gonzalez <aleixpol@blue-systems.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "contact-item.h"

#include <QAction>

#include <KIcon>
#include <KDebug>

#include <Nepomuk2/Resource>
#include <Nepomuk2/Variant>
#include <Nepomuk2/ResourceManager>

#include <Nepomuk2/Vocabulary/NCO>
#include <Nepomuk2/Vocabulary/NIE>
#include <Soprano/Vocabulary/NAO>
#include <Soprano/Model>
#include <Soprano/QueryResultIterator>

class ContactItemPrivate {
public:
    QUrl uri;
    QHash<int, QVariant> data;

    ///@returns a list of the roles that we'll expect to be a QVariantList
    static QSet<int> listRoles() {
        static QSet<int> s_listRoles;
        if(s_listRoles.isEmpty()) {
            //TODO: document this in persons-model.h
            s_listRoles.insert(PersonsModel::PhotoRole);
            s_listRoles.insert(PersonsModel::EmailRole);
            s_listRoles.insert(PersonsModel::PhoneRole);
        }
        return s_listRoles;
    }
};

ContactItem::ContactItem(const QUrl &uri)
    : d_ptr(new ContactItemPrivate)
{
//     setData(uri, PersonsModel::UriRole);
    d_ptr->uri = uri;
}

ContactItem::~ContactItem()
{
    delete d_ptr;
}

void ContactItem::setContactData(int role, const QVariant &v)
{
    Q_D(ContactItem);
    QHash< int, QVariant >::iterator it = d->data.find(role);
    QVariant value = d->listRoles().contains(role) ? (QVariantList() << v) : v;
    if (it == d->data.end()) {
        d->data.insert(role, value);
        emitDataChanged();
    } else if (*it != value) {
        Q_ASSERT(value.type() == it->type());
        *it = value;
        emitDataChanged();
    }
}

void ContactItem::addContactData(int role, const QVariant &value)
{
    Q_D(ContactItem);
    QHash<int, QVariant>::iterator it = d->data.find(role);
    if (!d->listRoles().contains(role)) {
        setContactData(role, value);
    } else {
        if (it == d->data.end()) {
            d->data.insert(role, QVariantList() << value);
            emitDataChanged();
        } else if (*it != value) {
            Q_ASSERT(it->type() == QVariant::List);
            QVariantList current = it->toList();
            Q_ASSERT(current.isEmpty() || current.first().type()==value.type());
            current.append(value);
            *it = current;
            emitDataChanged();
        }
    }
}

void ContactItem::removeData(int role)
{
    Q_D(ContactItem);
    if (d->data.remove(role)) {
        emitDataChanged();
    }
}

QUrl ContactItem::uri() const
{
    Q_D(const ContactItem);
    return d->uri;
}

QVariant ContactItem::data(int role) const
{
    Q_D(const ContactItem);

    QHash<int, QVariant>::const_iterator it = d->data.constFind(role);
    if (it!=d->data.constEnd()) {
        return *it;
    }

    switch(role) {
        case Qt::DisplayRole:
            if (!data(PersonsModel::NickRole).toString().isEmpty()) {
                return data(PersonsModel::NickRole);
            }
            if (!data(PersonsModel::LabelRole).toString().isEmpty()) {
                return data(PersonsModel::LabelRole);
            }
            if (!data(PersonsModel::NameRole).toString().isEmpty()) {
                return data(PersonsModel::NameRole);
            }
            if (!data(PersonsModel::IMRole).toString().isEmpty()) {
                return data(PersonsModel::IMRole);
            }
            if (!data(PersonsModel::EmailRole).toString().isEmpty()) {
                return data(PersonsModel::EmailRole);
            }
            if (!data(PersonsModel::PhoneRole).toString().isEmpty()) {
                return data(PersonsModel::PhoneRole);
            }

            return QString("Unknown person"); //FIXME: temporary
        case PersonsModel::UriRole: return d->uri; break;
        case PersonsModel::StatusRole: return QLatin1String("unknown"); //return unknown presence, for real presence use PersonsPresenceModel
        case PersonsModel::ContactsCountRole: return 1;
        case PersonsModel::ResourceTypeRole:
            return PersonsModel::Contact;
        case Qt::DecorationRole: {
            QVariantList photos = d->data.value(PersonsModel::PhotoRole).toList();
            return photos.isEmpty() ? KIcon("im-user") : KIcon(photos.first().toUrl().toLocalFile());
        }
    }
    return QStandardItem::data(role);
}

void ContactItem::loadData()
{
    Q_D(ContactItem);

    QHash<QString, PersonsModel::Role> bindingRoleMap;
    bindingRoleMap.insert("nco_imNickname", PersonsModel::NickRole);
    bindingRoleMap.insert("nao_prefLabel", PersonsModel::LabelRole);
    bindingRoleMap.insert("nco_imID", PersonsModel::IMRole);
    bindingRoleMap.insert("nco_imImAcountType", PersonsModel::IMAccountTypeRole);
    bindingRoleMap.insert("nco_hasIMAccount", PersonsModel::IMAccountUriRole);
    bindingRoleMap.insert("nco_contactGroupName", PersonsModel::ContactGroupsRole);
    bindingRoleMap.insert("nie_url", PersonsModel::PhotoRole);
    bindingRoleMap.insert("nco_emailAddress", PersonsModel::EmailRole);

    QString query = QString::fromUtf8(
            "select DISTINCT ?nco_hasIMAccount "
            "?nco_imNickname ?nco_imID ?nco_imAccountType ?nco_hasEmailAddress "
            "?nie_url ?nao_prefLabel ?nco_contactGroupName "

                "WHERE { "
                    "OPTIONAL { %1  nao:prefLabel  ?nao_prefLabel. }"

//                    "OPTIONAL { "
                        "%1                                    nco:hasIMAccount            ?nco_hasIMAccount. "
                        "OPTIONAL { ?nco_hasIMAccount          nco:imNickname              ?nco_imNickname. } "
                        "OPTIONAL { ?nco_hasIMAccount          nco:imID                    ?nco_imID. } "
                        "OPTIONAL { ?nco_hasIMAccount          nco:imAccountType           ?nco_imAccountType. } "
//                    " } "
                    "OPTIONAL { "
                        "%1 nco:belongsToGroup ?nco_belongsToGroup . "
                        "?nco_belongsToGroup nco:contactGroupName ?nco_contactGroupName . "
                    " }"
                    "OPTIONAL {"
                        "%1                         nco:photo                   ?phRes. "
                        "?phRes                     nie:url                     ?nie_url. "
                    " } "
                    "OPTIONAL { "
                        "%1                         nco:hasEmailAddress         ?nco_hasEmailAddress. "
                        "?nco_hasEmailAddress       nco:emailAddress            ?nco_emailAddress. "
                    " } "
                "}")
            .arg(Soprano::Node::resourceToN3(d->uri));

    Soprano::Model *model = Nepomuk2::ResourceManager::instance()->mainModel();
    Soprano::QueryResultIterator it = model->executeQuery( query, Soprano::Query::QueryLanguageSparql );
    // FIXME: The result might come multiple times
    d->data.clear();
    if( it.next() ) {

        //iterate over the results and add the wanted properties into the contact
        foreach (const QString& bName, it.bindingNames()) {
            if (!bindingRoleMap.contains(bName))
                continue;

            PersonsModel::Role role = bindingRoleMap.value(bName);
            QString value = it.binding(bName).toString();
            if (!value.isEmpty()) {
                addContactData(role, value);
            }
        }
        emitDataChanged();
    }
}
<|endoftext|>