Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mpd
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Иван Мажукин
mpd
Commits
2cfccc1c
Commit
2cfccc1c
authored
Jul 25, 2018
by
Max Kellermann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
SongFilter: make Item an interface
Prepare to allow more complex expressions.
parent
438366ef
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
296 additions
and
186 deletions
+296
-186
SongFilter.cxx
src/SongFilter.cxx
+130
-79
SongFilter.hxx
src/SongFilter.hxx
+123
-62
DatabaseCommands.cxx
src/command/DatabaseCommands.cxx
+1
-1
ProxyDatabasePlugin.cxx
src/db/plugins/ProxyDatabasePlugin.cxx
+31
-27
UpnpDatabasePlugin.cxx
src/db/plugins/upnp/UpnpDatabasePlugin.cxx
+11
-17
No files found.
src/SongFilter.cxx
View file @
2cfccc1c
/*
/*
* Copyright 2003-201
7
The Music Player Daemon Project
* Copyright 2003-201
8
The Music Player Daemon Project
* http://www.musicpd.org
* http://www.musicpd.org
*
*
* This program is free software; you can redistribute it and/or modify
* This program is free software; you can redistribute it and/or modify
...
@@ -78,74 +78,76 @@ StringFilter::Match(const char *s) const noexcept
...
@@ -78,74 +78,76 @@ StringFilter::Match(const char *s) const noexcept
}
}
}
}
SongFilter
::
Item
::
Item
(
unsigned
_tag
,
std
::
string
&&
_value
,
bool
_fold_case
)
std
::
string
:
tag
(
_tag
),
string_filter
(
std
::
move
(
_value
),
_fold_case
)
UriSongFilter
::
ToExpression
()
const
noexcept
{
{
return
std
::
string
(
"("
LOCATE_TAG_FILE_KEY
" "
)
+
(
negated
?
"!="
:
"=="
)
+
"
\"
"
+
filter
.
GetValue
()
+
"
\"
)"
;
}
}
SongFilter
::
Item
::
Item
(
unsigned
_tag
,
bool
std
::
chrono
::
system_clock
::
time_point
_time
)
UriSongFilter
::
Match
(
const
LightSong
&
song
)
const
noexcept
:
tag
(
_tag
),
time
(
_time
)
{
{
return
filter
.
Match
(
song
.
GetURI
().
c_str
())
!=
negated
;
}
}
std
::
string
std
::
string
SongFilter
::
Item
::
ToExpression
()
const
noexcept
BaseSongFilter
::
ToExpression
()
const
noexcept
{
{
switch
(
tag
)
{
return
"(base
\"
"
+
value
+
"
\"
)"
;
case
LOCATE_TAG_FILE_TYPE
:
}
return
std
::
string
(
"("
LOCATE_TAG_FILE_KEY
" "
)
+
(
IsNegated
()
?
"!="
:
"=="
)
+
"
\"
"
+
string_filter
.
GetValue
()
+
"
\"
)"
;
case
LOCATE_TAG_BASE_TYPE
:
return
"(base
\"
"
+
string_filter
.
GetValue
()
+
"
\"
)"
;
case
LOCATE_TAG_MODIFIED_SINCE
:
bool
return
"(modified-since
\"
"
+
string_filter
.
GetValue
()
+
"
\"
)"
;
BaseSongFilter
::
Match
(
const
LightSong
&
song
)
const
noexcept
{
return
uri_is_child_or_same
(
value
.
c_str
(),
song
.
GetURI
().
c_str
());
}
case
LOCATE_TAG_ANY_TYPE
:
std
::
string
return
std
::
string
(
"("
LOCATE_TAG_ANY_KEY
" "
)
+
(
IsNegated
()
?
"!="
:
"=="
)
+
"
\"
"
+
string_filter
.
GetValue
()
+
"
\"
)"
;
TagSongFilter
::
ToExpression
()
const
noexcept
{
const
char
*
name
=
type
==
TAG_NUM_OF_ITEM_TYPES
?
LOCATE_TAG_ANY_KEY
:
tag_item_names
[
type
];
default
:
return
std
::
string
(
"("
)
+
name
+
" "
+
(
negated
?
"!="
:
"=="
)
+
"
\"
"
+
filter
.
GetValue
()
+
"
\"
)"
;
return
std
::
string
(
"("
)
+
tag_item_names
[
tag
]
+
" "
+
(
IsNegated
()
?
"!="
:
"=="
)
+
"
\"
"
+
string_filter
.
GetValue
()
+
"
\"
)"
;
}
}
}
bool
bool
SongFilter
::
Item
::
MatchNN
(
const
TagItem
&
item
)
const
noexcept
TagSongFilter
::
MatchNN
(
const
TagItem
&
item
)
const
noexcept
{
{
return
(
t
ag
==
LOCATE_TAG_ANY_TYPE
||
(
unsigned
)
item
.
type
==
tag
)
&&
return
(
t
ype
==
TAG_NUM_OF_ITEM_TYPES
||
item
.
type
==
type
)
&&
string_
filter
.
Match
(
item
.
value
);
filter
.
Match
(
item
.
value
);
}
}
bool
bool
SongFilter
::
Item
::
MatchNN
(
const
Tag
&
_
tag
)
const
noexcept
TagSongFilter
::
MatchNN
(
const
Tag
&
tag
)
const
noexcept
{
{
bool
visited_types
[
TAG_NUM_OF_ITEM_TYPES
];
bool
visited_types
[
TAG_NUM_OF_ITEM_TYPES
];
std
::
fill_n
(
visited_types
,
size_t
(
TAG_NUM_OF_ITEM_TYPES
),
false
);
std
::
fill_n
(
visited_types
,
size_t
(
TAG_NUM_OF_ITEM_TYPES
),
false
);
for
(
const
auto
&
i
:
_
tag
)
{
for
(
const
auto
&
i
:
tag
)
{
visited_types
[
i
.
type
]
=
true
;
visited_types
[
i
.
type
]
=
true
;
if
(
MatchNN
(
i
))
if
(
MatchNN
(
i
))
return
true
;
return
true
;
}
}
if
(
t
ag
<
TAG_NUM_OF_ITEM_TYPES
&&
!
visited_types
[
tag
])
{
if
(
t
ype
<
TAG_NUM_OF_ITEM_TYPES
&&
!
visited_types
[
type
])
{
/* If the search critieron was not visited during the
/* If the search critieron was not visited during the
sweep through the song's tag, it means this field
sweep through the song's tag, it means this field
is absent from the tag or empty. Thus, if the
is absent from the tag or empty. Thus, if the
searched string is also empty
searched string is also empty
then it's a match as well and we should return
then it's a match as well and we should return
true. */
true. */
if
(
string_
filter
.
empty
())
if
(
filter
.
empty
())
return
true
;
return
true
;
if
(
t
ag
==
TAG_ALBUM_ARTIST
&&
visited_types
[
TAG_ARTIST
])
{
if
(
t
ype
==
TAG_ALBUM_ARTIST
&&
visited_types
[
TAG_ARTIST
])
{
/* if we're looking for "album artist", but
/* if we're looking for "album artist", but
only "artist" exists, use that */
only "artist" exists, use that */
for
(
const
auto
&
item
:
_
tag
)
for
(
const
auto
&
item
:
tag
)
if
(
item
.
type
==
TAG_ARTIST
&&
if
(
item
.
type
==
TAG_ARTIST
&&
string_
filter
.
Match
(
item
.
value
))
filter
.
Match
(
item
.
value
))
return
true
;
return
true
;
}
}
}
}
...
@@ -154,28 +156,27 @@ SongFilter::Item::MatchNN(const Tag &_tag) const noexcept
...
@@ -154,28 +156,27 @@ SongFilter::Item::MatchNN(const Tag &_tag) const noexcept
}
}
bool
bool
SongFilter
::
Item
::
MatchNN
(
const
LightSong
&
song
)
const
noexcept
TagSongFilter
::
Match
(
const
LightSong
&
song
)
const
noexcept
{
{
if
(
tag
==
LOCATE_TAG_BASE_TYPE
)
{
return
MatchNN
(
song
.
tag
)
!=
negated
;
const
auto
uri
=
song
.
GetURI
();
}
return
uri_is_child_or_same
(
string_filter
.
GetValue
().
c_str
(),
uri
.
c_str
());
}
if
(
tag
==
LOCATE_TAG_MODIFIED_SINCE
)
return
song
.
mtime
>=
time
;
if
(
tag
==
LOCATE_TAG_FILE_TYPE
)
{
std
::
string
const
auto
uri
=
song
.
GetURI
();
ModifiedSinceSongFilter
::
ToExpression
()
const
noexcept
return
string_filter
.
Match
(
uri
.
c_str
());
{
}
return
std
::
string
(
"(modified-since
\"
"
)
+
FormatISO8601
(
value
).
c_str
()
+
"
\"
)"
;
}
return
MatchNN
(
song
.
tag
);
bool
ModifiedSinceSongFilter
::
Match
(
const
LightSong
&
song
)
const
noexcept
{
return
song
.
mtime
>=
value
;
}
}
SongFilter
::
SongFilter
(
unsigned
tag
,
const
char
*
value
,
bool
fold_case
)
SongFilter
::
SongFilter
(
TagType
tag
,
const
char
*
value
,
bool
fold_case
)
{
{
items
.
emplace_back
(
tag
,
value
,
fold_case
);
items
.
emplace_back
(
std
::
make_unique
<
TagSongFilter
>
(
tag
,
value
,
fold_case
,
false
));
}
}
SongFilter
::~
SongFilter
()
SongFilter
::~
SongFilter
()
...
@@ -190,14 +191,14 @@ SongFilter::ToExpression() const noexcept
...
@@ -190,14 +191,14 @@ SongFilter::ToExpression() const noexcept
const
auto
end
=
items
.
end
();
const
auto
end
=
items
.
end
();
if
(
std
::
next
(
i
)
==
end
)
if
(
std
::
next
(
i
)
==
end
)
return
i
->
ToExpression
();
return
(
*
i
)
->
ToExpression
();
std
::
string
e
(
"("
);
std
::
string
e
(
"("
);
e
+=
i
->
ToExpression
();
e
+=
(
*
i
)
->
ToExpression
();
for
(
++
i
;
i
!=
end
;
++
i
)
{
for
(
++
i
;
i
!=
end
;
++
i
)
{
e
+=
" AND "
;
e
+=
" AND "
;
e
+=
i
->
ToExpression
();
e
+=
(
*
i
)
->
ToExpression
();
}
}
e
.
push_back
(
')'
);
e
.
push_back
(
')'
);
...
@@ -273,8 +274,8 @@ ExpectQuoted(const char *&s)
...
@@ -273,8 +274,8 @@ ExpectQuoted(const char *&s)
return
{
begin
,
end
};
return
{
begin
,
end
};
}
}
const
char
*
ISongFilterPtr
SongFilter
::
ParseExpression
(
const
char
*
s
,
bool
fold_case
)
SongFilter
::
ParseExpression
(
const
char
*
&
s
,
bool
fold_case
)
{
{
assert
(
*
s
==
'('
);
assert
(
*
s
==
'('
);
...
@@ -283,21 +284,21 @@ SongFilter::ParseExpression(const char *s, bool fold_case)
...
@@ -283,21 +284,21 @@ SongFilter::ParseExpression(const char *s, bool fold_case)
if
(
*
s
==
'('
)
if
(
*
s
==
'('
)
throw
std
::
runtime_error
(
"Nested expressions not yet implemented"
);
throw
std
::
runtime_error
(
"Nested expressions not yet implemented"
);
const
auto
type
=
ExpectFilterType
(
s
);
auto
type
=
ExpectFilterType
(
s
);
if
(
type
==
LOCATE_TAG_MODIFIED_SINCE
)
{
if
(
type
==
LOCATE_TAG_MODIFIED_SINCE
)
{
const
auto
value_s
=
ExpectQuoted
(
s
);
const
auto
value_s
=
ExpectQuoted
(
s
);
if
(
*
s
!=
')'
)
if
(
*
s
!=
')'
)
throw
std
::
runtime_error
(
"')' expected"
);
throw
std
::
runtime_error
(
"')' expected"
);
items
.
emplace_back
(
type
,
ParseTimeStamp
(
value_s
.
c_str
())
);
s
=
StripLeft
(
s
+
1
);
return
StripLeft
(
s
+
1
);
return
std
::
make_unique
<
ModifiedSinceSongFilter
>
(
ParseTimeStamp
(
value_s
.
c_str
())
);
}
else
if
(
type
==
LOCATE_TAG_BASE_TYPE
)
{
}
else
if
(
type
==
LOCATE_TAG_BASE_TYPE
)
{
auto
value
=
ExpectQuoted
(
s
);
auto
value
=
ExpectQuoted
(
s
);
if
(
*
s
!=
')'
)
if
(
*
s
!=
')'
)
throw
std
::
runtime_error
(
"')' expected"
);
throw
std
::
runtime_error
(
"')' expected"
);
s
=
StripLeft
(
s
+
1
);
items
.
emplace_back
(
type
,
std
::
move
(
value
),
fold_case
);
return
std
::
make_unique
<
BaseSongFilter
>
(
std
::
move
(
value
));
return
StripLeft
(
s
+
1
);
}
else
{
}
else
{
bool
negated
=
false
;
bool
negated
=
false
;
if
(
s
[
0
]
==
'!'
&&
s
[
1
]
==
'='
)
if
(
s
[
0
]
==
'!'
&&
s
[
1
]
==
'='
)
...
@@ -310,9 +311,19 @@ SongFilter::ParseExpression(const char *s, bool fold_case)
...
@@ -310,9 +311,19 @@ SongFilter::ParseExpression(const char *s, bool fold_case)
if
(
*
s
!=
')'
)
if
(
*
s
!=
')'
)
throw
std
::
runtime_error
(
"')' expected"
);
throw
std
::
runtime_error
(
"')' expected"
);
items
.
emplace_back
(
type
,
std
::
move
(
value
),
fold_case
);
s
=
StripLeft
(
s
+
1
);
items
.
back
().
SetNegated
(
negated
);
return
StripLeft
(
s
+
1
);
if
(
type
==
LOCATE_TAG_ANY_TYPE
)
type
=
TAG_NUM_OF_ITEM_TYPES
;
if
(
type
==
LOCATE_TAG_FILE_TYPE
)
return
std
::
make_unique
<
UriSongFilter
>
(
std
::
move
(
value
),
fold_case
,
negated
);
return
std
::
make_unique
<
TagSongFilter
>
(
TagType
(
type
),
std
::
move
(
value
),
fold_case
,
negated
);
}
}
}
}
...
@@ -320,21 +331,38 @@ void
...
@@ -320,21 +331,38 @@ void
SongFilter
::
Parse
(
const
char
*
tag_string
,
const
char
*
value
,
bool
fold_case
)
SongFilter
::
Parse
(
const
char
*
tag_string
,
const
char
*
value
,
bool
fold_case
)
{
{
unsigned
tag
=
locate_parse_type
(
tag_string
);
unsigned
tag
=
locate_parse_type
(
tag_string
);
if
(
tag
==
TAG_NUM_OF_ITEM_TYPES
)
switch
(
tag
)
{
case
TAG_NUM_OF_ITEM_TYPES
:
throw
std
::
runtime_error
(
"Unknown filter type"
);
throw
std
::
runtime_error
(
"Unknown filter type"
);
if
(
tag
==
LOCATE_TAG_BASE_TYPE
)
{
case
LOCATE_TAG_BASE_TYPE
:
if
(
!
uri_safe_local
(
value
))
if
(
!
uri_safe_local
(
value
))
throw
std
::
runtime_error
(
"Bad URI"
);
throw
std
::
runtime_error
(
"Bad URI"
);
/* case folding doesn't work with "base" */
items
.
emplace_back
(
std
::
make_unique
<
BaseSongFilter
>
(
value
));
fold_case
=
false
;
break
;
}
case
LOCATE_TAG_MODIFIED_SINCE
:
items
.
emplace_back
(
std
::
make_unique
<
ModifiedSinceSongFilter
>
(
ParseTimeStamp
(
value
)));
break
;
if
(
tag
==
LOCATE_TAG_MODIFIED_SINCE
)
case
LOCATE_TAG_FILE_TYPE
:
items
.
emplace_back
(
tag
,
ParseTimeStamp
(
value
));
items
.
emplace_back
(
std
::
make_unique
<
UriSongFilter
>
(
value
,
else
fold_case
,
items
.
emplace_back
(
tag
,
value
,
fold_case
);
false
));
break
;
default
:
if
(
tag
==
LOCATE_TAG_ANY_TYPE
)
tag
=
TAG_NUM_OF_ITEM_TYPES
;
items
.
emplace_back
(
std
::
make_unique
<
TagSongFilter
>
(
TagType
(
tag
),
value
,
fold_case
,
false
));
break
;
}
}
}
void
void
...
@@ -346,10 +374,12 @@ SongFilter::Parse(ConstBuffer<const char *> args, bool fold_case)
...
@@ -346,10 +374,12 @@ SongFilter::Parse(ConstBuffer<const char *> args, bool fold_case)
do
{
do
{
if
(
*
args
.
front
()
==
'('
)
{
if
(
*
args
.
front
()
==
'('
)
{
const
char
*
s
=
args
.
shift
();
const
char
*
s
=
args
.
shift
();
const
char
*
end
=
ParseExpression
(
s
,
fold_case
);
const
char
*
end
=
s
;
auto
f
=
ParseExpression
(
end
,
fold_case
);
if
(
*
end
!=
0
)
if
(
*
end
!=
0
)
throw
std
::
runtime_error
(
"Unparsed garbage after expression"
);
throw
std
::
runtime_error
(
"Unparsed garbage after expression"
);
items
.
emplace_back
(
std
::
move
(
f
));
continue
;
continue
;
}
}
...
@@ -366,18 +396,36 @@ bool
...
@@ -366,18 +396,36 @@ bool
SongFilter
::
Match
(
const
LightSong
&
song
)
const
noexcept
SongFilter
::
Match
(
const
LightSong
&
song
)
const
noexcept
{
{
for
(
const
auto
&
i
:
items
)
for
(
const
auto
&
i
:
items
)
if
(
!
i
.
Match
(
song
))
if
(
!
i
->
Match
(
song
))
return
false
;
return
false
;
return
true
;
return
true
;
}
}
bool
bool
SongFilter
::
HasFoldCase
()
const
noexcept
{
for
(
const
auto
&
i
:
items
)
{
if
(
auto
t
=
dynamic_cast
<
const
TagSongFilter
*>
(
i
.
get
()))
{
if
(
t
->
GetFoldCase
())
return
true
;
}
else
if
(
auto
u
=
dynamic_cast
<
const
UriSongFilter
*>
(
i
.
get
()))
{
if
(
u
->
GetFoldCase
())
return
true
;
}
}
return
false
;
}
bool
SongFilter
::
HasOtherThanBase
()
const
noexcept
SongFilter
::
HasOtherThanBase
()
const
noexcept
{
{
for
(
const
auto
&
i
:
items
)
for
(
const
auto
&
i
:
items
)
{
if
(
i
.
GetTag
()
!=
LOCATE_TAG_BASE_TYPE
)
const
auto
*
f
=
dynamic_cast
<
const
BaseSongFilter
*>
(
i
.
get
());
if
(
f
==
nullptr
)
return
true
;
return
true
;
}
return
false
;
return
false
;
}
}
...
@@ -385,9 +433,11 @@ SongFilter::HasOtherThanBase() const noexcept
...
@@ -385,9 +433,11 @@ SongFilter::HasOtherThanBase() const noexcept
const
char
*
const
char
*
SongFilter
::
GetBase
()
const
noexcept
SongFilter
::
GetBase
()
const
noexcept
{
{
for
(
const
auto
&
i
:
items
)
for
(
const
auto
&
i
:
items
)
{
if
(
i
.
GetTag
()
==
LOCATE_TAG_BASE_TYPE
)
const
auto
*
f
=
dynamic_cast
<
const
BaseSongFilter
*>
(
i
.
get
());
return
i
.
GetValue
();
if
(
f
!=
nullptr
)
return
f
->
GetValue
();
}
return
nullptr
;
return
nullptr
;
}
}
...
@@ -399,8 +449,9 @@ SongFilter::WithoutBasePrefix(const char *_prefix) const noexcept
...
@@ -399,8 +449,9 @@ SongFilter::WithoutBasePrefix(const char *_prefix) const noexcept
SongFilter
result
;
SongFilter
result
;
for
(
const
auto
&
i
:
items
)
{
for
(
const
auto
&
i
:
items
)
{
if
(
i
.
GetTag
()
==
LOCATE_TAG_BASE_TYPE
)
{
const
auto
*
f
=
dynamic_cast
<
const
BaseSongFilter
*>
(
i
.
get
());
const
char
*
s
=
StringAfterPrefix
(
i
.
GetValue
(),
prefix
);
if
(
f
!=
nullptr
)
{
const
char
*
s
=
StringAfterPrefix
(
f
->
GetValue
(),
prefix
);
if
(
s
!=
nullptr
)
{
if
(
s
!=
nullptr
)
{
if
(
*
s
==
0
)
if
(
*
s
==
0
)
continue
;
continue
;
...
@@ -409,14 +460,14 @@ SongFilter::WithoutBasePrefix(const char *_prefix) const noexcept
...
@@ -409,14 +460,14 @@ SongFilter::WithoutBasePrefix(const char *_prefix) const noexcept
++
s
;
++
s
;
if
(
*
s
!=
0
)
if
(
*
s
!=
0
)
result
.
items
.
emplace_back
(
LOCATE_TAG_BASE_TYPE
,
s
);
result
.
items
.
emplace_back
(
std
::
make_unique
<
BaseSongFilter
>
(
s
)
);
continue
;
continue
;
}
}
}
}
}
}
result
.
items
.
emplace_back
(
i
);
result
.
items
.
emplace_back
(
i
->
Clone
()
);
}
}
return
result
;
return
result
;
...
...
src/SongFilter.hxx
View file @
2cfccc1c
/*
/*
* Copyright 2003-201
7
The Music Player Daemon Project
* Copyright 2003-201
8
The Music Player Daemon Project
* http://www.musicpd.org
* http://www.musicpd.org
*
*
* This program is free software; you can redistribute it and/or modify
* This program is free software; you can redistribute it and/or modify
...
@@ -23,10 +23,13 @@
...
@@ -23,10 +23,13 @@
#include "lib/icu/Compare.hxx"
#include "lib/icu/Compare.hxx"
#include "Compiler.h"
#include "Compiler.h"
#include <memory>
#include <string>
#include <string>
#include <list>
#include <list>
#include <chrono>
#include <chrono>
#include <stdint.h>
/**
/**
* Limit the search to files within the given directory.
* Limit the search to files within the given directory.
*/
*/
...
@@ -42,9 +45,28 @@
...
@@ -42,9 +45,28 @@
#define LOCATE_TAG_ANY_TYPE TAG_NUM_OF_ITEM_TYPES+20
#define LOCATE_TAG_ANY_TYPE TAG_NUM_OF_ITEM_TYPES+20
template
<
typename
T
>
struct
ConstBuffer
;
template
<
typename
T
>
struct
ConstBuffer
;
enum
TagType
:
uint8_t
;
struct
Tag
;
struct
Tag
;
struct
TagItem
;
struct
TagItem
;
struct
LightSong
;
struct
LightSong
;
class
ISongFilter
;
using
ISongFilterPtr
=
std
::
unique_ptr
<
ISongFilter
>
;
class
ISongFilter
{
public
:
virtual
~
ISongFilter
()
noexcept
{}
virtual
ISongFilterPtr
Clone
()
const
noexcept
=
0
;
/**
* Convert this object into an "expression". This is
* only useful for debugging.
*/
virtual
std
::
string
ToExpression
()
const
noexcept
=
0
;
gcc_pure
virtual
bool
Match
(
const
LightSong
&
song
)
const
noexcept
=
0
;
};
class
StringFilter
{
class
StringFilter
{
std
::
string
value
;
std
::
string
value
;
...
@@ -55,8 +77,6 @@ class StringFilter {
...
@@ -55,8 +77,6 @@ class StringFilter {
IcuCompare
fold_case
;
IcuCompare
fold_case
;
public
:
public
:
StringFilter
()
=
default
;
template
<
typename
V
>
template
<
typename
V
>
StringFilter
(
V
&&
_value
,
bool
_fold_case
)
StringFilter
(
V
&&
_value
,
bool
_fold_case
)
:
value
(
std
::
forward
<
V
>
(
_value
)),
:
value
(
std
::
forward
<
V
>
(
_value
)),
...
@@ -80,82 +100,129 @@ public:
...
@@ -80,82 +100,129 @@ public:
bool
Match
(
const
char
*
s
)
const
noexcept
;
bool
Match
(
const
char
*
s
)
const
noexcept
;
};
};
class
SongFilter
{
class
UriSongFilter
final
:
public
ISongFilter
{
StringFilter
filter
;
bool
negated
;
public
:
public
:
class
Item
{
template
<
typename
V
>
unsigned
tag
;
UriSongFilter
(
V
&&
_value
,
bool
fold_case
,
bool
_negated
)
:
filter
(
std
::
forward
<
V
>
(
_value
),
fold_case
),
negated
(
_negated
)
{}
const
auto
&
GetValue
()
const
noexcept
{
return
filter
.
GetValue
();
}
bool
GetFoldCase
()
const
{
return
filter
.
GetFoldCase
();
}
bool
IsNegated
()
const
noexcept
{
return
negated
;
}
ISongFilterPtr
Clone
()
const
noexcept
override
{
return
std
::
make_unique
<
UriSongFilter
>
(
*
this
);
}
std
::
string
ToExpression
()
const
noexcept
override
;
bool
Match
(
const
LightSong
&
song
)
const
noexcept
override
;
};
bool
negated
=
false
;
class
BaseSongFilter
final
:
public
ISongFilter
{
std
::
string
value
;
StringFilter
string_filter
;
public
:
BaseSongFilter
(
const
BaseSongFilter
&
)
=
default
;
/**
template
<
typename
V
>
* For #LOCATE_TAG_MODIFIED_SINCE
explicit
BaseSongFilter
(
V
&&
_value
)
*/
:
value
(
std
::
forward
<
V
>
(
_value
))
{}
std
::
chrono
::
system_clock
::
time_point
time
;
public
:
const
char
*
GetValue
()
const
noexcept
{
Item
(
unsigned
tag
,
std
::
string
&&
_value
,
bool
fold_case
=
false
);
return
value
.
c_str
(
);
Item
(
unsigned
tag
,
std
::
chrono
::
system_clock
::
time_point
time
);
}
/**
ISongFilterPtr
Clone
()
const
noexcept
override
{
* Convert this object into an "expression". This is
return
std
::
make_unique
<
BaseSongFilter
>
(
*
this
);
* only useful for debugging.
}
*/
std
::
string
ToExpression
()
const
noexcept
;
std
::
string
ToExpression
()
const
noexcept
override
;
bool
Match
(
const
LightSong
&
song
)
const
noexcept
override
;
};
unsigned
GetTag
()
const
{
class
TagSongFilter
final
:
public
ISongFilter
{
return
tag
;
TagType
type
;
}
bool
IsNegated
()
const
noexcept
{
bool
negated
;
return
negated
;
}
void
SetNegated
(
bool
_negated
=
true
)
noexcept
{
StringFilter
filter
;
negated
=
_negated
;
}
bool
GetFoldCase
()
const
{
public
:
return
string_filter
.
GetFoldCase
();
template
<
typename
V
>
}
TagSongFilter
(
TagType
_type
,
V
&&
_value
,
bool
fold_case
,
bool
_negated
)
:
type
(
_type
),
negated
(
_negated
),
filter
(
std
::
forward
<
V
>
(
_value
),
fold_case
)
{}
const
char
*
GetValu
e
()
const
{
TagType
GetTagTyp
e
()
const
{
return
string_filter
.
GetValue
().
c_str
()
;
return
type
;
}
}
private
:
const
auto
&
GetValue
()
const
noexcept
{
/* note: the "NN" suffix means "no negation", i.e. the
return
filter
.
GetValue
();
method pretends negation is unset, and the caller
}
is responsibly for considering it */
gcc_pure
bool
GetFoldCase
()
const
{
bool
MatchNN
(
const
TagItem
&
tag_item
)
const
noexcept
;
return
filter
.
GetFoldCase
();
}
gcc_pure
bool
IsNegated
()
const
noexcept
{
bool
MatchNN
(
const
Tag
&
tag
)
const
noexcept
;
return
negated
;
}
gcc_pure
ISongFilterPtr
Clone
()
const
noexcept
override
{
bool
MatchNN
(
const
LightSong
&
song
)
const
noexcept
;
return
std
::
make_unique
<
TagSongFilter
>
(
*
this
);
}
public
:
std
::
string
ToExpression
()
const
noexcept
override
;
gcc_pure
bool
Match
(
const
LightSong
&
song
)
const
noexcept
override
;
bool
Match
(
const
LightSong
&
song
)
const
noexcept
{
return
MatchNN
(
song
)
!=
IsNegated
();
}
};
private
:
private
:
std
::
list
<
Item
>
items
;
bool
MatchNN
(
const
Tag
&
tag
)
const
noexcept
;
bool
MatchNN
(
const
TagItem
&
tag
)
const
noexcept
;
};
class
ModifiedSinceSongFilter
final
:
public
ISongFilter
{
std
::
chrono
::
system_clock
::
time_point
value
;
public
:
explicit
ModifiedSinceSongFilter
(
std
::
chrono
::
system_clock
::
time_point
_value
)
noexcept
:
value
(
_value
)
{}
ISongFilterPtr
Clone
()
const
noexcept
override
{
return
std
::
make_unique
<
ModifiedSinceSongFilter
>
(
*
this
);
}
std
::
string
ToExpression
()
const
noexcept
override
;
bool
Match
(
const
LightSong
&
song
)
const
noexcept
override
;
};
class
SongFilter
{
std
::
list
<
ISongFilterPtr
>
items
;
public
:
public
:
SongFilter
()
=
default
;
SongFilter
()
=
default
;
gcc_nonnull
(
3
)
gcc_nonnull
(
3
)
SongFilter
(
unsigned
tag
,
const
char
*
value
,
bool
fold_case
=
false
);
SongFilter
(
TagType
tag
,
const
char
*
value
,
bool
fold_case
=
false
);
~
SongFilter
();
~
SongFilter
();
SongFilter
(
SongFilter
&&
)
=
default
;
SongFilter
&
operator
=
(
SongFilter
&&
)
=
default
;
/**
/**
* Convert this object into an "expression". This is
* Convert this object into an "expression". This is
* only useful for debugging.
* only useful for debugging.
...
@@ -163,7 +230,7 @@ public:
...
@@ -163,7 +230,7 @@ public:
std
::
string
ToExpression
()
const
noexcept
;
std
::
string
ToExpression
()
const
noexcept
;
private
:
private
:
const
char
*
ParseExpression
(
const
char
*
s
,
bool
fold_case
=
false
);
ISongFilterPtr
ParseExpression
(
const
char
*&
s
,
bool
fold_case
=
false
);
gcc_nonnull
(
2
,
3
)
gcc_nonnull
(
2
,
3
)
void
Parse
(
const
char
*
tag
,
const
char
*
value
,
bool
fold_case
=
false
);
void
Parse
(
const
char
*
tag
,
const
char
*
value
,
bool
fold_case
=
false
);
...
@@ -177,7 +244,7 @@ public:
...
@@ -177,7 +244,7 @@ public:
gcc_pure
gcc_pure
bool
Match
(
const
LightSong
&
song
)
const
noexcept
;
bool
Match
(
const
LightSong
&
song
)
const
noexcept
;
const
std
::
list
<
Item
>
&
GetItems
()
const
noexcept
{
const
auto
&
GetItems
()
const
noexcept
{
return
items
;
return
items
;
}
}
...
@@ -190,13 +257,7 @@ public:
...
@@ -190,13 +257,7 @@ public:
* Is there at least one item with "fold case" enabled?
* Is there at least one item with "fold case" enabled?
*/
*/
gcc_pure
gcc_pure
bool
HasFoldCase
()
const
noexcept
{
bool
HasFoldCase
()
const
noexcept
;
for
(
const
auto
&
i
:
items
)
if
(
i
.
GetFoldCase
())
return
true
;
return
false
;
}
/**
/**
* Does this filter contain constraints other than "base"?
* Does this filter contain constraints other than "base"?
...
...
src/command/DatabaseCommands.cxx
View file @
2cfccc1c
...
@@ -248,7 +248,7 @@ handle_list(Client &client, Request args, Response &r)
...
@@ -248,7 +248,7 @@ handle_list(Client &client, Request args, Response &r)
return
CommandResult
::
ERROR
;
return
CommandResult
::
ERROR
;
}
}
filter
.
reset
(
new
SongFilter
(
(
unsigned
)
TAG_ARTIST
,
filter
.
reset
(
new
SongFilter
(
TAG_ARTIST
,
args
.
shift
()));
args
.
shift
()));
}
}
...
...
src/db/plugins/ProxyDatabasePlugin.cxx
View file @
2cfccc1c
...
@@ -268,49 +268,53 @@ CheckError(struct mpd_connection *connection)
...
@@ -268,49 +268,53 @@ CheckError(struct mpd_connection *connection)
}
}
static
bool
static
bool
SendConstraints
(
mpd_connection
*
connection
,
const
SongFilter
::
Item
&
item
)
SendConstraints
(
mpd_connection
*
connection
,
const
ISongFilter
&
f
)
{
{
switch
(
item
.
GetTag
())
{
if
(
auto
t
=
dynamic_cast
<
const
TagSongFilter
*>
(
&
f
))
{
mpd_tag_type
tag
;
if
(
t
->
IsNegated
())
// TODO implement
#if LIBMPDCLIENT_CHECK_VERSION(2,9,0)
case
LOCATE_TAG_BASE_TYPE
:
if
(
mpd_connection_cmp_server_version
(
connection
,
0
,
18
,
0
)
<
0
)
/* requires MPD 0.18 */
return
true
;
return
true
;
return
mpd_search_add_base_constraint
(
connection
,
if
(
t
->
GetTagType
()
==
TAG_NUM_OF_ITEM_TYPES
)
MPD_OPERATOR_DEFAULT
,
return
mpd_search_add_any_tag_constraint
(
connection
,
item
.
GetValue
());
MPD_OPERATOR_DEFAULT
,
#endif
t
->
GetValue
().
c_str
());
case
LOCATE_TAG_FILE_TYPE
:
return
mpd_search_add_uri_constraint
(
connection
,
MPD_OPERATOR_DEFAULT
,
item
.
GetValue
());
case
LOCATE_TAG_ANY_TYPE
:
return
mpd_search_add_any_tag_constraint
(
connection
,
MPD_OPERATOR_DEFAULT
,
item
.
GetValue
());
default
:
const
auto
tag
=
Convert
(
t
->
GetTagType
());
tag
=
Convert
(
TagType
(
item
.
GetTag
()));
if
(
tag
==
MPD_TAG_COUNT
)
if
(
tag
==
MPD_TAG_COUNT
)
return
true
;
return
true
;
return
mpd_search_add_tag_constraint
(
connection
,
return
mpd_search_add_tag_constraint
(
connection
,
MPD_OPERATOR_DEFAULT
,
MPD_OPERATOR_DEFAULT
,
tag
,
tag
,
item
.
GetValue
());
t
->
GetValue
().
c_str
());
}
}
else
if
(
auto
u
=
dynamic_cast
<
const
UriSongFilter
*>
(
&
f
))
{
if
(
u
->
IsNegated
())
// TODO implement
return
true
;
return
mpd_search_add_uri_constraint
(
connection
,
MPD_OPERATOR_DEFAULT
,
u
->
GetValue
().
c_str
());
#if LIBMPDCLIENT_CHECK_VERSION(2,9,0)
}
else
if
(
auto
b
=
dynamic_cast
<
const
BaseSongFilter
*>
(
&
f
))
{
if
(
mpd_connection_cmp_server_version
(
connection
,
0
,
18
,
0
)
<
0
)
/* requires MPD 0.18 */
return
true
;
return
mpd_search_add_base_constraint
(
connection
,
MPD_OPERATOR_DEFAULT
,
b
->
GetValue
());
#endif
}
else
return
true
;
}
}
static
bool
static
bool
SendConstraints
(
mpd_connection
*
connection
,
const
SongFilter
&
filter
)
SendConstraints
(
mpd_connection
*
connection
,
const
SongFilter
&
filter
)
{
{
for
(
const
auto
&
i
:
filter
.
GetItems
())
for
(
const
auto
&
i
:
filter
.
GetItems
())
if
(
!
SendConstraints
(
connection
,
i
))
if
(
!
SendConstraints
(
connection
,
*
i
))
return
false
;
return
false
;
return
true
;
return
true
;
...
...
src/db/plugins/upnp/UpnpDatabasePlugin.cxx
View file @
2cfccc1c
...
@@ -254,9 +254,9 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server,
...
@@ -254,9 +254,9 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server,
std
::
string
cond
;
std
::
string
cond
;
for
(
const
auto
&
item
:
filter
->
GetItems
())
{
for
(
const
auto
&
item
:
filter
->
GetItems
())
{
switch
(
auto
tag
=
item
.
GetTag
(
))
{
if
(
auto
t
=
dynamic_cast
<
const
TagSongFilter
*>
(
item
.
get
()
))
{
case
LOCATE_TAG_ANY_TYPE
:
auto
tag
=
t
->
GetTagType
();
{
if
(
tag
==
TAG_NUM_OF_ITEM_TYPES
)
{
if
(
!
cond
.
empty
())
{
if
(
!
cond
.
empty
())
{
cond
+=
" and "
;
cond
+=
" and "
;
}
}
...
@@ -268,29 +268,21 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server,
...
@@ -268,29 +268,21 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server,
else
else
cond
+=
" or "
;
cond
+=
" or "
;
cond
+=
cap
;
cond
+=
cap
;
if
(
item
.
GetFoldCase
())
{
if
(
t
->
GetFoldCase
())
{
cond
+=
" contains "
;
cond
+=
" contains "
;
}
else
{
}
else
{
cond
+=
" = "
;
cond
+=
" = "
;
}
}
dquote
(
cond
,
item
.
GetValue
());
dquote
(
cond
,
t
->
GetValue
().
c_str
());
}
}
cond
+=
')'
;
cond
+=
')'
;
continue
;
}
}
break
;
default
:
/* Unhandled conditions like
LOCATE_TAG_BASE_TYPE or
LOCATE_TAG_FILE_TYPE won't have a
corresponding upnp prop, so they will be
skipped */
if
(
tag
==
TAG_ALBUM_ARTIST
)
if
(
tag
==
TAG_ALBUM_ARTIST
)
tag
=
TAG_ARTIST
;
tag
=
TAG_ARTIST
;
// TODO: support LOCATE_TAG_ANY_TYPE etc.
const
char
*
name
=
tag_table_lookup
(
upnp_tags
,
tag
);
const
char
*
name
=
tag_table_lookup
(
upnp_tags
,
TagType
(
tag
));
if
(
name
==
nullptr
)
if
(
name
==
nullptr
)
continue
;
continue
;
...
@@ -304,13 +296,15 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server,
...
@@ -304,13 +296,15 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server,
case-insensitive, but at least some servers
case-insensitive, but at least some servers
have the same convention as mpd (e.g.:
have the same convention as mpd (e.g.:
minidlna) */
minidlna) */
if
(
item
.
GetFoldCase
())
{
if
(
t
->
GetFoldCase
())
{
cond
+=
" contains "
;
cond
+=
" contains "
;
}
else
{
}
else
{
cond
+=
" = "
;
cond
+=
" = "
;
}
}
dquote
(
cond
,
item
.
GetValue
());
dquote
(
cond
,
t
->
GetValue
().
c_str
());
}
}
// TODO: support other ISongFilter implementations
}
}
return
server
.
search
(
handle
,
objid
,
cond
.
c_str
());
return
server
.
search
(
handle
,
objid
,
cond
.
c_str
());
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment