Introduce TDEStringMatcher, a general purpose iterative string
matching class. Additional information about the class can be found in tdecore/README.tdestringmatcher. Introduce concept of a "hidden file matcher" instance of the TDEStringMatcher class that can be used to extend the definition "hidden" files beyond traditional "dotfiles". Create a global default instance of a "hidden file matcher" for use in applications that don't need their own. Modify KFileItem class to utilize a hidden file matcher when evaluating whether or not a filesystem object is hidden. This commit partially addresses issue # 273. Additional changes to tdelibs, tdebase/libkonq, and tdebase/konqueror will be required to resolve that issue. Signed-off-by: Vincent Reher <tde@4reher.org>pull/179/head
parent
682b8acae4
commit
78905ffa6a
@ -0,0 +1,63 @@
|
||||
The TDEStringMatcher class provides string matching against a list of
|
||||
one or more TQRegExp objects. The matching functions currently provided
|
||||
are:
|
||||
|
||||
matchAny(): strings match if they match any TQRegExp object in list.
|
||||
matchAll(): strings match only if the match all TQRegExp objects in list.
|
||||
|
||||
The list of TQRegExp objects is constructed using a specially formatted string
|
||||
that is passed by applications via the generatePatternList() function. This
|
||||
string is referred to as the "patternString" and is formatted as follows:
|
||||
|
||||
* The first character of the patternString defines the character that
|
||||
is used to split the remainder of the string into match specification
|
||||
strings. It is recommended (but not required) that this be a character
|
||||
that does not occur in a match pattern (see below).
|
||||
|
||||
* Each match specification string consists of an initial character that
|
||||
designates the match specification type followed by the corresponding
|
||||
match specification itself.
|
||||
|
||||
* Match specification strings starting with 'o' are followed by zero or
|
||||
or more letters, each of which are interpreted as setting or unsetting
|
||||
a match option. Match options remain in effect until explicitly overridden
|
||||
by subsequent match options. The match option letters that are currently
|
||||
recognized and processed are:
|
||||
|
||||
'w' - Match patterns are to be interpreted as "wildcards"
|
||||
'r' - Match patterns are to be interpreted as "regexes" (TQRegExp default)
|
||||
'c' - Matching will be case-sensitive (TQRegExp default)
|
||||
'c' - Matching will be case-INsensitive
|
||||
'm' - Regex matching will be "minimal" versus "greedy"
|
||||
'g' - Regex matching will be "greedy" (TQRegExp default)
|
||||
|
||||
* Match specification strings starting with 'p' are followed by one or
|
||||
more characters that constitute a match pattern. Each match pattern
|
||||
together with the match options in effect are used to construct
|
||||
a single TQRegExp object to be used for string matching. This object
|
||||
will be validated before acceptance.
|
||||
|
||||
* Match specification strings starting with any other character are ignored.
|
||||
|
||||
Some examples of patternString:
|
||||
|
||||
* |ow|p.*|p*~
|
||||
Dotfiles and kwrite backup files will be matched via wildcards
|
||||
|
||||
* /or/p[.][0-9]+$/ow/p.*
|
||||
Files with a version number suffix will be matched via regex
|
||||
and dotfiles will be matched via wildcard
|
||||
|
||||
Current and potential use of the TDEStringMatcher class include:
|
||||
|
||||
* Expansion of definition of "hidden" files, addressing issue # 270.
|
||||
|
||||
* Creation of a subclass that provides string parsing functions.
|
||||
|
||||
Miscellaneous implementation notes:
|
||||
|
||||
* It is the responsibility of applications that use this class to provide
|
||||
patternString editing (perhaps via UI), storage, and retrieval.
|
||||
|
||||
* Regex patterns use TQRegExp::search for matching but wildcard patterns
|
||||
must use TQRegExp::exactMatch in order for matching to work correctly.
|
@ -0,0 +1,169 @@
|
||||
#include "tdestringmatcher.h"
|
||||
|
||||
#include <tqregexp.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
class TDEStringMatcher::TDEStringMatcherPrivate {
|
||||
public:
|
||||
TQString patternString;
|
||||
}; // FIXME: This may be too small to warrant a private class :\
|
||||
|
||||
TDEStringMatcher::TDEStringMatcher()
|
||||
{
|
||||
p = new TDEStringMatcherPrivate;
|
||||
TSMTRACE << "TDEStringMatcher::TDEStringMatcher: New instance created: " << this << endl;
|
||||
}
|
||||
|
||||
TDEStringMatcher::~TDEStringMatcher()
|
||||
{
|
||||
patternList.setAutoDelete( true );
|
||||
patternList.clear();
|
||||
delete p;
|
||||
TSMTRACE << "TDEStringMatcher::TDEStringMatcher: Instance destroyed: " << this << endl;
|
||||
}
|
||||
|
||||
TQString TDEStringMatcher::getPatternString()
|
||||
{
|
||||
return p->patternString;
|
||||
}
|
||||
|
||||
bool TDEStringMatcher::generatePatternList( TQString newPatternString )
|
||||
{
|
||||
if ( newPatternString == p->patternString )
|
||||
return true;
|
||||
TSMTRACE << "TDEStringMatcher::generatePatternList: Proposed pattern string: <" << newPatternString << ">" << endl;
|
||||
if ( newPatternString.length() < 2 ) {
|
||||
TSMTRACE << " Input string too short to be interpreted, patterns will be cleared" << endl;
|
||||
patternList.clear();
|
||||
p->patternString = "" ;
|
||||
#ifdef TSMSIGNALS
|
||||
emit patternsChanged();
|
||||
#endif // TSMSIGNALS
|
||||
return true;
|
||||
}
|
||||
TQChar patternStringDivider = newPatternString[0];
|
||||
TSMTRACE << " patternStringDivider = '" << patternStringDivider << "'" << endl;
|
||||
TQStringList specList = TQStringList::split( patternStringDivider, newPatternString.mid(1), true );
|
||||
|
||||
TQRegExp rxWork;
|
||||
TQPtrList<TQRegExp> rxPatternList;
|
||||
|
||||
for ( const TQString &specification : specList ) {
|
||||
TSMTRACE << " Processing specification string: '" << specification << "'" << endl;
|
||||
TQChar specificationType = specification[0].lower();
|
||||
switch ( specificationType ) {
|
||||
case 'o' : {
|
||||
TQString optionString = specification.mid(1).lower();
|
||||
TSMTRACE << " Processing match option string: '" << optionString << "'" << endl;
|
||||
for ( int i = 0 ; i < optionString.length() ; i++ ) {
|
||||
TQChar optionChar = optionString[i];
|
||||
TSMTRACE << " Option character: '" << optionChar << "'" << endl;
|
||||
switch ( optionChar ) {
|
||||
case 'w' : rxWork.setWildcard( true ); break;
|
||||
case 'r' : rxWork.setWildcard( false ); break;
|
||||
case 'c' : rxWork.setCaseSensitive( true ); break;
|
||||
case 'i' : rxWork.setCaseSensitive( false ); break;
|
||||
case 'm' : rxWork.setMinimal( true ); break;
|
||||
case 'g' : rxWork.setMinimal( false ); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
TSMTRACE << " Wildcard/CaseSensitive settings: " << rxWork.wildcard() << "/" << rxWork.caseSensitive() << endl;
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case 'p' : {
|
||||
TQString pattern = specification.mid(1);
|
||||
TSMTRACE << " Processing match pattern: '" << pattern << "'" << endl;
|
||||
if ( pattern.isEmpty() ) {
|
||||
TSMTRACE << " Empty patterns are not allowed" << endl;
|
||||
rxPatternList.clear();
|
||||
return false;
|
||||
}
|
||||
rxWork.setPattern( pattern );
|
||||
if (! rxWork.isValid() ) {
|
||||
TSMTRACE << " Invalid pattern" << endl;
|
||||
rxPatternList.clear();
|
||||
return false;
|
||||
}
|
||||
TQRegExp *rxPattern = new TQRegExp( rxWork );
|
||||
rxPatternList.append( rxPattern );
|
||||
}
|
||||
break;
|
||||
|
||||
default :
|
||||
TSMTRACE << " Ignoring unknown specification type '" << specificationType << "'" << endl;
|
||||
//-Relax, don't overreact: rxPatternList.clear();
|
||||
//-Relax, don't overreact: return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// patternList.clear(); // no need to do this?
|
||||
patternList.setAutoDelete( true );
|
||||
patternList = rxPatternList;
|
||||
p->patternString = newPatternString;
|
||||
// rxPatternList.clear(); // no need to do this?
|
||||
|
||||
TSMTRACE << " Final patternString: '" << p->patternString << "'" << endl;
|
||||
TSMTRACE << " Number of regex match patterns in list: '" << patternList.count() << "'" << endl;
|
||||
|
||||
#ifdef TSMSIGNALS
|
||||
TSMTRACE << " Notifying slots of pattern change" << endl;
|
||||
emit patternsChanged();
|
||||
TSMTRACE << " All slots have been notified" << endl;
|
||||
#endif // TSMSIGNALS
|
||||
TSMTRACE << "TDEStringMatcher::generatePatternList: Patterns were successfully regenerated" << endl << endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TDEStringMatcher::matchAny( const TQString& stringToMatch )
|
||||
{
|
||||
//-Debug: TSMTRACE << "Attempting to match string '" << stringToMatch << "' against stored patterns" << endl;
|
||||
for ( const TQRegExp *rxPattern : patternList ) {
|
||||
if (
|
||||
( rxPattern->wildcard() && rxPattern->exactMatch( stringToMatch ) ) ||
|
||||
( ! rxPattern->wildcard() && rxPattern->search( stringToMatch ) >= 0)
|
||||
)
|
||||
{
|
||||
//-Debug: TSMTRACE << "String matched pattern: '" << rxPattern->pattern() << "'" << endl;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( patternList.isEmpty() ) {
|
||||
//-Debug: TSMTRACE << "Match failed on empty pattern list!" << endl;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
//-Debug: TSMTRACE << "Match failed, no pattern matched!" << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool TDEStringMatcher::matchAll( const TQString& stringToMatch )
|
||||
{
|
||||
//-Debug: TSMTRACE << "Attempting to match string '" << stringToMatch << "' against ALL stored patterns" << endl;
|
||||
for ( const TQRegExp *rxPattern : patternList ) {
|
||||
if ( !
|
||||
( rxPattern->wildcard() && rxPattern->exactMatch( stringToMatch ) ) ||
|
||||
( ! rxPattern->wildcard() && rxPattern->search( stringToMatch ) >= 0)
|
||||
)
|
||||
{
|
||||
//-Debug: TSMTRACE << "String failed to match pattern: '" << rxPattern->pattern() << "'" << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( patternList.isEmpty() ) {
|
||||
//-Debug: TSMTRACE << "Match failed on empty pattern list!" << endl;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
//-Debug: TSMTRACE << "Match succeeded, all patterns matched!" << endl;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#include "tdestringmatcher.moc"
|
@ -0,0 +1,60 @@
|
||||
#ifndef TDESTRINGMATCHER_H
|
||||
#define TDESTRINGMATCHER_H
|
||||
|
||||
#include "tdelibs_export.h"
|
||||
|
||||
#include <tqstring.h>
|
||||
#include <tqptrlist.h>
|
||||
#include <tqobject.h>
|
||||
|
||||
#define TSMTRACE kdDebug() << "<TSMTRACE> "
|
||||
#define TSMSIGNALS
|
||||
|
||||
/**
|
||||
*
|
||||
* Generic string matcher class.
|
||||
*/
|
||||
class TDECORE_EXPORT TDEStringMatcher : public TQObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
TDEStringMatcher();
|
||||
~TDEStringMatcher();
|
||||
|
||||
/**
|
||||
Use @param newPatternString to generate @property patternList. Refer to
|
||||
file README.tdestringmatcher for more information on how the input
|
||||
string should be formatted.
|
||||
*/
|
||||
bool generatePatternList( TQString newPatternString );
|
||||
|
||||
/**
|
||||
Return pattern string from which @property patternList was created.
|
||||
String is stored in @property TDEStringMatcherPrivate::patternString.
|
||||
*/
|
||||
TQString getPatternString();
|
||||
|
||||
/**
|
||||
Methods that determine whether or not @param stringToMatch match
|
||||
any/all of the TQRegExp objects contained in @property patternList.
|
||||
*/
|
||||
bool matchAny( const TQString& stringToMatch );
|
||||
bool matchAll( const TQString& stringToMatch );
|
||||
|
||||
signals:
|
||||
|
||||
void patternsChanged();
|
||||
|
||||
protected:
|
||||
|
||||
TQPtrList<TQRegExp> patternList;
|
||||
|
||||
private:
|
||||
|
||||
class TDEStringMatcherPrivate;
|
||||
TDEStringMatcherPrivate *p;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue