ICU-10923 Adding wildcard resource matching.

This commit is contained in:
Shane F. Carr 2019-02-16 02:56:40 -08:00
parent 8db0321f54
commit 7791a58a83
6 changed files with 256 additions and 47 deletions

View file

@ -1234,39 +1234,113 @@ void NewResourceBundleTest::TestFilter() {
REQUIRE_SUCCESS(status);
assertEquals("alabama", alabama.getType(), URES_TABLE);
ResourceBundle alaska = alabama.get("alaska", status);
REQUIRE_SUCCESS(status);
assertEquals("alaska", alaska.getType(), URES_TABLE);
{
ResourceBundle alaska = alabama.get("alaska", status);
REQUIRE_SUCCESS(status);
assertEquals("alaska", alaska.getType(), URES_TABLE);
ResourceBundle arizona = alaska.get("arizona", status);
REQUIRE_SUCCESS(status);
assertEquals("arizona", arizona.getType(), URES_STRING);
{
ResourceBundle arizona = alaska.get("arizona", status);
REQUIRE_SUCCESS(status);
assertEquals("arizona", arizona.getType(), URES_STRING);
assertEquals("arizona", u"arkansas", arizona.getString(status));
REQUIRE_SUCCESS(status);
assertEquals("arizona", u"arkansas", arizona.getString(status));
REQUIRE_SUCCESS(status);
// Filter: california should not be included
ResourceBundle california = alaska.get("california", status);
REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR, status);
}
// Filter: california should not be included
ResourceBundle california = alaska.get("california", status);
REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR, status);
// Filter: connecticut should not be included
ResourceBundle connecticut = alabama.get("connecticut", status);
REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR, status);
// Filter: connecticut should not be included
ResourceBundle connecticut = alabama.get("connecticut", status);
REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR, status);
}
ResourceBundle fornia = rb.get("fornia", status);
REQUIRE_SUCCESS(status);
assertEquals("fornia", fornia.getType(), URES_TABLE);
ResourceBundle hawaii = fornia.get("hawaii", status);
REQUIRE_SUCCESS(status);
assertEquals("hawaii", hawaii.getType(), URES_STRING);
{
ResourceBundle hawaii = fornia.get("hawaii", status);
REQUIRE_SUCCESS(status);
assertEquals("hawaii", hawaii.getType(), URES_STRING);
assertEquals("hawaii", u"idaho", hawaii.getString(status));
REQUIRE_SUCCESS(status);
assertEquals("hawaii", u"idaho", hawaii.getString(status));
REQUIRE_SUCCESS(status);
// Filter: illinois should not be included
ResourceBundle illinois = fornia.get("illinois", status);
REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR, status);
}
// Filter: illinois should not be included
ResourceBundle illinois = fornia.get("illinois", status);
REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR, status);
ResourceBundle mississippi = rb.get("mississippi", status);
REQUIRE_SUCCESS(status);
assertEquals("mississippi", mississippi.getType(), URES_TABLE);
{
ResourceBundle louisiana = mississippi.get("louisiana", status);
REQUIRE_SUCCESS(status);
assertEquals("louisiana", louisiana.getType(), URES_TABLE);
{
ResourceBundle maine = louisiana.get("maine", status);
REQUIRE_SUCCESS(status);
assertEquals("maine", maine.getType(), URES_STRING);
assertEquals("maine", u"maryland", maine.getString(status));
REQUIRE_SUCCESS(status);
ResourceBundle iowa = louisiana.get("iowa", status);
REQUIRE_SUCCESS(status);
assertEquals("iowa", iowa.getType(), URES_STRING);
assertEquals("iowa", u"kansas", iowa.getString(status));
REQUIRE_SUCCESS(status);
// Filter: missouri should not be included
ResourceBundle missouri = louisiana.get("missouri", status);
REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR, status);
}
ResourceBundle michigan = mississippi.get("michigan", status);
REQUIRE_SUCCESS(status);
assertEquals("michigan", michigan.getType(), URES_TABLE);
{
ResourceBundle maine = michigan.get("maine", status);
REQUIRE_SUCCESS(status);
assertEquals("maine", maine.getType(), URES_STRING);
assertEquals("maine", u"minnesota", maine.getString(status));
REQUIRE_SUCCESS(status);
// Filter: iowa should not be included
ResourceBundle iowa = michigan.get("iowa", status);
REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR, status);
ResourceBundle missouri = michigan.get("missouri", status);
REQUIRE_SUCCESS(status);
assertEquals("missouri", missouri.getType(), URES_STRING);
assertEquals("missouri", u"nebraska", missouri.getString(status));
REQUIRE_SUCCESS(status);
}
ResourceBundle nevada = mississippi.get("nevada", status);
REQUIRE_SUCCESS(status);
assertEquals("nevada", nevada.getType(), URES_TABLE);
{
ResourceBundle maine = nevada.get("maine", status);
REQUIRE_SUCCESS(status);
assertEquals("maine", maine.getType(), URES_STRING);
assertEquals("maine", u"new-hampshire", maine.getString(status));
REQUIRE_SUCCESS(status);
// Filter: iowa should not be included
ResourceBundle iowa = nevada.get("iowa", status);
REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR, status);
// Filter: missouri should not be included
ResourceBundle missouri = nevada.get("missouri", status);
REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR, status);
}
}
}
//eof

View file

@ -4,3 +4,8 @@
-/alabama
+/alabama/alaska/arizona
-/fornia/illinois
-/mississippi
+/mississippi/michigan
+/mississippi/*/maine
-/mississippi/*/iowa
+/mississippi/louisiana/iowa

View file

@ -17,4 +17,21 @@ filtertest {
hawaii {"idaho"}
illinois {"indiana"}
}
mississippi {
louisiana {
maine {"maryland"}
iowa {"kansas"}
missouri {"montana"}
}
michigan {
maine {"minnesota"}
iowa {"kentucky"}
missouri {"nebraska"}
}
nevada {
maine {"new-hampshire"}
iowa {"new-mexico"}
missouri {"new-york"}
}
}
}

View file

@ -85,17 +85,10 @@ void SimpleRuleBasedPathFilter::addRule(const ResKeyPath& path, bool inclusionRu
if (U_FAILURE(status)) {
return;
}
Tree* node = &fRoot;
for (auto& key : path.pieces()) {
// note: operator[] auto-constructs default values
node = &node->fChildren[key];
}
if (isVerbose() && (node->fIncluded != PARTIAL || !node->fChildren.empty())) {
std::cout << "genrb info: rule on path " << path
<< " overrides previous rules" << std::endl;
}
node->fIncluded = inclusionRule ? INCLUDE : EXCLUDE;
node->fChildren.clear();
fRoot.applyRule(path, path.pieces().begin(), inclusionRule, status);
// DEBUG TIP: Enable the following line to view the inclusion tree:
//print(std::cout);
}
PathFilter::EInclusion SimpleRuleBasedPathFilter::match(const ResKeyPath& path) const {
@ -116,10 +109,16 @@ PathFilter::EInclusion SimpleRuleBasedPathFilter::match(const ResKeyPath& path)
auto child = node->fChildren.find(key);
// Leaf case 1: input path descends outside the filter tree
if (child == node->fChildren.end()) {
isLeaf = true;
break;
if (node->fWildcard) {
// A wildcard pattern is present; continue checking
node = node->fWildcard.get();
} else {
isLeaf = true;
break;
}
} else {
node = &child->second;
}
node = &child->second;
if (node->fIncluded != PARTIAL) {
defaultResult = node->fIncluded;
}
@ -143,6 +142,65 @@ PathFilter::EInclusion SimpleRuleBasedPathFilter::match(const ResKeyPath& path)
return node->fIncluded;
}
SimpleRuleBasedPathFilter::Tree::Tree(const Tree& other)
: fIncluded(other.fIncluded), fChildren(other.fChildren) {
// Note: can't use the default copy assignment because of the std::unique_ptr
if (other.fWildcard) {
fWildcard.reset(new Tree(*other.fWildcard));
}
}
void SimpleRuleBasedPathFilter::Tree::applyRule(
const ResKeyPath& path,
std::list<std::string>::const_iterator it,
bool inclusionRule,
UErrorCode& status) {
// Base Case
if (it == path.pieces().end()) {
if (isVerbose() && (fIncluded != PARTIAL || !fChildren.empty())) {
std::cout << "genrb info: rule on path " << path
<< " overrides previous rules" << std::endl;
}
fIncluded = inclusionRule ? INCLUDE : EXCLUDE;
fChildren.clear();
fWildcard.reset();
return;
}
// Recursive Step
auto& key = *it;
if (key == "*") {
// Case 1: Wildcard
if (!fWildcard) {
fWildcard.reset(new Tree());
}
// Apply the rule to fWildcard and also to all existing children.
it++;
fWildcard->applyRule(path, it, inclusionRule, status);
for (auto& child : fChildren) {
child.second.applyRule(path, it, inclusionRule, status);
}
it--;
} else {
// Case 2: Normal Key
auto search = fChildren.find(key);
if (search == fChildren.end()) {
if (fWildcard) {
// Deep-copy the existing wildcard tree into the new key
search = fChildren.emplace(key, Tree(*fWildcard)).first;
} else {
search = fChildren.emplace(key, Tree()).first;
}
}
it++;
search->second.applyRule(path, it, inclusionRule, status);
it--;
}
}
void SimpleRuleBasedPathFilter::Tree::print(std::ostream& out, int32_t indent) const {
for (int32_t i=0; i<indent; i++) out << "\t";
out << "included: " << kEInclusionNames[fIncluded] << std::endl;
@ -153,6 +211,13 @@ void SimpleRuleBasedPathFilter::Tree::print(std::ostream& out, int32_t indent) c
for (int32_t i=0; i<indent; i++) out << "\t";
out << "}" << std::endl;
}
if (fWildcard) {
for (int32_t i=0; i<indent; i++) out << "\t";
out << "* {" << std::endl;
fWildcard->print(out, indent + 1);
for (int32_t i=0; i<indent; i++) out << "\t";
out << "}" << std::endl;
}
}
void SimpleRuleBasedPathFilter::print(std::ostream& out) const {

View file

@ -5,9 +5,10 @@
#define __FILTERRB_H__
#include <list>
#include <string>
#include <map>
#include <memory>
#include <ostream>
#include <string>
#include "unicode/utypes.h"
@ -64,12 +65,20 @@ public:
/**
* Implementation of PathFilter for a list of inclusion/exclusion rules.
*
* The wildcard pattern "*" means that the subsequent filters are applied to
* every other tree sharing the same parent.
*
* For example, given this list of filter rules:
*
* -/alabama
* +/alabama/alaska/arizona
* -/fornia/hawaii
*
*/
// -/alabama
// +/alabama/alaska/arizona
// -/fornia/hawaii
// -/mississippi
// +/mississippi/michigan
// +/mississippi/*/maine
// -/mississippi/*/iowa
// +/mississippi/louisiana/iowa
/*
* You get the following structure:
*
* SimpleRuleBasedPathFilter {
@ -89,6 +98,36 @@ public:
* included: EXCLUDE
* }
* }
* mississippi: {
* included: EXCLUDE
* louisiana: {
* included: PARTIAL
* iowa: {
* included: INCLUDE
* }
* maine: {
* included: INCLUDE
* }
* }
* michigan: {
* included: INCLUDE
* iowa: {
* included: EXCLUDE
* }
* maine: {
* included: INCLUDE
* }
* }
* * {
* included: PARTIAL
* iowa: {
* included: EXCLUDE
* }
* maine: {
* included: INCLUDE
* }
* }
* }
* }
*/
class SimpleRuleBasedPathFilter : public PathFilter {
@ -102,6 +141,12 @@ public:
private:
struct Tree {
Tree() = default;
/** Copy constructor */
Tree(const Tree& other);
/**
* Information on the USER-SPECIFIED inclusion/exclusion.
*
@ -111,6 +156,13 @@ private:
*/
EInclusion fIncluded = PARTIAL;
std::map<std::string, Tree> fChildren;
std::unique_ptr<Tree> fWildcard;
void applyRule(
const ResKeyPath& path,
std::list<std::string>::const_iterator it,
bool inclusionRule,
UErrorCode& status);
void print(std::ostream& out, int32_t indent) const;
};

View file

@ -585,9 +585,7 @@ processFile(const char *filename, const char *cp,
CharString inputDirBuf;
char outputFileName[256];
int32_t dirlen = 0;
int32_t filelen = 0;
if (U_FAILURE(status)) {
return;
@ -595,8 +593,6 @@ processFile(const char *filename, const char *cp,
if(filename==NULL){
status=U_ILLEGAL_ARGUMENT_ERROR;
return;
}else{
filelen = (int32_t)uprv_strlen(filename);
}
if(inputDir == NULL) {