<commit_before>#ifndef ALPINO_CORPUSREADER_HH
#define ALPINO_CORPUSREADER_HH

#include <cstddef>
#include <list>
#include <queue>
#include <string>

#include <AlpinoCorpus/DLLDefines.hh>
#include <AlpinoCorpus/IterImpl.hh>
#include <AlpinoCorpus/util/NonCopyable.hh>

namespace alpinocorpus {

/**
 * Abstract base class for corpus readers.
 *
 * A corpus is conceptually a mapping of names to XML documents.
 * Both are represented as strings.
 */
class ALPINO_CORPUS_EXPORT CorpusReader : private util::NonCopyable
{
  public:
    /** Forward iterator over entry names */
    class ALPINO_CORPUS_EXPORT EntryIterator
    : public std::iterator<std::input_iterator_tag, std::string, ptrdiff_t, std::string *, std::string>
    {
      public:
        EntryIterator();
        EntryIterator(IterImpl *p);
        EntryIterator(EntryIterator const &other);
        virtual ~EntryIterator();
        EntryIterator &operator=(EntryIterator const &other);
        EntryIterator &operator++();
        EntryIterator operator++(int);
        bool operator==(EntryIterator const &other) const;
        bool operator!=(EntryIterator const &other) const;
        value_type operator*() const;
        //value_type const *operator->() const { return impl->current(); }

        /**
         * Get contents of entry pointed to by iterator.
         * This will be a null string for an ordinary iterator,
         * and the matching part for a query iterator.
         */
        std::string contents(CorpusReader const &rdr) const;

        /**
         * Interrupt an iterator that is blocking.
         */
        void interrupt();

      private:
        void copy(EntryIterator const &other);

        IterImpl *d_impl;
    };

    struct MarkerQuery {
        MarkerQuery(std::string const &newQuery, std::string const &newAttr,
            std::string const &newValue) :
            query(newQuery), attr(newAttr), value(newValue) {}
        std::string query;
        std::string attr;
        std::string value;

        bool operator==(MarkerQuery const &other) const;
    };

    virtual ~CorpusReader() {}

    /** Return canonical name of corpus */
    std::string name() const;

    /** Iterator to begin of entry names */
    EntryIterator begin() const;

    /**
     * Iterator to begin of entry names, contents are transformed with
     * the given stylesheet.
     */
    EntryIterator beginWithStylesheet(std::string const &stylesheet,
      std::list<MarkerQuery> const &markerQueries = std::list<MarkerQuery>()) const;

    /** Iterator to end of entry names */
    EntryIterator end() const;

    enum QueryDialect { XPATH, XQUERY };

    /** Is a query valid? */
    bool isValidQuery(QueryDialect d, bool variables, std::string const &q) const;

    /** Execute query. The end of the range is given by end(). */
    EntryIterator query(QueryDialect d, std::string const &q) const;

    /**
     * Execute a query, applying the given stylesheet to each entry. The
     * end of the range is given by end().
     */
    EntryIterator queryWithStylesheet(QueryDialect d, std::string const &q,
      std::string const &stylesheet,
      std::list<MarkerQuery> const &markerQueries) const;

    /**
     * Return content of a single treebank entry. Mark elements if a marker
     * queries were provided.
     */
    std::string read(std::string const &entry,
      std::list<MarkerQuery> const &queries = std::list<MarkerQuery>()) const;

    /** The number of entries in the corpus. */
    size_t size() const;

  private:
    virtual EntryIterator getBegin() const = 0;
    virtual EntryIterator getEnd() const = 0;
    virtual std::string getName() const = 0;
    virtual size_t getSize() const = 0;
    virtual std::string readEntry(std::string const &entry) const = 0;
    virtual std::string readEntryMarkQueries(std::string const &entry,
        std::list<MarkerQuery> const &queries) const;
    virtual EntryIterator runXPath(std::string const &) const;
    virtual EntryIterator runXQuery(std::string const &) const;
    virtual EntryIterator runQueryWithStylesheet(QueryDialect d,
      std::string const &q, std::string const &stylesheet,
      std::list<MarkerQuery> const &markerQueries) const;
    virtual bool validQuery(QueryDialect d, bool variables, std::string const &q) const;
};

}

#endif  // ALPINO_CORPUSREADER_HH
<commit_msg>Alter documentation for #18.<commit_after>#ifndef ALPINO_CORPUSREADER_HH
#define ALPINO_CORPUSREADER_HH

#include <cstddef>
#include <list>
#include <queue>
#include <string>

#include <AlpinoCorpus/DLLDefines.hh>
#include <AlpinoCorpus/IterImpl.hh>
#include <AlpinoCorpus/util/NonCopyable.hh>

namespace alpinocorpus {

/**
 * Abstract base class for corpus readers.
 *
 * A corpus is conceptually a mapping of names to XML documents.
 * Both are represented as strings.
 */
class ALPINO_CORPUS_EXPORT CorpusReader : private util::NonCopyable
{
  public:
    /** Forward iterator over entry names */
    class ALPINO_CORPUS_EXPORT EntryIterator
    : public std::iterator<std::input_iterator_tag, std::string, ptrdiff_t, std::string *, std::string>
    {
      public:
        EntryIterator();
        EntryIterator(IterImpl *p);
        EntryIterator(EntryIterator const &other);
        virtual ~EntryIterator();
        EntryIterator &operator=(EntryIterator const &other);
        EntryIterator &operator++();
        EntryIterator operator++(int);
        bool operator==(EntryIterator const &other) const;
        bool operator!=(EntryIterator const &other) const;
        value_type operator*() const;
        //value_type const *operator->() const { return impl->current(); }

        /**
         * Get contents of entry pointed to by iterator.
         * This will be a null string for an ordinary iterator,
         * and the string value for a query iterator. If the query
         * does not evaluate to a string, the node value will be
         * returned (which may be empty).
         */
        std::string contents(CorpusReader const &rdr) const;

        /**
         * Interrupt an iterator that is blocking.
         */
        void interrupt();

      private:
        void copy(EntryIterator const &other);

        IterImpl *d_impl;
    };

    struct MarkerQuery {
        MarkerQuery(std::string const &newQuery, std::string const &newAttr,
            std::string const &newValue) :
            query(newQuery), attr(newAttr), value(newValue) {}
        std::string query;
        std::string attr;
        std::string value;

        bool operator==(MarkerQuery const &other) const;
    };

    virtual ~CorpusReader() {}

    /** Return canonical name of corpus */
    std::string name() const;

    /** Iterator to begin of entry names */
    EntryIterator begin() const;

    /**
     * Iterator to begin of entry names, contents are transformed with
     * the given stylesheet.
     */
    EntryIterator beginWithStylesheet(std::string const &stylesheet,
      std::list<MarkerQuery> const &markerQueries = std::list<MarkerQuery>()) const;

    /** Iterator to end of entry names */
    EntryIterator end() const;

    enum QueryDialect { XPATH, XQUERY };

    /** Is a query valid? */
    bool isValidQuery(QueryDialect d, bool variables, std::string const &q) const;

    /** Execute query. The end of the range is given by end(). */
    EntryIterator query(QueryDialect d, std::string const &q) const;

    /**
     * Execute a query, applying the given stylesheet to each entry. The
     * end of the range is given by end().
     */
    EntryIterator queryWithStylesheet(QueryDialect d, std::string const &q,
      std::string const &stylesheet,
      std::list<MarkerQuery> const &markerQueries) const;

    /**
     * Return content of a single treebank entry. Mark elements if a marker
     * queries were provided.
     */
    std::string read(std::string const &entry,
      std::list<MarkerQuery> const &queries = std::list<MarkerQuery>()) const;

    /** The number of entries in the corpus. */
    size_t size() const;

  private:
    virtual EntryIterator getBegin() const = 0;
    virtual EntryIterator getEnd() const = 0;
    virtual std::string getName() const = 0;
    virtual size_t getSize() const = 0;
    virtual std::string readEntry(std::string const &entry) const = 0;
    virtual std::string readEntryMarkQueries(std::string const &entry,
        std::list<MarkerQuery> const &queries) const;
    virtual EntryIterator runXPath(std::string const &) const;
    virtual EntryIterator runXQuery(std::string const &) const;
    virtual EntryIterator runQueryWithStylesheet(QueryDialect d,
      std::string const &q, std::string const &stylesheet,
      std::list<MarkerQuery> const &markerQueries) const;
    virtual bool validQuery(QueryDialect d, bool variables, std::string const &q) const;
};

}

#endif  // ALPINO_CORPUSREADER_HH
<|endoftext|>


<commit_before>/*
   Copyright (c) 2009-2015, Jack Poulson
   All rights reserved.

   This file is part of Elemental and is under the BSD 2-Clause License,
   which can be found in the LICENSE file in the root directory, or at
   http://opensource.org/licenses/BSD-2-Clause
*/
#pragma once
#ifndef EL_ENVIRONMENT_DECL_HPP
#define EL_ENVIRONMENT_DECL_HPP

