From c0b5bc3eea23d9c99804f8e8425875491efaac47 Mon Sep 17 00:00:00 2001
From: "Edward Z. Yang" <edwardzyang@thewritingpot.com>
Date: Tue, 4 Mar 2008 05:21:04 +0000
Subject: [PATCH] [3.1.0] Implement NamespaceExists and ParseId

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1599 48356398-32a2-884e-a903-53898d9a118a
---
 library/HTMLPurifier.includes.php             |  2 ++
 .../HTMLPurifier/ConfigSchema/Interchange.php | 33 ++++++++++++++-----
 .../ConfigSchema/Validator/Alnum.php          |  4 +--
 .../Validator/NamespaceExists.php             | 16 +++++++++
 .../ConfigSchema/Validator/ParseId.php        | 15 +++++++++
 .../ConfigSchema/Validator/Unique.php         |  2 ++
 .../ConfigSchema/Validator/AlnumTest.php      |  2 +-
 .../ConfigSchema/Validator/ExistsTest.php     |  2 +-
 .../Validator/NamespaceExistsTest.php         | 18 ++++++++++
 .../ConfigSchema/Validator/ParseIdTest.php    | 25 ++++++++++++++
 .../ConfigSchema/Validator/UniqueTest.php     |  5 ---
 .../ConfigSchema/ValidatorHarness.php         |  8 +++++
 12 files changed, 114 insertions(+), 18 deletions(-)
 create mode 100644 library/HTMLPurifier/ConfigSchema/Validator/NamespaceExists.php
 create mode 100644 library/HTMLPurifier/ConfigSchema/Validator/ParseId.php
 create mode 100644 tests/HTMLPurifier/ConfigSchema/Validator/NamespaceExistsTest.php
 create mode 100644 tests/HTMLPurifier/ConfigSchema/Validator/ParseIdTest.php

diff --git a/library/HTMLPurifier.includes.php b/library/HTMLPurifier.includes.php
index 6e86c36e..8039d459 100644
--- a/library/HTMLPurifier.includes.php
+++ b/library/HTMLPurifier.includes.php
@@ -133,6 +133,8 @@ require 'HTMLPurifier/ConfigSchema/StringHashReverseAdapter.php';
 require 'HTMLPurifier/ConfigSchema/Validator.php';
 require 'HTMLPurifier/ConfigSchema/Validator/Alnum.php';
 require 'HTMLPurifier/ConfigSchema/Validator/Exists.php';
+require 'HTMLPurifier/ConfigSchema/Validator/NamespaceExists.php';
+require 'HTMLPurifier/ConfigSchema/Validator/ParseId.php';
 require 'HTMLPurifier/ConfigSchema/Validator/Unique.php';
 require 'HTMLPurifier/DefinitionCache/Decorator.php';
 require 'HTMLPurifier/DefinitionCache/Null.php';
diff --git a/library/HTMLPurifier/ConfigSchema/Interchange.php b/library/HTMLPurifier/ConfigSchema/Interchange.php
index d44cf4c3..e2fc6c05 100644
--- a/library/HTMLPurifier/ConfigSchema/Interchange.php
+++ b/library/HTMLPurifier/ConfigSchema/Interchange.php
@@ -57,17 +57,32 @@ class HTMLPurifier_ConfigSchema_Interchange
         $validator = new HTMLPurifier_ConfigSchema_InterchangeValidator($this);
         
         // Validators should be defined in the order they are to be called.
-        
-        // Common validators
-        $validator->addValidator(new HTMLPurifier_ConfigSchema_Validator_Exists('ID'));
-        $validator->addValidator(new HTMLPurifier_ConfigSchema_Validator_Unique());
-        $validator->addValidator(new HTMLPurifier_ConfigSchema_Validator_Exists('DESCRIPTION'));
-        
-        // Namespace validators
-        
-        // Directive validators
+        $validator->addValidator($this->make('Exists', 'ID'));
+        $validator->addValidator($this->make('Unique'));
+        $validator->addNamespaceValidator($this->make('Alnum', 'ID'));
+        $validator->addValidator($this->make('ParseId'));
+        $validator->addValidator($this->make('Exists', '_NAMESPACE'));
+        $validator->addValidator($this->make('Alnum', '_NAMESPACE'));
+        $validator->addDirectiveValidator($this->make('Exists', '_DIRECTIVE'));
+        $validator->addDirectiveValidator($this->make('Alnum', '_DIRECTIVE'));
+        $validator->addDirectiveValidator($this->make('Exists', 'TYPE'));
+        $validator->addDirectiveValidator($this->make('Exists', 'DEFAULT'));
+        $validator->addDirectiveValidator($this->make('NamespaceExists'));
+        $validator->addValidator($this->make('Exists', 'DESCRIPTION'));
         
         return $validator;
     }
     
