1 // © 2018 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
11 const char* PathFilter::kEInclusionNames
[] = {
18 ResKeyPath::ResKeyPath() {}
20 ResKeyPath::ResKeyPath(const std::string
& path
, UErrorCode
& status
) {
21 if (path
.empty() || path
[0] != '/') {
22 std::cerr
<< "genrb error: path must start with /: " << path
<< std::endl
;
23 status
= U_PARSE_ERROR
;
30 j
= path
.find('/', i
);
31 std::string key
= path
.substr(i
, j
- i
);
33 std::cerr
<< "genrb error: empty subpaths and trailing slashes are not allowed: " << path
<< std::endl
;
34 status
= U_PARSE_ERROR
;
38 if (j
== std::string::npos
) {
44 void ResKeyPath::push(const std::string
& key
) {
48 void ResKeyPath::pop() {
52 const std::list
<std::string
>& ResKeyPath::pieces() const {
56 std::ostream
& operator<<(std::ostream
& out
, const ResKeyPath
& value
) {
57 if (value
.pieces().empty()) {
59 } else for (auto& key
: value
.pieces()) {
66 PathFilter::~PathFilter() = default;
69 void SimpleRuleBasedPathFilter::addRule(const std::string
& ruleLine
, UErrorCode
& status
) {
70 if (ruleLine
.empty()) {
71 std::cerr
<< "genrb error: empty filter rules are not allowed" << std::endl
;
72 status
= U_PARSE_ERROR
;
75 bool inclusionRule
= false;
76 if (ruleLine
[0] == '+') {
78 } else if (ruleLine
[0] != '-') {
79 std::cerr
<< "genrb error: rules must start with + or -: " << ruleLine
<< std::endl
;
80 status
= U_PARSE_ERROR
;
83 ResKeyPath
path(ruleLine
.substr(1), status
);
84 addRule(path
, inclusionRule
, status
);
87 void SimpleRuleBasedPathFilter::addRule(const ResKeyPath
& path
, bool inclusionRule
, UErrorCode
& status
) {
88 if (U_FAILURE(status
)) {
91 fRoot
.applyRule(path
, path
.pieces().begin(), inclusionRule
, status
);
94 PathFilter::EInclusion
SimpleRuleBasedPathFilter::match(const ResKeyPath
& path
) const {
95 const Tree
* node
= &fRoot
;
97 // defaultResult "bubbles up" the nearest "definite" inclusion/exclusion rule
98 EInclusion defaultResult
= INCLUDE
;
99 if (node
->fIncluded
!= PARTIAL
) {
100 // rules handled here: "+/" and "-/"
101 defaultResult
= node
->fIncluded
;
104 // isLeaf is whether the filter tree can provide no additional information
105 // even if additional subpaths are added to the given key
108 for (auto& key
: path
.pieces()) {
109 auto child
= node
->fChildren
.find(key
);
110 // Leaf case 1: input path descends outside the filter tree
111 if (child
== node
->fChildren
.end()) {
112 if (node
->fWildcard
) {
113 // A wildcard pattern is present; continue checking
114 node
= node
->fWildcard
.get();
120 node
= &child
->second
;
122 if (node
->fIncluded
!= PARTIAL
) {
123 defaultResult
= node
->fIncluded
;
127 // Leaf case 2: input path exactly matches a filter leaf
128 if (node
->isLeaf()) {
132 // Always return PARTIAL if we are not at a leaf
137 // If leaf node is PARTIAL, return the default
138 if (node
->fIncluded
== PARTIAL
) {
139 return defaultResult
;
142 return node
->fIncluded
;
146 SimpleRuleBasedPathFilter::Tree::Tree(const Tree
& other
)
147 : fIncluded(other
.fIncluded
), fChildren(other
.fChildren
) {
148 // Note: can't use the default copy assignment because of the std::unique_ptr
149 if (other
.fWildcard
) {
150 fWildcard
.reset(new Tree(*other
.fWildcard
));
154 bool SimpleRuleBasedPathFilter::Tree::isLeaf() const {
155 return fChildren
.empty() && !fWildcard
;
158 void SimpleRuleBasedPathFilter::Tree::applyRule(
159 const ResKeyPath
& path
,
160 std::list
<std::string
>::const_iterator it
,
162 UErrorCode
& status
) {
165 if (it
== path
.pieces().end()) {
166 if (isVerbose() && (fIncluded
!= PARTIAL
|| !isLeaf())) {
167 std::cout
<< "genrb info: rule on path " << path
168 << " overrides previous rules" << std::endl
;
170 fIncluded
= inclusionRule
? INCLUDE
: EXCLUDE
;
181 fWildcard
.reset(new Tree());
183 // Apply the rule to fWildcard and also to all existing children.
185 fWildcard
->applyRule(path
, it
, inclusionRule
, status
);
186 for (auto& child
: fChildren
) {
187 child
.second
.applyRule(path
, it
, inclusionRule
, status
);
192 // Case 2: Normal Key
193 auto search
= fChildren
.find(key
);
194 if (search
== fChildren
.end()) {
196 // Deep-copy the existing wildcard tree into the new key
197 search
= fChildren
.emplace(key
, Tree(*fWildcard
)).first
;
199 search
= fChildren
.emplace(key
, Tree()).first
;
203 search
->second
.applyRule(path
, it
, inclusionRule
, status
);
208 void SimpleRuleBasedPathFilter::Tree::print(std::ostream
& out
, int32_t indent
) const {
209 for (int32_t i
=0; i
<indent
; i
++) out
<< "\t";
210 out
<< "included: " << kEInclusionNames
[fIncluded
] << std::endl
;
211 for (auto& child
: fChildren
) {
212 for (int32_t i
=0; i
<indent
; i
++) out
<< "\t";
213 out
<< child
.first
<< ": {" << std::endl
;
214 child
.second
.print(out
, indent
+ 1);
215 for (int32_t i
=0; i
<indent
; i
++) out
<< "\t";
216 out
<< "}" << std::endl
;
219 for (int32_t i
=0; i
<indent
; i
++) out
<< "\t";
220 out
<< "* {" << std::endl
;
221 fWildcard
->print(out
, indent
+ 1);
222 for (int32_t i
=0; i
<indent
; i
++) out
<< "\t";
223 out
<< "}" << std::endl
;
227 void SimpleRuleBasedPathFilter::print(std::ostream
& out
) const {
228 out
<< "SimpleRuleBasedPathFilter {" << std::endl
;
230 out
<< "}" << std::endl
;
233 std::ostream
& operator<<(std::ostream
& out
, const SimpleRuleBasedPathFilter
& value
) {