namespace El {

using std::size_t;

using std::array;
using std::complex;
using std::deque;
using std::function;
using std::pair;
using std::set;
using std::vector;

using std::make_shared;
using std::shared_ptr;
using std::unique_ptr;

using std::move;

using std::string;
using std::cout;
using std::cerr;
using std::endl;
using std::ifstream;
using std::ofstream;
using std::ostream;
using std::ostringstream;

using std::exception;
using std::uncaught_exception;

void PrintVersion( ostream& os=cout );
void PrintConfig( ostream& os=cout );
void PrintCCompilerInfo( ostream& os=cout );
void PrintCxxCompilerInfo( ostream& os=cout );
bool Using64BitInt();
bool Using64BitBlasInt();

// For initializing and finalizing Elemental
void Initialize( int& argc, char**& argv );
void Finalize();
bool Initialized();

// For getting the MPI argument instance (for internal usage)
class Args : public choice::MpiArgs
{
public:
    Args
    ( int argc, char** argv,
      mpi::Comm comm=mpi::COMM_WORLD, ostream& error=cerr )
    : choice::MpiArgs(argc,argv,comm,error)
    { }
    virtual ~Args() { }
protected:
    virtual void HandleVersion( ostream& os=cout ) const;
    virtual void HandleBuild( ostream& os=cout ) const;
};
Args& GetArgs();

// For processing command-line arguments
template<typename T>
T Input( string name, string desc );
template<typename T>
T Input( string name, string desc, T defaultVal );
void ProcessInput();
void PrintInputReport();

// For getting and setting the algorithmic blocksize
Int Blocksize();
void SetBlocksize( Int blocksize );

// For manipulating the algorithmic blocksize as a stack
void PushBlocksizeStack( Int blocksize );
void PopBlocksizeStack();

Int DefaultBlockHeight();
Int DefaultBlockWidth();
void SetDefaultBlockHeight( Int blockHeight );
void SetDefaultBlockWidth( Int blockWidth );

std::mt19937& Generator();

template<typename T>
inline const T& Max( const T& m, const T& n ) EL_NO_EXCEPT
{ return std::max(m,n); }

inline const Int& Max( const Int& m, const Int& n ) EL_NO_EXCEPT
{ return std::max(m,n); }

template<typename T>
inline const T& Min( const T& m, const T& n ) EL_NO_EXCEPT
{ return std::min(m,n); }

inline const Int& Min( const Int& m, const Int& n ) EL_NO_EXCEPT
{ return std::min(m,n); }

// Replacement for std::memcpy, which is known to often be suboptimal.
// Notice the sizeof(T) is no longer required.
template<typename T>
void MemCopy( T* dest, const T* source, size_t numEntries );

template<typename T>
void MemSwap( T* a, T* b, T* temp, size_t numEntries );

// Generalization of std::memcpy so that unit strides are not required
template<typename T>
void StridedMemCopy
(       T* dest,   Int destStride,
  const T* source, Int sourceStride, Int numEntries );

template<typename S,typename T>
inline void CopySTL( const S& a, T& b )
{
    b.resize( a.size() );
    std::copy( a.begin(), a.end(), b.begin() );
}

// Replacement for std::memset, which is likely suboptimal and hard to extend
// to non-POD datatypes. Notice that sizeof(T) is no longer required.
template<typename T>
void MemZero( T* buffer, size_t numEntries );

// Clear the contents of x by swapping with an empty object of the same type
template<typename T>
void SwapClear( T& x );

inline void BuildStream( ostringstream& os ) { }

template<typename T,typename... Args>
inline void BuildStream( ostringstream& os, T item, Args... args )
{
    os << item;
    BuildStream( os, args... );
}

template<typename... Args>
inline void LogicError( Args... args )
{
    ostringstream os;
    BuildStream( os, args... );
    os << endl;
    throw std::logic_error( os.str().c_str() );
}

template<typename... Args>
inline void RuntimeError( Args... args )
{
    ostringstream os;
    BuildStream( os, args... );
    os << endl;
    throw std::logic_error( os.str().c_str() );
}

// This is the only place that Elemental is currently using duck-typing.
// I'm not sure if it's a good idea to use it more often.
template<class MatType>
inline string
DimsString( const MatType& A, string label="Matrix" )
{
    ostringstream os;
    os << label << " ~ " << A.Height() << " x " << A.Width();
    return os.str();
}

// This is defined in choice.hpp
class ArgException;

// An exception which signifies that a matrix was unexpectedly singular.
class SingularMatrixException : public std::runtime_error
{
public:
    SingularMatrixException( const char* msg="Matrix was singular" )
    : std::runtime_error( msg ) { }
};

// An exception which signifies a zero pivot was chosen, though the matrix
// may not actually be singular
class ZeroPivotException : public std::runtime_error
{
public:
    ZeroPivotException( const char* msg="Zero pivot was chosen" )
    : std::runtime_error( msg ) { }
};

// An exception which signifies that a matrix was unexpectedly non-HPD
class NonHPDMatrixException  : public std::runtime_error
{
public:
    NonHPDMatrixException( const char* msg="Matrix was not HPD" )
    : std::runtime_error( msg ) { }
};

// An exception which signifies that a matrix was unexpectedly non-HPSD
class NonHPSDMatrixException  : public std::runtime_error
{
public:
    NonHPSDMatrixException( const char* msg="Matrix was not HPSD" )
    : std::runtime_error( msg ) { }
};

DEBUG_ONLY(
    void PushCallStack( string s );
    void PopCallStack();
    void DumpCallStack( ostream& os=cerr );

    class CallStackEntry
    {
    public:
        CallStackEntry( string s )
        {
            if( !uncaught_exception() )
                PushCallStack(s);
        }
        ~CallStackEntry()
        {
            if( !uncaught_exception() )
                PopCallStack();
        }
    };
    typedef CallStackEntry CSE;
)

#ifndef EL_RELEASE
    void LogFileOpen( char* filename );

    std::ostream & LogFileOS();

    void LogFileCoutStr( std::string str );

    template<typename... Args>
    void LogFileCout( Args... args );
#endif

void ReportException( const exception& e, ostream& os=cerr );

void ComplainIfDebug();

Int PushIndent();
Int PopIndent();
void SetIndent( Int level );
void ClearIndent();
Int IndentLevel();
std::string Indent();

template<typename... Args>
inline void Output( Args... args )
{
    ostringstream os;
    os << Indent();
    BuildStream( os, args... );
    os << endl;
    cout << os.str();
}

// TODO: OutputRoot?

template<typename T>
void EnsureConsistent( T alpha, mpi::Comm comm, string name="" );

// This will be guaranteed by C++14 via std::make_unique
template<typename T, typename ...Args>
inline unique_ptr<T> MakeUnique( Args&& ...args )
{ return unique_ptr<T>( new T( std::forward<Args>(args)... ) ); }

template<typename T>
T Scan( const vector<T>& counts, vector<T>& offsets );

template<typename T>
bool IsSorted( const vector<T>& x );
// While is_strictly_sorted exists in Boost, it does not exist in the STL (yet)
template<typename T>
bool IsStrictlySorted( const vector<T>& x );

void Union
( vector<Int>& both,
  const vector<Int>& first, const vector<Int>& second );
vector<Int>
Union( const vector<Int>& first, const vector<Int>& second );

void RelativeIndices
( vector<Int>& relInds, const vector<Int>& sub, const vector<Int>& full );
vector<Int> RelativeIndices( const vector<Int>& sub, const vector<Int>& full );

Int Find( const vector<Int>& sortedInds, Int index );
Int Find( const vector<Int>& sortedInds, Int index, const string& msg );

template<typename F>
void UpdateScaledSquare
( F alpha, Base<F>& scale, Base<F>& scaledSquare ) EL_NO_EXCEPT;

} // namespace El

#endif // ifndef EL_ENVIRONMENT_DECL_HPP
<commit_msg>Added a missing definition<commit_after>/*
   Copyright (c) 2009-2015, Jack Poulson
   All rights reserved.

   This file is part of Elemental and is under the BSD 2-Clause License,
   which can be found in the LICENSE file in the root directory, or at
   http://opensource.org/licenses/BSD-2-Clause
*/
#pragma once
#ifndef EL_ENVIRONMENT_DECL_HPP
#define EL_ENVIRONMENT_DECL_HPP

namespace El {

using std::size_t;

using std::array;
using std::complex;
using std::deque;
using std::function;
using std::pair;
using std::set;
using std::vector;

using std::make_shared;
using std::shared_ptr;
using std::unique_ptr;

using std::move;

using std::string;
using std::cout;
using std::cerr;
using std::endl;
using std::ifstream;
using std::ofstream;
using std::ostream;
using std::ostringstream;

using std::exception;
using std::uncaught_exception;

void PrintVersion( ostream& os=cout );
void PrintConfig( ostream& os=cout );
void PrintCCompilerInfo( ostream& os=cout );
void PrintCxxCompilerInfo( ostream& os=cout );
bool Using64BitInt();
bool Using64BitBlasInt();

// For initializing and finalizing Elemental
void Initialize( int& argc, char**& argv );
void Finalize();
bool Initialized();

// For getting the MPI argument instance (for internal usage)
class Args : public choice::MpiArgs
{
public:
    Args
    ( int argc, char** argv,
      mpi::Comm comm=mpi::COMM_WORLD, ostream& error=cerr )
    : choice::MpiArgs(argc,argv,comm,error)
    { }
    virtual ~Args() { }
protected:
    virtual void HandleVersion( ostream& os=cout ) const;
    virtual void HandleBuild( ostream& os=cout ) const;
};
Args& GetArgs();

// For processing command-line arguments
template<typename T>
T Input( string name, string desc );
template<typename T>
T Input( string name, string desc, T defaultVal );
void ProcessInput();
void PrintInputReport();

// For getting and setting the algorithmic blocksize
Int Blocksize();
void SetBlocksize( Int blocksize );

// For manipulating the algorithmic blocksize as a stack
void PushBlocksizeStack( Int blocksize );
void PopBlocksizeStack();

Int DefaultBlockHeight();
Int DefaultBlockWidth();
void SetDefaultBlockHeight( Int blockHeight );
void SetDefaultBlockWidth( Int blockWidth );

std::mt19937& Generator();

template<typename T>
inline const T& Max( const T& m, const T& n ) EL_NO_EXCEPT
{ return std::max(m,n); }

inline const Int& Max( const Int& m, const Int& n ) EL_NO_EXCEPT
{ return std::max(m,n); }

template<typename T>
inline const T& Min( const T& m, const T& n ) EL_NO_EXCEPT
{ return std::min(m,n); }

inline const Int& Min( const Int& m, const Int& n ) EL_NO_EXCEPT
{ return std::min(m,n); }

// Replacement for std::memcpy, which is known to often be suboptimal.
// Notice the sizeof(T) is no longer required.
template<typename T>
void MemCopy( T* dest, const T* source, size_t numEntries );

template<typename T>
void MemSwap( T* a, T* b, T* temp, size_t numEntries );

// Generalization of std::memcpy so that unit strides are not required
template<typename T>
void StridedMemCopy
(       T* dest,   Int destStride,
  const T* source, Int sourceStride, Int numEntries );

template<typename S,typename T>
inline void CopySTL( const S& a, T& b )
{
    b.resize( a.size() );
    std::copy( a.begin(), a.end(), b.begin() );
}

// Replacement for std::memset, which is likely suboptimal and hard to extend
// to non-POD datatypes. Notice that sizeof(T) is no longer required.
template<typename T>
void MemZero( T* buffer, size_t numEntries );

// Clear the contents of x by swapping with an empty object of the same type
template<typename T>
void SwapClear( T& x );

inline void BuildStream( ostringstream& os ) { }

template<typename T,typename... Args>
inline void BuildStream( ostringstream& os, T item, Args... args )
{
    os << item;
    BuildStream( os, args... );
}

template<typename... Args>
inline void LogicError( Args... args )
{
    ostringstream os;
    BuildStream( os, args... );
    os << endl;
    throw std::logic_error( os.str().c_str() );
}

template<typename... Args>
inline void RuntimeError( Args... args )
{
    ostringstream os;
    BuildStream( os, args... );
    os << endl;
    throw std::logic_error( os.str().c_str() );
}

// This is the only place that Elemental is currently using duck-typing.
// I'm not sure if it's a good idea to use it more often.
template<class MatType>
inline string
DimsString( const MatType& A, string label="Matrix" )
{
    ostringstream os;
    os << label << " ~ " << A.Height() << " x " << A.Width();
    return os.str();
}

// This is defined in choice.hpp
class ArgException;

// An exception which signifies that a matrix was unexpectedly singular.
class SingularMatrixException : public std::runtime_error
{
public:
    SingularMatrixException( const char* msg="Matrix was singular" )
    : std::runtime_error( msg ) { }
};

// An exception which signifies a zero pivot was chosen, though the matrix
// may not actually be singular
class ZeroPivotException : public std::runtime_error
{
public:
    ZeroPivotException( const char* msg="Zero pivot was chosen" )
    : std::runtime_error( msg ) { }
};

// An exception which signifies that a matrix was unexpectedly non-HPD
class NonHPDMatrixException  : public std::runtime_error
{
public:
    NonHPDMatrixException( const char* msg="Matrix was not HPD" )
    : std::runtime_error( msg ) { }
};

// An exception which signifies that a matrix was unexpectedly non-HPSD
class NonHPSDMatrixException  : public std::runtime_error
{
public:
    NonHPSDMatrixException( const char* msg="Matrix was not HPSD" )
    : std::runtime_error( msg ) { }
};

DEBUG_ONLY(
    void PushCallStack( string s );
    void PopCallStack();
    void DumpCallStack( ostream& os=cerr );

    class CallStackEntry
    {
    public:
        CallStackEntry( string s )
        {
            if( !uncaught_exception() )
                PushCallStack(s);
        }
        ~CallStackEntry()
        {
            if( !uncaught_exception() )
                PopCallStack();
        }
    };
    typedef CallStackEntry CSE;
)

#ifndef EL_RELEASE
    void LogFileOpen( char* filename );

    std::ostream & LogFileOS();

    void LogFileCoutStr( std::string str );

    template<typename... Args>
    void LogFileCout( Args... args );

    void LogFileClose();
#endif

void ReportException( const exception& e, ostream& os=cerr );

void ComplainIfDebug();

Int PushIndent();
Int PopIndent();
void SetIndent( Int level );
void ClearIndent();
Int IndentLevel();
std::string Indent();

template<typename... Args>
inline void Output( Args... args )
{
    ostringstream os;
    os << Indent();
    BuildStream( os, args... );
    os << endl;
    cout << os.str();
}

// TODO: OutputRoot?

template<typename T>
void EnsureConsistent( T alpha, mpi::Comm comm, string name="" );

// This will be guaranteed by C++14 via std::make_unique
template<typename T, typename ...Args>
inline unique_ptr<T> MakeUnique( Args&& ...args )
{ return unique_ptr<T>( new T( std::forward<Args>(args)... ) ); }

template<typename T>
T Scan( const vector<T>& counts, vector<T>& offsets );

template<typename T>
bool IsSorted( const vector<T>& x );
// While is_strictly_sorted exists in Boost, it does not exist in the STL (yet)
template<typename T>
bool IsStrictlySorted( const vector<T>& x );

void Union
( vector<Int>& both,
  const vector<Int>& first, const vector<Int>& second );
vector<Int>
Union( const vector<Int>& first, const vector<Int>& second );

void RelativeIndices
( vector<Int>& relInds, const vector<Int>& sub, const vector<Int>& full );
vector<Int> RelativeIndices( const vector<Int>& sub, const vector<Int>& full );

Int Find( const vector<Int>& sortedInds, Int index );
Int Find( const vector<Int>& sortedInds, Int index, const string& msg );

template<typename F>
void UpdateScaledSquare
( F alpha, Base<F>& scale, Base<F>& scaledSquare ) EL_NO_EXCEPT;

} // namespace El

#endif // ifndef EL_ENVIRONMENT_DECL_HPP
<|endoftext|>


<commit_before>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#define DBLEVEL  20

#include "IdAllocator.h"
#include "MDS.h"
#include "MDLog.h"

#include "osdc/Filer.h"

#include "include/types.h"

#include "config.h"
#undef dout
#define dout(x)  if (x <= g_conf.debug_mds) cout << g_clock.now() << " mds" << mds->get_nodeid() << ".idalloc: "

void IdAllocator::init_inode()
{
  memset(&inode, 0, sizeof(inode));
  inode.ino = MDS_INO_IDS_OFFSET + mds->get_nodeid();
  inode.layout = g_OSD_FileLayout;
}

inodeno_t IdAllocator::alloc_id()
{
  assert(is_active());

  // pick one
  inodeno_t id = free.start();
  free.erase(id);
  dout(10) << "idalloc " << this << ": alloc id " << id << endl;

  version++;

  // log it
  /*
  if (!replay)
    mds->mdlog->submit_entry(new EAlloc(IDTYPE_INO, id, EALLOC_EV_ALLOC, version));
  */

  return id;
}

void IdAllocator::reclaim_id(inodeno_t id)
{
  assert(is_active());

  dout(10) << "idalloc " << this << ": reclaim id " << id << endl;
  free.insert(id);

  version++;

  /*
  if (!replay)
    mds->mdlog->submit_entry(new EAlloc(IDTYPE_INO, id, EALLOC_EV_FREE, version));
  */
}

class C_ID_Save : public Context {
  IdAllocator *ida;
  version_t version;
public:
  C_ID_Save(IdAllocator *i, version_t v) : ida(i), version(v) {}
  void finish(int r) {
    ida->save_2(version);
  }
};

void IdAllocator::save(Context *onfinish, version_t v)
{
  if (v > 0 && v <= committing_version) {
    dout(10) << "save v " << version << " - already saving "
	     << committing_version << " >= needed " << v << endl;
    waitfor_save[v].push_back(onfinish);
    return;
  }

  dout(10) << "save v " << version << endl;
  assert(is_active());

  bufferlist bl;

  bl.append((char*)&version, sizeof(version));
  ::_encode(free.m, bl);

  committing_version = version;

  if (onfinish)
    waitfor_save[version].push_back(onfinish);

  // write (async)
  mds->filer->write(inode,
                    0, bl.length(), bl,
                    0,
		    0, new C_ID_Save(this, version));
}

void IdAllocator::save_2(version_t v)
{
  dout(10) << "save_2 v " << v << endl;

  committed_version = v;

  list<Context*> ls;
  while (!waitfor_save.empty()) {
    if (waitfor_save.begin()->first > v) break;
    ls.splice(ls.end(), waitfor_save.begin()->second);
    waitfor_save.erase(waitfor_save.begin());
  }
  finish_contexts(ls,0);
}

void IdAllocator::reset()
{
  init_inode();

  // use generic range. FIXME THIS IS CRAP
  free.clear();
  free.insert((uint64_t)0x10000000000 * (uint64_t)(mds->get_nodeid()+1),
	      (uint64_t)0x10000000000 * (uint64_t)(mds->get_nodeid()+2) - (uint64_t)1);

  state = STATE_ACTIVE;
}

// -----------------------

class C_ID_Load : public Context {
public:
  IdAllocator *ida;
  Context *onfinish;
  bufferlist bl;
  C_ID_Load(IdAllocator *i, Context *o) : ida(i), onfinish(o) {}
  void finish(int r) {
    ida->load_2(r, bl, onfinish);
  }
};

void IdAllocator::load(Context *onfinish)
{
  dout(10) << "load" << endl;

  init_inode();

  assert(is_undef());
  state = STATE_OPENING;

  C_ID_Load *c = new C_ID_Load(this, onfinish);
  mds->filer->read(inode,
                   0, inode.layout.stripe_unit,
                   &c->bl,
                   c);
}

void IdAllocator::load_2(int r, bufferlist& bl, Context *onfinish)
{
  assert(is_opening());
  state = STATE_ACTIVE;

  if (r > 0) {
    dout(10) << "load_2 got " << bl.length() << " bytes" << endl;
    int off = 0;
    bl.copy(off, sizeof(version), (char*)&version);
    off += sizeof(version);
    ::_decode(free.m, bl, off);
    committed_version = version;
  }
  else {
    dout(10) << "load_2 found no alloc file" << endl;
    assert(0); // this shouldn't happen if mkfs finished.
    reset();
  }

  if (onfinish) {
    onfinish->finish(0);
    delete onfinish;
  }
}
<commit_msg>clean up inode number ranges<commit_after>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#define DBLEVEL  20

#include "IdAllocator.h"
#include "MDS.h"
#include "MDLog.h"

#include "osdc/Filer.h"

#include "include/types.h"

#include "config.h"
#undef dout
#define dout(x)  if (x <= g_conf.debug_mds) cout << g_clock.now() << " mds" << mds->get_nodeid() << ".idalloc: "

void IdAllocator::init_inode()
{
  memset(&inode, 0, sizeof(inode));
  inode.ino = MDS_INO_IDS_OFFSET + mds->get_nodeid();
  inode.layout = g_OSD_FileLayout;
}

inodeno_t IdAllocator::alloc_id()
{
  assert(is_active());

  // pick one
  inodeno_t id = free.start();
  free.erase(id);
  dout(10) << "idalloc " << this << ": alloc id " << id << endl;

  version++;

  // log it
  /*
  if (!replay)
    mds->mdlog->submit_entry(new EAlloc(IDTYPE_INO, id, EALLOC_EV_ALLOC, version));
  */

  return id;
}

void IdAllocator::reclaim_id(inodeno_t id)
{
  assert(is_active());

  dout(10) << "idalloc " << this << ": reclaim id " << id << endl;
  free.insert(id);

  version++;

  /*
  if (!replay)
    mds->mdlog->submit_entry(new EAlloc(IDTYPE_INO, id, EALLOC_EV_FREE, version));
  */
}

class C_ID_Save : public Context {
  IdAllocator *ida;
  version_t version;
public:
  C_ID_Save(IdAllocator *i, version_t v) : ida(i), version(v) {}
  void finish(int r) {
    ida->save_2(version);
  }
};

void IdAllocator::save(Context *onfinish, version_t v)
{
  if (v > 0 && v <= committing_version) {
    dout(10) << "save v " << version << " - already saving "
	     << committing_version << " >= needed " << v << endl;
    waitfor_save[v].push_back(onfinish);
    return;
  }

  dout(10) << "save v " << version << endl;
  assert(is_active());

  bufferlist bl;

  bl.append((char*)&version, sizeof(version));
  ::_encode(free.m, bl);

  committing_version = version;

  if (onfinish)
    waitfor_save[version].push_back(onfinish);

  // write (async)
  mds->filer->write(inode,
                    0, bl.length(), bl,
                    0,
		    0, new C_ID_Save(this, version));
}

void IdAllocator::save_2(version_t v)
{
  dout(10) << "save_2 v " << v << endl;

  committed_version = v;

  list<Context*> ls;
  while (!waitfor_save.empty()) {
    if (waitfor_save.begin()->first > v) break;
    ls.splice(ls.end(), waitfor_save.begin()->second);
    waitfor_save.erase(waitfor_save.begin());
  }
  finish_contexts(ls,0);
}

void IdAllocator::reset()
{
  init_inode();

  // use generic range. FIXME THIS IS CRAP
  free.clear();
  uint64_t start = (uint64_t)(mds->get_nodeid()+1) << 40;
  uint64_t end = ((uint64_t)(mds->get_nodeid()+2) << 40) - 1;
  free.insert(start, end);

  state = STATE_ACTIVE;
}

// -----------------------

class C_ID_Load : public Context {
public:
  IdAllocator *ida;
  Context *onfinish;
  bufferlist bl;
  C_ID_Load(IdAllocator *i, Context *o) : ida(i), onfinish(o) {}
  void finish(int r) {
    ida->load_2(r, bl, onfinish);
  }
};

void IdAllocator::load(Context *onfinish)
{
  dout(10) << "load" << endl;

  init_inode();

  assert(is_undef());
  state = STATE_OPENING;

  C_ID_Load *c = new C_ID_Load(this, onfinish);
  mds->filer->read(inode,
                   0, inode.layout.stripe_unit,
                   &c->bl,
                   c);
}

void IdAllocator::load_2(int r, bufferlist& bl, Context *onfinish)
{
  assert(is_opening());
  state = STATE_ACTIVE;

  if (r > 0) {
    dout(10) << "load_2 got " << bl.length() << " bytes" << endl;
    int off = 0;
    bl.copy(off, sizeof(version), (char*)&version);
    off += sizeof(version);
    ::_decode(free.m, bl, off);
    committed_version = version;
  }
  else {
    dout(10) << "load_2 found no alloc file" << endl;
    assert(0); // this shouldn't happen if mkfs finished.
    reset();
  }

  if (onfinish) {
    onfinish->finish(0);
    delete onfinish;
  }
}
<|endoftext|>