+    /**
+     * Creates a validator.
+     * @warning
+     *      Only *one* argument is supported; multiple args shouldn't use
+     *      this function.
+     */
+    protected function make($name, $arg = null) {
+        $class = "HTMLPurifier_ConfigSchema_Validator_$name";
+        if ($arg === null) return new $class();
+        else return new $class($arg);
+    }
+    
 }
diff --git a/library/HTMLPurifier/ConfigSchema/Validator/Alnum.php b/library/HTMLPurifier/ConfigSchema/Validator/Alnum.php
index 10a7a306..ed25339a 100644
--- a/library/HTMLPurifier/ConfigSchema/Validator/Alnum.php
+++ b/library/HTMLPurifier/ConfigSchema/Validator/Alnum.php
@@ -1,8 +1,8 @@
 <?php
 
 /**
- * Validates that a field is alphanumeric in the array (does not check
- * existence!)
+ * Validates that a field is alphanumeric in the array. Expects $index
+ * to exist.
  */
 class HTMLPurifier_ConfigSchema_Validator_Alnum extends HTMLPurifier_ConfigSchema_Validator
 {
diff --git a/library/HTMLPurifier/ConfigSchema/Validator/NamespaceExists.php b/library/HTMLPurifier/ConfigSchema/Validator/NamespaceExists.php
new file mode 100644
index 00000000..2edf5536
--- /dev/null
+++ b/library/HTMLPurifier/ConfigSchema/Validator/NamespaceExists.php
@@ -0,0 +1,16 @@
+<?php
+
+/**
+ * Validates that the directive's namespace exists. Expects _NAMESPACE
+ * to have been created via HTMLPurifier_ConfigSchema_Validator_ParseId
+ */
+class HTMLPurifier_ConfigSchema_Validator_NamespaceExists extends HTMLPurifier_ConfigSchema_Validator
+{
+    
+    public function validate(&$arr, $interchange) {
+        if (!isset($interchange->namespaces[$arr['_NAMESPACE']])) {
+            $this->error('Cannot define directive for undefined namespace ' . $arr['_NAMESPACE']);
+        }
+    }
+    
+}
diff --git a/library/HTMLPurifier/ConfigSchema/Validator/ParseId.php b/library/HTMLPurifier/ConfigSchema/Validator/ParseId.php
new file mode 100644
index 00000000..56d6ef3f
--- /dev/null
+++ b/library/HTMLPurifier/ConfigSchema/Validator/ParseId.php
@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * Parses ID into NAMESPACE and, if appropriate, DIRECTIVE. Expects ID to exist.
+ */
+class HTMLPurifier_ConfigSchema_Validator_ParseId extends HTMLPurifier_ConfigSchema_Validator
+{
+    
+    public function validate(&$arr, $interchange) {
+        $r = explode('.', $arr['ID'], 2);
+        $arr['_NAMESPACE'] = $r[0];
+        if (isset($r[1])) $arr['_DIRECTIVE'] = $r[1];
+    }
+    
+}
diff --git a/library/HTMLPurifier/ConfigSchema/Validator/Unique.php b/library/HTMLPurifier/ConfigSchema/Validator/Unique.php
index 6775f025..671b3c93 100644
--- a/library/HTMLPurifier/ConfigSchema/Validator/Unique.php
+++ b/library/HTMLPurifier/ConfigSchema/Validator/Unique.php
@@ -2,6 +2,8 @@
 
 /**
  * Validates that this ID does not exist already in the interchange object.
+ * Expects ID to exist.
+ * 
  * @note
  *      Although this tests both possible values, in practice the ID
  *      will only be in one or the other. We do this to keep things simple.
diff --git a/tests/HTMLPurifier/ConfigSchema/Validator/AlnumTest.php b/tests/HTMLPurifier/ConfigSchema/Validator/AlnumTest.php
index ddf317b1..b68f53bd 100644
--- a/tests/HTMLPurifier/ConfigSchema/Validator/AlnumTest.php
+++ b/tests/HTMLPurifier/ConfigSchema/Validator/AlnumTest.php
@@ -4,8 +4,8 @@ class HTMLPurifier_ConfigSchema_Validator_AlnumTest extends HTMLPurifier_ConfigS
 {
     
     public function setup() {
-        parent::setup();
         $this->validator = new HTMLPurifier_ConfigSchema_Validator_Alnum('ID');
+        parent::setup();
     }
     
     public function testValidate() {
diff --git a/tests/HTMLPurifier/ConfigSchema/Validator/ExistsTest.php b/tests/HTMLPurifier/ConfigSchema/Validator/ExistsTest.php
index ed0c9e21..4e6a73ba 100644
--- a/tests/HTMLPurifier/ConfigSchema/Validator/ExistsTest.php
+++ b/tests/HTMLPurifier/ConfigSchema/Validator/ExistsTest.php
@@ -4,8 +4,8 @@ class HTMLPurifier_ConfigSchema_Validator_ExistsTest extends HTMLPurifier_Config
 {
     
     public function setup() {
-        parent::setup();
         $this->validator = new HTMLPurifier_ConfigSchema_Validator_Exists('ID');
+        parent::setup();
     }
     
     public function testValidate() {
diff --git a/tests/HTMLPurifier/ConfigSchema/Validator/NamespaceExistsTest.php b/tests/HTMLPurifier/ConfigSchema/Validator/NamespaceExistsTest.php
new file mode 100644
index 00000000..cc78ac8c
--- /dev/null
+++ b/tests/HTMLPurifier/ConfigSchema/Validator/NamespaceExistsTest.php
@@ -0,0 +1,18 @@
+<?php
+
+class HTMLPurifier_ConfigSchema_Validator_NamespaceExistsTest extends HTMLPurifier_ConfigSchema_ValidatorHarness
+{
+    
+    public function testValidateFail() {
+        $arr = array('_NAMESPACE' => 'Namespace');
+        $this->expectSchemaException('Cannot define directive for undefined namespace Namespace');
+        $this->validator->validate($arr, $this->interchange);
+    }
+    
+    public function testValidatePass() {
+        $arr = array('_NAMESPACE' => 'Namespace');
+        $this->interchange->addNamespace(array('ID' => 'Namespace'));
+        $this->validator->validate($arr, $this->interchange);
+    }
+    
+}
diff --git a/tests/HTMLPurifier/ConfigSchema/Validator/ParseIdTest.php b/tests/HTMLPurifier/ConfigSchema/Validator/ParseIdTest.php
new file mode 100644
index 00000000..3e7bf429
--- /dev/null
+++ b/tests/HTMLPurifier/ConfigSchema/Validator/ParseIdTest.php
@@ -0,0 +1,25 @@
+<?php
+
+class HTMLPurifier_ConfigSchema_Validator_ParseIdTest extends HTMLPurifier_ConfigSchema_ValidatorHarness
+{
+    
+    public function testValidateNamespace() {
+        $arr = array('ID' => 'Namespace');
+        $this->validator->validate($arr, $this->interchange);
+        $this->assertIdentical($arr, array(
+            'ID' => 'Namespace',
+            '_NAMESPACE' => 'Namespace'
+        ));
+    }
+    
+    public function testValidateDirective() {
+        $arr = array('ID' => 'Namespace.Directive');
+        $this->validator->validate($arr, $this->interchange);
+        $this->assertIdentical($arr, array(
+            'ID' => 'Namespace.Directive',
+            '_NAMESPACE' => 'Namespace',
+            '_DIRECTIVE' => 'Directive'
+        ));
+    }
+    
+}
diff --git a/tests/HTMLPurifier/ConfigSchema/Validator/UniqueTest.php b/tests/HTMLPurifier/ConfigSchema/Validator/UniqueTest.php
index fce84e11..b71aa428 100644
--- a/tests/HTMLPurifier/ConfigSchema/Validator/UniqueTest.php
+++ b/tests/HTMLPurifier/ConfigSchema/Validator/UniqueTest.php
@@ -3,11 +3,6 @@
 class HTMLPurifier_ConfigSchema_Validator_UniqueTest extends HTMLPurifier_ConfigSchema_ValidatorHarness
 {
     
-    public function setup() {
-        parent::setup();
-        $this->validator = new HTMLPurifier_ConfigSchema_Validator_Unique();
-    }
-    
     public function testValidateNamespace() {
         $this->interchange->addNamespace(array('ID' => 'Namespace'));
         $this->expectSchemaException('Cannot redefine namespace');
diff --git a/tests/HTMLPurifier/ConfigSchema/ValidatorHarness.php b/tests/HTMLPurifier/ConfigSchema/ValidatorHarness.php
index 33865cec..f9dd0922 100644
--- a/tests/HTMLPurifier/ConfigSchema/ValidatorHarness.php
+++ b/tests/HTMLPurifier/ConfigSchema/ValidatorHarness.php
@@ -7,6 +7,14 @@ class HTMLPurifier_ConfigSchema_ValidatorHarness extends UnitTestCase
     
     public function setup() {
         $this->interchange = new HTMLPurifier_ConfigSchema_Interchange();
+        if (empty($this->validator)) {
+            $class_to_test = substr(get_class($this), 0, -4);
+            $this->validator = new $class_to_test;
+        }
+    }
+    
+    public function teardown() {
+        unset($this->validator, $this->interchange);
     }
     
     protected function expectSchemaException($msg) {