]> granicus.if.org Git - icu/commitdiff
ICU-10923 Adding wildcard resource matching.
authorShane F. Carr <shane@unicode.org>
Sat, 16 Feb 2019 10:56:40 +0000 (02:56 -0800)
committerShane F. Carr <shane@unicode.org>
Wed, 20 Feb 2019 18:20:38 +0000 (12:20 -0600)
icu4c/source/test/intltest/restsnew.cpp
icu4c/source/test/testdata/filters/filtertest.txt
icu4c/source/test/testdata/filtertest.txt
icu4c/source/tools/genrb/filterrb.cpp
icu4c/source/tools/genrb/filterrb.h
icu4c/source/tools/genrb/genrb.cpp

index e38bc89fdf6a3ea7efdc9d687531655af9fa24ab..1c19fc8e91deeae7b9a574a7b1a8dfc700efb81d 100644 (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 arizona = alaska.get("arizona", status);
-    REQUIRE_SUCCESS(status);
-    assertEquals("arizona", arizona.getType(), URES_STRING);
-
-    assertEquals("arizona", u"arkansas", arizona.getString(status));
-    REQUIRE_SUCCESS(status);
+    {
+        ResourceBundle alaska = alabama.get("alaska", status);
+        REQUIRE_SUCCESS(status);
+        assertEquals("alaska", alaska.getType(), URES_TABLE);
 
-    // Filter: california should not be included
-    ResourceBundle california = alaska.get("california", status);
-    REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR, status);
+        {
+            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);
+
+            // 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);
+
+        // Filter: illinois should not be included
+        ResourceBundle illinois = fornia.get("illinois", status);
+        REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR, status);
+    }
 
-    assertEquals("hawaii", u"idaho", hawaii.getString(status));
+    ResourceBundle mississippi = rb.get("mississippi", status);
     REQUIRE_SUCCESS(status);
+    assertEquals("mississippi", mississippi.getType(), URES_TABLE);
 
-    // Filter: illinois should not be included
-    ResourceBundle illinois = fornia.get("illinois", status);
-    REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR, status);
+    {
+        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
index e222ff300e5c12d2683c79908f4bdf2ff24a058c..91c780146e5ae46ac518dedb197392ca97719155 100644 (file)
@@ -4,3 +4,8 @@
 -/alabama
 +/alabama/alaska/arizona
 -/fornia/illinois
+-/mississippi
++/mississippi/michigan
++/mississippi/*/maine
+-/mississippi/*/iowa
++/mississippi/louisiana/iowa
index 149e2d5bf9e695393c94fcb368d937616f526d78..4d9aeb50d55ca391d33a799c4c325079d4f49801 100644 (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"}
+        }
+    }
 }
index 2302157ac4cd94815bf981995fb967d18fecaa50..c5feef180fd4e71279b74da9798912a7b51f6087 100644 (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 {
index 77c5360b9eaff5c1029aed500b985c80355fc3e2..7e361807b398e46a7a40314218659b6c64b3ce7d 100644 (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.
  *
- * For example, given this list of filter rules:
- *
- *     -/alabama
- *     +/alabama/alaska/arizona
- *     -/fornia/hawaii
+ * 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
+//     -/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;
     };
index 64b7a1e8d1e3264cee9969791207c22de3f53b65..4c17f45d458a727f13bfacaf6e1b08210c8e1017 100644 (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) {