<commit_before>/******************************************************************************\
* File:          lexer.cpp
* Purpose:       Implementation of lexer classes
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
*
* Copyright (c) 1998-2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
//#include <numeric> // both for accumulate
//#include <functional>
#include <algorithm>
#include <wx/stc/stc.h> // for wxSTC_KEYWORDSET_MAX
#include <wx/tokenzr.h>
#include <wx/extension/lexer.h>
#include <wx/extension/lexers.h>
#include <wx/extension/util.h> // for wxExAlignText

wxExLexer::wxExLexer(const wxXmlNode* node)
{
  m_CommentBegin.clear();
  m_CommentBegin2.clear();
  m_CommentEnd.clear();
  m_CommentEnd2.clear();
  m_Colourings.clear();
  m_Extensions.clear();
  m_Properties.clear();
  m_Keywords.clear();
  m_KeywordsSet.clear();
  m_ScintillaLexer.clear();

  if (node != NULL)
  {
    Set(node);
  }
}

void wxExLexer::ApplyKeywords(wxStyledTextCtrl* stc) const
{
  // Reset keywords, also if no lexer is available.
  for (size_t setno = 0; setno < wxSTC_KEYWORDSET_MAX; setno++)
  {
    stc->SetKeyWords(setno, wxEmptyString);
  }

  // Readme: The Scintilla lexer only recognized lower case words, apparently.
  for (
    auto it = m_KeywordsSet.begin();
    it != m_KeywordsSet.end();
    ++it)
  {
    stc->SetKeyWords(
      it->first,
      GetKeywordsString(it->first).Lower());
  }
}

void wxExLexer::ApplyProperties(wxStyledTextCtrl* stc) const
{
  for_each (m_Properties.begin(), m_Properties.end(),
    std::bind2nd(std::mem_fun_ref(&wxExProperty::Apply), stc));
}

const std::vector<wxExStyle> wxExLexer::AutoMatch(
  const wxString& lexer) const
{
  std::vector<wxExStyle> text;

  auto itlow =
    wxExLexers::Get()->GetMacros().lower_bound(lexer);

  auto itup =
    wxExLexers::Get()->GetMacros().upper_bound(lexer + "ZZZ");

  for (
    auto it = itlow;
    it != itup;
    ++it)
  {
    for (
      auto style =
        wxExLexers::Get()->GetMacrosStyle().begin();
      style != wxExLexers::Get()->GetMacrosStyle().end();
      ++style)
    {
      if (it->first.Contains(style->first))
      {
        text.push_back(wxExStyle(it->second, style->second));
      }
    }
  }

  return text;
}

void wxExLexer::Colourise(wxStyledTextCtrl* stc) const
{
  for_each (m_Colourings.begin(), m_Colourings.end(),
    std::bind2nd(std::mem_fun_ref(&wxExStyle::Apply), stc));

  // And finally colour the entire document.
  stc->Colourise(0, stc->GetLength() - 1);

  // Todo: should be from wxExSTC.
  const int margin_fold_no = 2;

  if (stc->GetProperty("fold") == "1")
  {
    stc->SetMarginWidth(margin_fold_no,
      wxConfigBase::Get()->ReadLong(_("Folding"), 16));

    stc->SetFoldFlags(
      wxConfigBase::Get()->ReadLong(_("Fold Flags"),
      wxSTC_FOLDFLAG_LINEBEFORE_CONTRACTED | wxSTC_FOLDFLAG_LINEAFTER_CONTRACTED));
  }
  else
  {
    stc->SetMarginWidth(margin_fold_no, 0);
  }
}

const wxString wxExLexer::GetFormattedText(
  const wxString& lines,
  const wxString& header,
  bool fill_out_with_space,
  bool fill_out) const
{
  wxString text = lines, header_to_use = header;
  size_t nCharIndex;

  wxString out;

  // Process text between the carriage return line feeds.
  while ((nCharIndex = text.find("\n")) != wxString::npos)
  {
    out << wxExAlignText(
      text.substr(0, nCharIndex),
      header_to_use,
      fill_out_with_space,
      fill_out,
      *this);

    text = text.substr(nCharIndex + 1);
    header_to_use = wxString(' ', header.size());
  }

  if (!text.empty())
  {
    out << wxExAlignText(
      text,
      header_to_use,
      fill_out_with_space,
      fill_out,
      *this);
  }

  return out;
}

const wxString wxExLexer::GetKeywordsString(int keyword_set) const
{
  if (keyword_set == -1)
  {
    return GetKeywordsStringSet(m_Keywords);
  }
  else
  {
    const auto it =
      m_KeywordsSet.find(keyword_set);

    if (it != m_KeywordsSet.end())
    {
      return GetKeywordsStringSet(it->second);
    }
  }

  return wxEmptyString;
}

const wxString wxExLexer::GetKeywordsStringSet(
  const std::set<wxString>& kset) const
{
  // accumulate would be nice, but does not add a space, could not do it easily.
  // return accumulate(kset.begin(), kset.end(), wxEmptyString);
  wxString keywords;

  for (
    auto it = kset.begin();
    it != kset.end();
    ++it)
  {
    keywords += *it + " ";
  }

  return keywords.Trim(); // remove the ending space
}

bool wxExLexer::IsKeyword(const wxString& word) const
{
  const auto it = m_Keywords.find(word);
  return (it != m_Keywords.end());
}

bool wxExLexer::IsOk() const
{
  // At this moment ok if scintilla lexer has been filled.
  return !m_ScintillaLexer.empty();
}

bool wxExLexer::KeywordStartsWith(const wxString& word) const
{
  const auto it = m_Keywords.lower_bound(word.Lower());
  return
    it != m_Keywords.end() &&
    it->StartsWith(word.Lower());
}

const wxString wxExLexer::MakeComment(
  const wxString& text,
  bool fill_out_with_space,
  bool fill_out) const
{
  wxString out;

  text.find("\n") != wxString::npos ?
    out << GetFormattedText(text, wxEmptyString, fill_out_with_space, fill_out):
    out << wxExAlignText(text, wxEmptyString, fill_out_with_space, fill_out, *this);

  return out;
}

const wxString wxExLexer::MakeComment(
  const wxString& prefix,
  const wxString& text) const
{
  wxString out;

  text.find("\n") != wxString::npos ?
    out << GetFormattedText(text, prefix, true, true):
    out << wxExAlignText(text, prefix, true, true, *this);

  return out;
}

const wxString wxExLexer::MakeSingleLineComment(
  const wxString& text,
  bool fill_out_with_space,
  bool fill_out) const
{
  if (m_CommentBegin.empty() && m_CommentEnd.empty())
  {
    return text;
  }

  // First set the fill_out_character.
  wxUniChar fill_out_character;

  if (fill_out_with_space || m_ScintillaLexer == "hypertext")
  {
    fill_out_character = ' ';
  }
  else
  {
    if (text.empty())
    {
      if (m_CommentBegin == m_CommentEnd)
           fill_out_character = '-';
      else fill_out_character = m_CommentBegin[m_CommentBegin.size() - 1];
    }
    else   fill_out_character = ' ';
  }

  wxString out = m_CommentBegin + fill_out_character + text;

  // Fill out characters.
  if (fill_out)
  {
    // To prevent filling out spaces
    if (fill_out_character != ' ' || !m_CommentEnd.empty())
    {
      const auto fill_chars = UsableCharactersPerLine() - text.size();

      if (fill_chars > 0)
      {
        const wxString fill_out(fill_out_character, fill_chars);
        out += fill_out;
      }
    }
  }

  if (!m_CommentEnd.empty()) out += fill_out_character + m_CommentEnd;

  return out;
}

const std::vector<wxExStyle> wxExLexer::ParseNodeColourings(
  const wxXmlNode* node) const
{
  std::vector<wxExStyle> text;

  wxXmlNode* child = node->GetChildren();

  while (child)
  {
    if (child->GetName() == "colouring")
    {
      text.push_back(wxExStyle(child));
    }
    else if (child->GetName() == "comment")
    {
      // Ignore comments.
    }
    else
    {
      wxLogError(_("Undefined colourings tag: %s on line: %d"),
        child->GetName().c_str(),
        child->GetLineNumber());
    }

    child = child->GetNext();
  }

  return text;
}

void wxExLexer::Set(const wxXmlNode* node)
{
  m_ScintillaLexer = node->GetAttribute("name", "");

#ifdef __WXMSW__
  m_Extensions = node->GetAttribute(
    "extensions",
    "*." + m_ScintillaLexer);
#else
  m_Extensions = node->GetAttribute("extensions", "");
#endif

  if (node->GetAttribute("match", "") != "")
  {
    m_Colourings = AutoMatch(node->GetAttribute("match", ""));
  }

  if (m_ScintillaLexer == "hypertext")
  {
    // As our lexers.xml files cannot use xml comments,
    // add them here.
    m_CommentBegin = "<!--";
    m_CommentEnd = "-->";
  }

  wxXmlNode *child = node->GetChildren();

  while (child)
  {
    if (child->GetName() == "colourings")
    {
      const std::vector<wxExStyle> v = ParseNodeColourings(child);

      m_Colourings.insert(
        m_Colourings.end(),
        v.begin(), v.end());
    }
    else if (child->GetName() == "keywords")
    {
      if (!SetKeywords(child->GetNodeContent().Strip(wxString::both)))
      {
        wxLogError(
          _("Keywords could not be set on line: %d"),
          child->GetLineNumber());
      }
    }
    else if (child->GetName() == "properties")
    {
      m_Properties = wxExLexers::Get()->ParseNodeProperties(child);
    }
    else if (child->GetName() == "comments")
    {
      m_CommentBegin = child->GetAttribute("begin1", "");
      m_CommentEnd = child->GetAttribute("end1", "");
      m_CommentBegin2 = child->GetAttribute("begin2", "");
      m_CommentEnd2 = child->GetAttribute("end2", "");
    }
    else if (child->GetName() == "comment")
    {
      // Ignore comments.
    }
    else
    {
      wxLogError(_("Undefined lexer tag: %s on line: %d"),
        child->GetName().c_str(),
        child->GetLineNumber());
    }

    child = child->GetNext();
  }

  if (!IsOk())
  {
    wxLogError(_("Illegal lexer on line: %d"), node->GetLineNumber());
  }
}

// Adds the specified keywords to the keywords map and the keywords set.
// The text might contain the keyword set after a ':'.
// Returns true if keyword could be added
// and false if specified set is illegal.
// Empties existing keywords.
bool wxExLexer::SetKeywords(const wxString& value)
{
  if (!m_Keywords.empty())
  {
    m_Keywords.clear();
  }

  if (!m_KeywordsSet.empty())
  {
    m_KeywordsSet.clear();
  }

  std::set<wxString> keywords_set;

  wxStringTokenizer tkz(value, "\r\n ");

  int setno = 0;

  while (tkz.HasMoreTokens())
  {
    const wxString line = tkz.GetNextToken();
    wxStringTokenizer fields(line, ":");

    wxString keyword;

    if (fields.CountTokens() > 1)
    {
      keyword = fields.GetNextToken();

      const auto new_setno = atoi(fields.GetNextToken().c_str());

      if (new_setno >= wxSTC_KEYWORDSET_MAX)
      {
        return false;
      }

      if (new_setno != setno)
      {
        if (!keywords_set.empty())
        {
          m_KeywordsSet.insert(make_pair(setno, keywords_set));
          keywords_set.clear();
        }

        setno = new_setno;
      }

      keywords_set.insert(keyword);
    }
    else
    {
      keyword = line;
      keywords_set.insert(line);
    }

    m_Keywords.insert(keyword);
  }

  m_KeywordsSet.insert(make_pair(setno, keywords_set));

  return true;
}

bool wxExLexer::SetScintillaLexer(
  const wxString& lexer,
  wxStyledTextCtrl* stc,
  bool show_error)
{
  stc->ClearDocumentStyle();

  for_each (m_Properties.begin(), m_Properties.end(),
    std::bind2nd(std::mem_fun_ref(&wxExProperty::ApplyReset), stc));

  (*this) = wxExLexers::Get()->FindByName(lexer);

  stc->SetLexerLanguage(m_ScintillaLexer);

  if (!IsOk())
  {
    (*this) = wxExLexers::Get()->FindByText(stc->GetLine(0));

    stc->SetLexerLanguage(m_ScintillaLexer);
  }

  if (
      IsOk() &&
      // And check whether the GetLexer from scintilla has a good value.
      // Otherwise it is not known, and we better show an error.
      stc->GetLexer() == wxSTC_LEX_NULL)
  {
    if (show_error)
    {
      wxLogError(_("Lexer is not known") + ": " + m_ScintillaLexer);
    }
  }

  ApplyKeywords(stc);

  wxExLexers::Get()->GetDefaultStyle().Apply(stc);

  stc->StyleClearAll();

  wxExLexers::Get()->ApplyGlobalStyles(stc);
  wxExLexers::Get()->ApplyIndicators(stc);
  wxExLexers::Get()->ApplyProperties(stc);
  wxExLexers::Get()->ApplyMarkers(stc);
  ApplyProperties(stc);

  Colourise(stc);

  return stc->GetLexer() != wxSTC_LEX_NULL;
}

int wxExLexer::UsableCharactersPerLine() const
{
  // We always use lines with 80 characters. We adjust this here for
  // the space the beginning and end of the comment characters occupy.
  return 80
    - ((m_CommentBegin.size() != 0) ? m_CommentBegin.size() + 1 : 0)
    - ((m_CommentEnd.size() != 0) ? m_CommentEnd.size() + 1 : 0);
}
<commit_msg>simplified code<commit_after>/******************************************************************************\
* File:          lexer.cpp
* Purpose:       Implementation of lexer classes
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
*
* Copyright (c) 1998-2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
//#include <numeric> // both for accumulate
//#include <functional>
#include <algorithm>
#include <wx/stc/stc.h> // for wxSTC_KEYWORDSET_MAX
#include <wx/tokenzr.h>
#include <wx/extension/lexer.h>
#include <wx/extension/lexers.h>
#include <wx/extension/util.h> // for wxExAlignText

wxExLexer::wxExLexer(const wxXmlNode* node)
{
  m_CommentBegin.clear();
  m_CommentBegin2.clear();
  m_CommentEnd.clear();
  m_CommentEnd2.clear();
  m_Colourings.clear();
  m_Extensions.clear();
  m_Properties.clear();
  m_Keywords.clear();
  m_KeywordsSet.clear();
  m_ScintillaLexer.clear();

  if (node != NULL)
  {
    Set(node);
  }
}

void wxExLexer::ApplyKeywords(wxStyledTextCtrl* stc) const
{
  // Reset keywords, also if no lexer is available.
  for (size_t setno = 0; setno < wxSTC_KEYWORDSET_MAX; setno++)
  {
    stc->SetKeyWords(setno, wxEmptyString);
  }

  // Readme: The Scintilla lexer only recognized lower case words, apparently.
  for (
    auto it = m_KeywordsSet.begin();
    it != m_KeywordsSet.end();
    ++it)
  {
    stc->SetKeyWords(
      it->first,
      GetKeywordsString(it->first).Lower());
  }
}

void wxExLexer::ApplyProperties(wxStyledTextCtrl* stc) const
{
  for_each (m_Properties.begin(), m_Properties.end(),
    std::bind2nd(std::mem_fun_ref(&wxExProperty::Apply), stc));
}

const std::vector<wxExStyle> wxExLexer::AutoMatch(
  const wxString& lexer) const
{
  std::vector<wxExStyle> text;

  auto itlow =
    wxExLexers::Get()->GetMacros().lower_bound(lexer);

  auto itup =
    wxExLexers::Get()->GetMacros().upper_bound(lexer + "ZZZ");

  for (
    auto it = itlow;
    it != itup;
    ++it)
  {
    for (
      auto style =
        wxExLexers::Get()->GetMacrosStyle().begin();
      style != wxExLexers::Get()->GetMacrosStyle().end();
      ++style)
    {
      if (it->first.Contains(style->first))
      {
        text.push_back(wxExStyle(it->second, style->second));
      }
    }
  }

  return text;
}

void wxExLexer::Colourise(wxStyledTextCtrl* stc) const
{
  for_each (m_Colourings.begin(), m_Colourings.end(),
    std::bind2nd(std::mem_fun_ref(&wxExStyle::Apply), stc));

  // And finally colour the entire document.
  stc->Colourise(0, stc->GetLength() - 1);

  // Todo: should be from wxExSTC.
  const int margin_fold_no = 2;

  if (stc->GetProperty("fold") == "1")
  {
    stc->SetMarginWidth(margin_fold_no,
      wxConfigBase::Get()->ReadLong(_("Folding"), 16));

    stc->SetFoldFlags(
      wxConfigBase::Get()->ReadLong(_("Fold Flags"),
      wxSTC_FOLDFLAG_LINEBEFORE_CONTRACTED | wxSTC_FOLDFLAG_LINEAFTER_CONTRACTED));
  }
  else
  {
    stc->SetMarginWidth(margin_fold_no, 0);
  }
}

const wxString wxExLexer::GetFormattedText(
  const wxString& lines,
  const wxString& header,
  bool fill_out_with_space,
  bool fill_out) const
{
  wxString text = lines, header_to_use = header;
  size_t nCharIndex;

  wxString out;

  // Process text between the carriage return line feeds.
  while ((nCharIndex = text.find("\n")) != wxString::npos)
  {
    out << wxExAlignText(
      text.substr(0, nCharIndex),
      header_to_use,
      fill_out_with_space,
      fill_out,
      *this);

    text = text.substr(nCharIndex + 1);
    header_to_use = wxString(' ', header.size());
  }

  if (!text.empty())
  {
    out << wxExAlignText(
      text,
      header_to_use,
      fill_out_with_space,
      fill_out,
      *this);
  }

  return out;
}

const wxString wxExLexer::GetKeywordsString(int keyword_set) const
{
  if (keyword_set == -1)
  {
    return GetKeywordsStringSet(m_Keywords);
  }
  else
  {
    const auto it =
      m_KeywordsSet.find(keyword_set);

    if (it != m_KeywordsSet.end())
    {
      return GetKeywordsStringSet(it->second);
    }
  }

  return wxEmptyString;
}

const wxString wxExLexer::GetKeywordsStringSet(
  const std::set<wxString>& kset) const
{
  // accumulate would be nice, but does not add a space, could not do it easily.
  // return accumulate(kset.begin(), kset.end(), wxEmptyString);
  wxString keywords;

  for (
    auto it = kset.begin();
    it != kset.end();
    ++it)
  {
    keywords += *it + " ";
  }

  return keywords.Trim(); // remove the ending space
}

bool wxExLexer::IsKeyword(const wxString& word) const
{
  const auto it = m_Keywords.find(word);
  return (it != m_Keywords.end());
}

bool wxExLexer::IsOk() const
{
  // At this moment ok if scintilla lexer has been filled.
  return !m_ScintillaLexer.empty();
}

bool wxExLexer::KeywordStartsWith(const wxString& word) const
{
  const auto it = m_Keywords.lower_bound(word.Lower());
  return
    it != m_Keywords.end() &&
    it->StartsWith(word.Lower());
}

const wxString wxExLexer::MakeComment(
  const wxString& text,
  bool fill_out_with_space,
  bool fill_out) const
{
  wxString out;

  text.find("\n") != wxString::npos ?
    out << GetFormattedText(text, wxEmptyString, fill_out_with_space, fill_out):
    out << wxExAlignText(text, wxEmptyString, fill_out_with_space, fill_out, *this);

  return out;
}

const wxString wxExLexer::MakeComment(
  const wxString& prefix,
  const wxString& text) const
{
  wxString out;

  text.find("\n") != wxString::npos ?
    out << GetFormattedText(text, prefix, true, true):
    out << wxExAlignText(text, prefix, true, true, *this);

  return out;
}

const wxString wxExLexer::MakeSingleLineComment(
  const wxString& text,
  bool fill_out_with_space,
  bool fill_out) const
{
  if (m_CommentBegin.empty() && m_CommentEnd.empty())
  {
    return text;
  }

  // First set the fill_out_character.
  wxUniChar fill_out_character;

  if (fill_out_with_space || m_ScintillaLexer == "hypertext")
  {
    fill_out_character = ' ';
  }
  else
  {
    if (text.empty())
    {
      if (m_CommentBegin == m_CommentEnd)
           fill_out_character = '-';
      else fill_out_character = m_CommentBegin[m_CommentBegin.size() - 1];
    }
    else   fill_out_character = ' ';
  }

  wxString out = m_CommentBegin + fill_out_character + text;

  // Fill out characters.
  if (fill_out)
  {
    // To prevent filling out spaces
    if (fill_out_character != ' ' || !m_CommentEnd.empty())
    {
      const auto fill_chars = UsableCharactersPerLine() - text.size();

      if (fill_chars > 0)
      {
        const wxString fill_out(fill_out_character, fill_chars);
        out += fill_out;
      }
    }
  }

  if (!m_CommentEnd.empty()) out += fill_out_character + m_CommentEnd;

  return out;
}

const std::vector<wxExStyle> wxExLexer::ParseNodeColourings(
  const wxXmlNode* node) const
{
  std::vector<wxExStyle> text;

  wxXmlNode* child = node->GetChildren();

  while (child)
  {
    if (child->GetName() == "colouring")
    {
      text.push_back(wxExStyle(child));
    }
    else if (child->GetName() == "comment")
    {
      // Ignore comments.
    }
    else
    {
      wxLogError(_("Undefined colourings tag: %s on line: %d"),
        child->GetName().c_str(),
        child->GetLineNumber());
    }

    child = child->GetNext();
  }

  return text;
}

void wxExLexer::Set(const wxXmlNode* node)
{
  m_ScintillaLexer = node->GetAttribute("name", "");

#ifdef __WXMSW__
  m_Extensions = node->GetAttribute(
    "extensions",
    "*." + m_ScintillaLexer);
#else
  m_Extensions = node->GetAttribute("extensions", "");
#endif

  if (node->GetAttribute("match", "") != "")
  {
    m_Colourings = AutoMatch(node->GetAttribute("match", ""));
  }

  if (m_ScintillaLexer == "hypertext")
  {
    // As our lexers.xml files cannot use xml comments,
    // add them here.
    m_CommentBegin = "<!--";
    m_CommentEnd = "-->";
  }

  wxXmlNode *child = node->GetChildren();

  while (child)
  {
    if (child->GetName() == "colourings")
    {
      const std::vector<wxExStyle> v = ParseNodeColourings(child);

      m_Colourings.insert(
        m_Colourings.end(),
        v.begin(), v.end());
    }
    else if (child->GetName() == "keywords")
    {
      if (!SetKeywords(child->GetNodeContent().Strip(wxString::both)))
      {
        wxLogError(
          _("Keywords could not be set on line: %d"),
          child->GetLineNumber());
      }
    }
    else if (child->GetName() == "properties")
    {
      m_Properties = wxExLexers::Get()->ParseNodeProperties(child);
    }
    else if (child->GetName() == "comments")
    {
      m_CommentBegin = child->GetAttribute("begin1", "");
      m_CommentEnd = child->GetAttribute("end1", "");
      m_CommentBegin2 = child->GetAttribute("begin2", "");
      m_CommentEnd2 = child->GetAttribute("end2", "");
    }
    else if (child->GetName() == "comment")
    {
      // Ignore comments.
    }
    else
    {
      wxLogError(_("Undefined lexer tag: %s on line: %d"),
        child->GetName().c_str(),
        child->GetLineNumber());
    }

    child = child->GetNext();
  }

  if (!IsOk())
  {
    wxLogError(_("Illegal lexer on line: %d"), node->GetLineNumber());
  }
}

// Adds the specified keywords to the keywords map and the keywords set.
// The text might contain the keyword set after a ':'.
// Returns true if keyword could be added
// and false if specified set is illegal.
// Empties existing keywords.
bool wxExLexer::SetKeywords(const wxString& value)
{
  if (!m_Keywords.empty())
  {
    m_Keywords.clear();
  }

  if (!m_KeywordsSet.empty())
  {
    m_KeywordsSet.clear();
  }

  std::set<wxString> keywords_set;

  wxStringTokenizer tkz(value, "\r\n ");

  int setno = 0;

  while (tkz.HasMoreTokens())
  {
    const wxString line = tkz.GetNextToken();
    wxStringTokenizer fields(line, ":");

    wxString keyword;

    if (fields.CountTokens() > 1)
    {
      keyword = fields.GetNextToken();

      const auto new_setno = atoi(fields.GetNextToken().c_str());

      if (new_setno >= wxSTC_KEYWORDSET_MAX)
      {
        return false;
      }

      if (new_setno != setno)
      {
        if (!keywords_set.empty())
        {
          m_KeywordsSet.insert(make_pair(setno, keywords_set));
          keywords_set.clear();
        }

        setno = new_setno;
      }

      keywords_set.insert(keyword);
    }
    else
    {
      keyword = line;
      keywords_set.insert(line);
    }

    m_Keywords.insert(keyword);
  }

  m_KeywordsSet.insert(make_pair(setno, keywords_set));

  return true;
}

bool wxExLexer::SetScintillaLexer(
  const wxString& lexer,
  wxStyledTextCtrl* stc,
  bool show_error)
{
  stc->ClearDocumentStyle();

  for_each (m_Properties.begin(), m_Properties.end(),
    std::bind2nd(std::mem_fun_ref(&wxExProperty::ApplyReset), stc));

  (*this) = wxExLexers::Get()->FindByName(lexer);

  stc->SetLexerLanguage(m_ScintillaLexer);

  if (!IsOk())
  {
    (*this) = wxExLexers::Get()->FindByText(stc->GetLine(0));

    stc->SetLexerLanguage(m_ScintillaLexer);
  }

  if (
      IsOk() &&
      // And check whether the GetLexer from scintilla has a good value.
      // Otherwise it is not known, and we better show an error.
      stc->GetLexer() == wxSTC_LEX_NULL &&
      show_error)
  {
    wxLogError(_("Lexer is not known") + ": " + m_ScintillaLexer);
  }

  ApplyKeywords(stc);

  wxExLexers::Get()->GetDefaultStyle().Apply(stc);

  stc->StyleClearAll();

  wxExLexers::Get()->ApplyGlobalStyles(stc);
  wxExLexers::Get()->ApplyIndicators(stc);
  wxExLexers::Get()->ApplyProperties(stc);
  wxExLexers::Get()->ApplyMarkers(stc);
  ApplyProperties(stc);

  Colourise(stc);

  return stc->GetLexer() != wxSTC_LEX_NULL;
}

int wxExLexer::UsableCharactersPerLine() const
{
  // We always use lines with 80 characters. We adjust this here for
  // the space the beginning and end of the comment characters occupy.
  return 80
    - ((m_CommentBegin.size() != 0) ? m_CommentBegin.size() + 1 : 0)
    - ((m_CommentEnd.size() != 0) ? m_CommentEnd.size() + 1 : 0);
}
<|endoftext|>


<commit_before>/*
    WPN-XM Server Control Panel

    WPN-XM SCP is a tool to manage Nginx, PHP and MariaDb daemons under windows.
    It's a fork of Easy WEMP originally written by Yann Le Moigne and (c) 2010.
    WPN-XM SCP is written by Jens-Andre Koch and (c) 2011 - onwards.

    This file is part of WPN-XM Serverpack for Windows.

    WPN-XM SCP is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    WPN-XM SCP 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with WPN-XM SCP. If not, see <http://www.gnu.org/licenses/>.
*/

// local WPN-XM SCP includes
#include "main.h"
#include "mainwindow.h"

// global QT includes
#include <QApplication>
#include <QObject>
#include <QSystemTrayIcon>
#include <QMessageBox>
#include <QSharedMemory>
#include <QtGui>

// main method
int main(int argc, char * argv[])
{
    Q_INIT_RESOURCE(Resources);

    QApplication application(argc, argv);

    // Single Instance Check
    exitIfAlreadyRunning();

    // Application Meta Data
    application.setApplicationName(APP_NAME);
    application.setApplicationVersion(APP_VERSION);
    application.setOrganizationName("Jens-Andr Koch");
    application.setOrganizationDomain("http://wpn-xm.org/");
    application.setWindowIcon(QIcon(":/wpnxm"));

    // do not leave until Quit is clicked in the tray menu
    application.setQuitOnLastWindowClosed(false);

    MainWindow mainWindow;
    mainWindow.show();

    //qDebug() << APP_NAME;
    //qDebug() << APP_VERSION;

    // enter the Qt Event loop here
    return application.exec();
}

/*
 * Single Instance Check
 * Although some people tend to solve this problem by using QtSingleApplication,
 * this approach uses a GUID stored into shared memory.
 */
void exitIfAlreadyRunning()
{
      // Set GUID for WPN-XM Server Control Panel to memory
      // It needs to be "static", because the QSharedMemory instance gets destroyed
      // at the end of the function and so does the shared memory segment.
      static QSharedMemory shared("004d54f6-7d00-4478-b612-f242f081b023");

      // already running
      if( !shared.create( 512, QSharedMemory::ReadWrite) )
      {
          QMessageBox msgBox;
          msgBox.setWindowTitle(APP_NAME);
          msgBox.setText( QObject::tr("WPN-XM is already running.") );
          msgBox.setIcon( QMessageBox::Critical );
          msgBox.exec();

          exit(0);
      }
      else
      {
          qDebug() << "Application started and not already running.";
      }
}
<commit_msg>renamed application to app<commit_after>/*
    WPN-XM Server Control Panel

    WPN-XM SCP is a tool to manage Nginx, PHP and MariaDb daemons under windows.
    It's a fork of Easy WEMP originally written by Yann Le Moigne and (c) 2010.
    WPN-XM SCP is written by Jens-Andre Koch and (c) 2011 - onwards.

    This file is part of WPN-XM Serverpack for Windows.

    WPN-XM SCP is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    WPN-XM SCP 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with WPN-XM SCP. If not, see <http://www.gnu.org/licenses/>.
*/

// local WPN-XM SCP includes
#include "main.h"
#include "mainwindow.h"

// global QT includes
#include <QApplication>
#include <QObject>
#include <QSystemTrayIcon>
#include <QMessageBox>
#include <QSharedMemory>
#include <QtGui>

// main method
int main(int argc, char * argv[])
{
    Q_INIT_RESOURCE(Resources);

    QApplication app(argc, argv);

    // Single Instance Check
    exitIfAlreadyRunning();

    // Application Meta Data
    app.setApplicationName(APP_NAME);
    app.setApplicationVersion(APP_VERSION);
    app.setOrganizationName("Jens-Andr Koch");
    app.setOrganizationDomain("http://wpn-xm.org/");
    app.setWindowIcon(QIcon(":/wpnxm"));

    // do not leave until Quit is clicked in the tray menu
    app.setQuitOnLastWindowClosed(false);

    MainWindow mainWindow;
    mainWindow.show();

    //qDebug() << APP_NAME;
    //qDebug() << APP_VERSION;

    // enter the Qt Event loop here
    return app.exec();
}

/*
 * Single Instance Check
 * Although some people tend to solve this problem by using QtSingleApplication,
 * this approach uses a GUID stored into shared memory.
 */
void exitIfAlreadyRunning()
{
      // Set GUID for WPN-XM Server Control Panel to memory
      // It needs to be "static", because the QSharedMemory instance gets destroyed
      // at the end of the function and so does the shared memory segment.
      static QSharedMemory shared("004d54f6-7d00-4478-b612-f242f081b023");

      // already running
      if( !shared.create( 512, QSharedMemory::ReadWrite) )
      {
          QMessageBox msgBox;
          msgBox.setWindowTitle(APP_NAME);
          msgBox.setText( QObject::tr("WPN-XM is already running.") );
          msgBox.setIcon( QMessageBox::Critical );
          msgBox.exec();

          exit(0);
      }
      else
      {
          qDebug() << "Application started and not already running.";
      }
}
<|endoftext|>


<commit_before>/*
 *  This file is part of Poedit (https://poedit.net)
 *
 *  Copyright (C) 2014-2022 Vaclav Slavik
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *  DEALINGS IN THE SOFTWARE.
 *
 */

#include "syntaxhighlighter.h"

#include "catalog.h"
#include "str_helpers.h"

#include <unicode/uchar.h>

#include <regex>

namespace
{

class BasicSyntaxHighlighter : public SyntaxHighlighter
{
public:
    void Highlight(const std::wstring& s, const CallbackType& highlight) override
    {
        if (s.empty())
            return;

        const int length = int(s.length());

        // Leading whitespace:
        for (auto i = s.begin(); i != s.end(); ++i)
        {
            if (!u_isblank(*i))
            {
                int wlen = int(i - s.begin());
                if (wlen)
                    highlight(0, wlen, LeadingWhitespace);
                break;
            }
        }

        // Trailing whitespace:
        for (auto i = s.rbegin(); i != s.rend(); ++i)
        {
            if (!u_isblank(*i))
            {
                int wlen = int(i - s.rbegin());
                if (wlen)
                    highlight(length - wlen, length, LeadingWhitespace);
                break;
            }
        }

        int blank_block_pos = -1;

        for (auto i = s.begin(); i != s.end(); ++i)
        {
            // Some special whitespace characters should always be highlighted:
            if (*i == 0x00A0 /*non-breakable space*/)
            {
                int pos = int(i - s.begin());
                highlight(pos, pos + 1, LeadingWhitespace);
            }

            // Duplicate whitespace (2+ spaces etc.):
            else if (u_isblank(*i))
            {
                if (blank_block_pos == -1)
                    blank_block_pos = int(i - s.begin());
            }
            else if (blank_block_pos != -1)
            {
                int endpos = int(i - s.begin());
                if (endpos - blank_block_pos >= 2)
                    highlight(blank_block_pos, endpos, LeadingWhitespace);
                blank_block_pos = -1;
            }

            // Escape sequences:
            if (*i == '\\')
            {
                int pos = int(i - s.begin());
                if (++i == s.end())
                    break;
                // Note: this must match AnyTranslatableTextCtrl::EscapePlainText()
                switch (*i)
                {
                    case '0':
                    case 'a':
                    case 'b':
                    case 'f':
                    case 'n':
                    case 'r':
                    case 't':
                    case 'v':
                    case '\\':
                        highlight(pos, pos + 2, Escape);
                        break;
                    default:
                        break;
                }
            }
        }
    }
};

/// Highlighter that runs multiple sub-highlighters
class CompositeSyntaxHighlighter : public SyntaxHighlighter
{
public:
    void Add(std::shared_ptr<SyntaxHighlighter> h) { m_sub.push_back(h); }

    void Highlight(const std::wstring& s, const CallbackType& highlight) override
    {
        for (auto h : m_sub)
            h->Highlight(s, highlight);
    }

private:
    std::vector<std::shared_ptr<SyntaxHighlighter>> m_sub;
};

/// Match regular expressions for highlighting
class RegexSyntaxHighlighter : public SyntaxHighlighter
{
public:
    /// Ctor. Notice that @a re is a reference and must outlive the highlighter!
    RegexSyntaxHighlighter(std::wregex& re, TextKind kind) : m_re(re), m_kind(kind) {}

    void Highlight(const std::wstring& s, const CallbackType& highlight) override
    {
        try
        {
            std::wsregex_iterator next(s.begin(), s.end(), m_re);
            std::wsregex_iterator end;
            while (next != end)
            {
                auto match = *next++;
                if (match.empty())
                    continue;
                int pos = static_cast<int>(match.position());
                highlight(pos, pos + static_cast<int>(match.length()), m_kind);
            }
        }
        catch (std::regex_error& e)
        {
            switch (e.code())
            {
                case std::regex_constants::error_complexity:
                case std::regex_constants::error_stack:
                    // MSVC version of std::regex in particular can fail to match
                    // e.g. HTML regex with backreferences on insanely large strings;
                    // in that case, don't highlight instead of failing outright.
                    return;
                default:
                    throw;
            }
        }
    }

private:
    std::wregex& m_re;
    TextKind m_kind;
};

std::wregex RE_HTML_MARKUP(LR"((<\/?[a-zA-Z0-9:-]+(\s+[-:\w]+(=([-:\w+]|"[^"]*"|'[^']*'))?)*\s*\/?>)|(&[^ ;]+;))",
                           std::regex_constants::ECMAScript | std::regex_constants::optimize);

// php-format per http://php.net/manual/en/function.sprintf.php plus positionals
std::wregex RE_PHP_FORMAT(LR"(%(\d+\$)?[-+]{0,2}([ 0]|'.)?-?\d*(\..?\d+)?[%bcdeEfFgGosuxX])",
                          std::regex_constants::ECMAScript | std::regex_constants::optimize);

// c-format per http://en.cppreference.com/w/cpp/io/c/fprintf,
//              http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html
std::wregex RE_C_FORMAT(LR"(%(\d+\$)?[-+ #0]{0,5}(\d+|\*)?(\.(\d+|\*))?(hh|ll|[hljztL])?[%csdioxXufFeEaAgGnp])",
                        std::regex_constants::ECMAScript | std::regex_constants::optimize);

// python-format old style https://docs.python.org/2/library/stdtypes.html#string-formatting
//               new style https://docs.python.org/3/library/string.html#format-string-syntax
std::wregex RE_PYTHON_FORMAT(LR"((%(\(\w+\))?[-+ #0]?(\d+|\*)?(\.(\d+|\*))?[hlL]?[diouxXeEfFgGcrs%]))" // old style
                             "|"
                             LR"((\{([^{}])*\}))", // new style, being permissive
                             std::regex_constants::ECMAScript | std::regex_constants::optimize);

// ruby-format per https://ruby-doc.org/core-2.7.1/Kernel.html#method-i-sprintf
std::wregex RE_RUBY_FORMAT(LR"(%(\d+\$)?[-+ #0]{0,5}(\d+|\*)?(\.(\d+|\*))?(hh|ll|[hljztL])?[%csdioxXufFeEaAgGnp])",
                           std::regex_constants::ECMAScript | std::regex_constants::optimize);

// variables expansion for various template languages
std::wregex RE_COMMON_PLACEHOLDERS(
                    //
                    //           |             |               |
                    LR"(%[\w.-]+%|%?\{[\w.-]+\}|\{\{[\w.-]+\}\}|[@%:][\w-]+)",
                    //      |    |      |      |        |      |      |
                    //      |           |               |             +--------- Drupal: non-terminated @var, %var or :var
                    //      |           |               |                        !MUST be last in regex because of lack of termination character!
                    //      |           |               |
                    //      |           |               +----------------------- {{var}}
                    //      |           |
                    //      |           +--------------------------------------- %{var} (Ruby) and {var}
                    //      |
                    //      +--------------------------------------------------- %var% (Twig)
                    //
                    std::regex_constants::ECMAScript | std::regex_constants::optimize);

} // anonymous namespace

SyntaxHighlighterPtr SyntaxHighlighter::ForItem(const CatalogItem& item, int kindsMask)
{
    auto formatFlag = item.GetFormatFlag();
    bool needsHTML = (kindsMask & Markup) && std::regex_search(str::to_wstring(item.GetString()), RE_HTML_MARKUP);
    bool needsGenericPlaceholders = (kindsMask & Placeholder) && std::regex_search(str::to_wstring(item.GetString()), RE_COMMON_PLACEHOLDERS);

    static auto basic = std::make_shared<BasicSyntaxHighlighter>();
    if (!needsHTML && !needsGenericPlaceholders && formatFlag.empty())
    {
        if (kindsMask & (LeadingWhitespace | Escape))
            return basic;
        else
            return nullptr;
    }

    auto all = std::make_shared<CompositeSyntaxHighlighter>();

    // HTML goes first, has lowest priority than special-purpose stuff like format strings:
    if (needsHTML)
    {
        static auto html = std::make_shared<RegexSyntaxHighlighter>(RE_HTML_MARKUP, TextKind::Markup);
        all->Add(html);
    }

    if (needsGenericPlaceholders)
    {
        // If no format specified, heuristically apply highlighting of common variable markers
        static auto placeholders = std::make_shared<RegexSyntaxHighlighter>(RE_COMMON_PLACEHOLDERS, TextKind::Placeholder);
        all->Add(placeholders);
    }

    if (kindsMask & Placeholder)
    {
        // TODO: more/all languages
        if (formatFlag == "php")
        {
            static auto php_format = std::make_shared<RegexSyntaxHighlighter>(RE_PHP_FORMAT, TextKind::Placeholder);
            all->Add(php_format);
        }
        else if (formatFlag == "c")
        {
            static auto c_format = std::make_shared<RegexSyntaxHighlighter>(RE_C_FORMAT, TextKind::Placeholder);
            all->Add(c_format);
        }
        else if (formatFlag == "python")
        {
            static auto python_format = std::make_shared<RegexSyntaxHighlighter>(RE_PYTHON_FORMAT, TextKind::Placeholder);
            all->Add(python_format);
        }
        else if (formatFlag == "ruby")
        {
            static auto ruby_format = std::make_shared<RegexSyntaxHighlighter>(RE_RUBY_FORMAT, TextKind::Placeholder);
            all->Add(ruby_format);
        }
    }

    // basic highlighting has highest priority, so should come last in the order:
    if (kindsMask & (LeadingWhitespace | Escape))
        all->Add(basic);

    return all;
}
<commit_msg>Detect placeholders in only plural strings too<commit_after>/*
 *  This file is part of Poedit (https://poedit.net)
 *
 *  Copyright (C) 2014-2022 Vaclav Slavik
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *  DEALINGS IN THE SOFTWARE.
 *
 */

#include "syntaxhighlighter.h"

#include "catalog.h"
#include "str_helpers.h"

#include <unicode/uchar.h>

#include <regex>

namespace
{

class BasicSyntaxHighlighter : public SyntaxHighlighter
{
public:
    void Highlight(const std::wstring& s, const CallbackType& highlight) override
    {
        if (s.empty())
            return;

        const int length = int(s.length());

        // Leading whitespace:
        for (auto i = s.begin(); i != s.end(); ++i)
        {
            if (!u_isblank(*i))
            {
                int wlen = int(i - s.begin());
                if (wlen)
                    highlight(0, wlen, LeadingWhitespace);
                break;
            }
        }

        // Trailing whitespace:
        for (auto i = s.rbegin(); i != s.rend(); ++i)
        {
            if (!u_isblank(*i))
            {
                int wlen = int(i - s.rbegin());
                if (wlen)
                    highlight(length - wlen, length, LeadingWhitespace);
                break;
            }
        }

        int blank_block_pos = -1;

        for (auto i = s.begin(); i != s.end(); ++i)
        {
            // Some special whitespace characters should always be highlighted:
            if (*i == 0x00A0 /*non-breakable space*/)
            {
                int pos = int(i - s.begin());
                highlight(pos, pos + 1, LeadingWhitespace);
            }

            // Duplicate whitespace (2+ spaces etc.):
            else if (u_isblank(*i))
            {
                if (blank_block_pos == -1)
                    blank_block_pos = int(i - s.begin());
            }
            else if (blank_block_pos != -1)
            {
                int endpos = int(i - s.begin());
                if (endpos - blank_block_pos >= 2)
                    highlight(blank_block_pos, endpos, LeadingWhitespace);
                blank_block_pos = -1;
            }

            // Escape sequences:
            if (*i == '\\')
            {
                int pos = int(i - s.begin());
                if (++i == s.end())
                    break;
                // Note: this must match AnyTranslatableTextCtrl::EscapePlainText()
                switch (*i)
                {
                    case '0':
                    case 'a':
                    case 'b':
                    case 'f':
                    case 'n':
                    case 'r':
                    case 't':
                    case 'v':
                    case '\\':
                        highlight(pos, pos + 2, Escape);
                        break;
                    default:
                        break;
                }
            }
        }
    }
};

/// Highlighter that runs multiple sub-highlighters
class CompositeSyntaxHighlighter : public SyntaxHighlighter
{
public:
    void Add(std::shared_ptr<SyntaxHighlighter> h) { m_sub.push_back(h); }

    void Highlight(const std::wstring& s, const CallbackType& highlight) override
    {
        for (auto h : m_sub)
            h->Highlight(s, highlight);
    }

private:
    std::vector<std::shared_ptr<SyntaxHighlighter>> m_sub;
};

/// Match regular expressions for highlighting
class RegexSyntaxHighlighter : public SyntaxHighlighter
{
public:
    /// Ctor. Notice that @a re is a reference and must outlive the highlighter!
    RegexSyntaxHighlighter(std::wregex& re, TextKind kind) : m_re(re), m_kind(kind) {}

    void Highlight(const std::wstring& s, const CallbackType& highlight) override
    {
        try
        {
            std::wsregex_iterator next(s.begin(), s.end(), m_re);
            std::wsregex_iterator end;
            while (next != end)
            {
                auto match = *next++;
                if (match.empty())
                    continue;
                int pos = static_cast<int>(match.position());
                highlight(pos, pos + static_cast<int>(match.length()), m_kind);
            }
        }
        catch (std::regex_error& e)
        {
            switch (e.code())
            {
                case std::regex_constants::error_complexity:
                case std::regex_constants::error_stack:
                    // MSVC version of std::regex in particular can fail to match
                    // e.g. HTML regex with backreferences on insanely large strings;
                    // in that case, don't highlight instead of failing outright.
                    return;
                default:
                    throw;
            }
        }
    }

private:
    std::wregex& m_re;
    TextKind m_kind;
};

std::wregex RE_HTML_MARKUP(LR"((<\/?[a-zA-Z0-9:-]+(\s+[-:\w]+(=([-:\w+]|"[^"]*"|'[^']*'))?)*\s*\/?>)|(&[^ ;]+;))",
                           std::regex_constants::ECMAScript | std::regex_constants::optimize);

// php-format per http://php.net/manual/en/function.sprintf.php plus positionals
std::wregex RE_PHP_FORMAT(LR"(%(\d+\$)?[-+]{0,2}([ 0]|'.)?-?\d*(\..?\d+)?[%bcdeEfFgGosuxX])",
                          std::regex_constants::ECMAScript | std::regex_constants::optimize);

// c-format per http://en.cppreference.com/w/cpp/io/c/fprintf,
//              http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html
std::wregex RE_C_FORMAT(LR"(%(\d+\$)?[-+ #0]{0,5}(\d+|\*)?(\.(\d+|\*))?(hh|ll|[hljztL])?[%csdioxXufFeEaAgGnp])",
                        std::regex_constants::ECMAScript | std::regex_constants::optimize);

// python-format old style https://docs.python.org/2/library/stdtypes.html#string-formatting
//               new style https://docs.python.org/3/library/string.html#format-string-syntax
std::wregex RE_PYTHON_FORMAT(LR"((%(\(\w+\))?[-+ #0]?(\d+|\*)?(\.(\d+|\*))?[hlL]?[diouxXeEfFgGcrs%]))" // old style
                             "|"
                             LR"((\{([^{}])*\}))", // new style, being permissive
                             std::regex_constants::ECMAScript | std::regex_constants::optimize);

// ruby-format per https://ruby-doc.org/core-2.7.1/Kernel.html#method-i-sprintf
std::wregex RE_RUBY_FORMAT(LR"(%(\d+\$)?[-+ #0]{0,5}(\d+|\*)?(\.(\d+|\*))?(hh|ll|[hljztL])?[%csdioxXufFeEaAgGnp])",
                           std::regex_constants::ECMAScript | std::regex_constants::optimize);

// variables expansion for various template languages
std::wregex RE_COMMON_PLACEHOLDERS(
                    //
                    //           |             |               |
                    LR"(%[\w.-]+%|%?\{[\w.-]+\}|\{\{[\w.-]+\}\}|[@%:][\w-]+)",
                    //      |    |      |      |        |      |      |
                    //      |           |               |             +--------- Drupal: non-terminated @var, %var or :var
                    //      |           |               |                        !MUST be last in regex because of lack of termination character!
                    //      |           |               |
                    //      |           |               +----------------------- {{var}}
                    //      |           |
                    //      |           +--------------------------------------- %{var} (Ruby) and {var}
                    //      |
                    //      +--------------------------------------------------- %var% (Twig)
                    //
                    std::regex_constants::ECMAScript | std::regex_constants::optimize);

} // anonymous namespace

SyntaxHighlighterPtr SyntaxHighlighter::ForItem(const CatalogItem& item, int kindsMask)
{
    auto formatFlag = item.GetFormatFlag();
    bool needsHTML = (kindsMask & Markup);
    if (needsHTML)
    {
        needsHTML = std::regex_search(str::to_wstring(item.GetString()), RE_HTML_MARKUP) ||
                (item.HasPlural() && std::regex_search(str::to_wstring(item.GetPluralString()), RE_HTML_MARKUP));
    }
    bool needsGenericPlaceholders = (kindsMask & Placeholder);
    if (needsGenericPlaceholders)
    {
        needsGenericPlaceholders = std::regex_search(str::to_wstring(item.GetString()), RE_COMMON_PLACEHOLDERS) ||
                (item.HasPlural() && std::regex_search(str::to_wstring(item.GetPluralString()), RE_COMMON_PLACEHOLDERS));
    }

    static auto basic = std::make_shared<BasicSyntaxHighlighter>();
    if (!needsHTML && !needsGenericPlaceholders && formatFlag.empty())
    {
        if (kindsMask & (LeadingWhitespace | Escape))
            return basic;
        else
            return nullptr;
    }

    auto all = std::make_shared<CompositeSyntaxHighlighter>();

    // HTML goes first, has lowest priority than special-purpose stuff like format strings:
    if (needsHTML)
    {
        static auto html = std::make_shared<RegexSyntaxHighlighter>(RE_HTML_MARKUP, TextKind::Markup);
        all->Add(html);
    }

    if (needsGenericPlaceholders)
    {
        // If no format specified, heuristically apply highlighting of common variable markers
        static auto placeholders = std::make_shared<RegexSyntaxHighlighter>(RE_COMMON_PLACEHOLDERS, TextKind::Placeholder);
        all->Add(placeholders);
    }

    if (kindsMask & Placeholder)
    {
        // TODO: more/all languages
        if (formatFlag == "php")
        {
            static auto php_format = std::make_shared<RegexSyntaxHighlighter>(RE_PHP_FORMAT, TextKind::Placeholder);
            all->Add(php_format);
        }
        else if (formatFlag == "c")
        {
            static auto c_format = std::make_shared<RegexSyntaxHighlighter>(RE_C_FORMAT, TextKind::Placeholder);
            all->Add(c_format);
        }
        else if (formatFlag == "python")
        {
            static auto python_format = std::make_shared<RegexSyntaxHighlighter>(RE_PYTHON_FORMAT, TextKind::Placeholder);
            all->Add(python_format);
        }
        else if (formatFlag == "ruby")
        {
            static auto ruby_format = std::make_shared<RegexSyntaxHighlighter>(RE_RUBY_FORMAT, TextKind::Placeholder);
            all->Add(ruby_format);
        }
    }

    // basic highlighting has highest priority, so should come last in the order:
    if (kindsMask & (LeadingWhitespace | Escape))
        all->Add(basic);

    return all;
}
<|endoftext|>