uCalc API Version: 2.1.3-preview.2 Released: 6/16/2026

Warning

uCalc API Preview Release Notice:The documentation describes the intended behavior of the API. The current preview build contains incomplete features, unoptimized performance, and is subject to breaking changes.

Advanced Data Validation with the Transformer

Product: 

Class: 

Learn how to use the Transformer to enforce structural rules, validate data formats, and ensure content integrity using advanced pattern properties.

Remarks

🛡️ Advanced Data Validation with the Transformer

The Transformer is not just a tool for finding and replacing text; it's also a powerful engine for validating the structure and content of your data. By defining rules that describe what a valid document should look like, you can create sophisticated linters, configuration file validators, or static analysis tools.

This tutorial moves beyond simple pattern matching to explore the features that allow you to enforce constraints, check values, and ensure the integrity of your text data.


1. ✅ Existence and Occurrence Checks

The most basic form of validation is ensuring that a required pattern exists or that it appears a specific number of times. The Transformer provides both imperative and declarative ways to handle this.

The Imperative Way: Find() and Count()

The simplest approach is to define a Pattern and then check the result of the Matches collection after a Find() operation.

using (var t = new uCalc.Transformer()) {  t.Pattern("Header: OK");  if (t.Find("Header: OK").Matches.Count() > 0) {    Console.WriteLine("Document is valid.");  }}

While functional, this requires you to write explicit checking logic in your application code for every rule.

The Declarative Way: Minimum and Maximum

A more powerful and declarative approach is to use the Minimum and Maximum properties on a Rule object. These properties instruct the engine to automatically invalidate a rule's matches if the count is not met.

  • Minimum(n): The rule must match at least n times for its matches to be considered valid. If it finds fewer, its own matches are discarded (other rules are unaffected).
  • Maximum(n): The rule must match at most n times. If it finds more, its own matches are discarded.
  • GlobalMinimum(n) / GlobalMaximum(n): Same as above, but if the condition is not met, the entire transformation fails, and no matches from any rule are returned.

This allows you to build validation logic directly into your patterns, as shown in the practical example below.


2. 🔬 Content Validation with {@Eval}

Sometimes, it's not enough to know that a pattern exists; you also need to validate its content. For example, a port number must not only be a number, but it must be within a specific range. The {@Eval} directive is the perfect tool for this.

You can create a rule that captures a value and then uses {@Eval} with a conditional function like IIf to perform a check during the transformation.

// This rule will validate port numbers as it transforms them.t.FromTo("Port: {@Number:port_num}",          "Port: {port_num} - {@Eval: IIf(Double(port_num) > 0 and Double(port_num) < 65536, 'OK', 'INVALID')}");

This turns the transformer into a dynamic validation engine that can check values against business logic, not just syntax.


3. ❌ Negative Validation

Validation can also mean ensuring that certain patterns do not exist. You can achieve this in two ways:

  • Define a pattern for the invalid syntax and then check if its Matches.Count() is greater than zero. If it is, the document is invalid.
  • Use SkipOver for sections that are allowed but should not be processed by other validation rules. This is useful for ignoring comments or other non-functional blocks.

Examples

Succinct: Checks for the existence of a required header in a string.
				
					using uCalcSoftware;

var uc = new uCalc();
using (var t = new uCalc.Transformer()) {
   var text_ok = "Header: OK";
   var text_fail = "Header: ERROR";

   // This rule only matches if the status is "OK"
   t.Pattern("Header: OK");

   // Find() returns the transformer, so we can chain Matches().Count()
   if (t.SetText(text_ok).Find().Matches.Count() > 0) {
      Console.WriteLine("text_ok is valid.");
   }

   if (t.SetText(text_fail).Find().Matches.Count() == 0) {
      Console.WriteLine("text_fail is invalid.");
   }
}
				
			
text_ok is valid.
text_fail is invalid.
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

int main() {
   uCalc uc;
   {
      uCalc::Transformer t;
      t.Owned(); // Causes t to be released when it goes out of scope
      auto text_ok = "Header: OK";
      auto text_fail = "Header: ERROR";

      // This rule only matches if the status is "OK"
      t.Pattern("Header: OK");

      // Find() returns the transformer, so we can chain Matches().Count()
      if (t.SetText(text_ok).Find().Matches().Count() > 0) {
         cout << "text_ok is valid." << endl;
      }

      if (t.SetText(text_fail).Find().Matches().Count() == 0) {
         cout << "text_fail is invalid." << endl;
      }
   }
}
				
			
text_ok is valid.
text_fail is invalid.
				
					Imports System
Imports uCalcSoftware
Public Module Program
   Public Sub Main()
      Dim uc As New uCalc()
      Using t As New uCalc.Transformer()
         Dim text_ok = "Header: OK"
         Dim text_fail = "Header: ERROR"
         
         '// This rule only matches if the status is "OK"
         t.Pattern("Header: OK")
         
         '// Find() returns the transformer, so we can chain Matches().Count()
         If t.SetText(text_ok).Find().Matches.Count() > 0 Then
            Console.WriteLine("text_ok is valid.")
         End If
         
         If t.SetText(text_fail).Find().Matches.Count() = 0 Then
            Console.WriteLine("text_fail is invalid.")
         End If
      End Using
   End Sub
End Module
				
			
text_ok is valid.
text_fail is invalid.
Practical: Validates a configuration file format by enforcing the number of times specific keys must appear.
				
					using uCalcSoftware;

var uc = new uCalc();
using (var validator = new uCalc.Transformer()) {
   
   // Rule 1: Must contain exactly one 'Host' setting.
   var hostRule = validator.Pattern("Host: {@Alpha}").SetMinimum(1).SetMaximum(1);

   // Rule 2: Must contain at least one 'Port' setting.
   var portRule = validator.Pattern("Port: {@Number}").SetMinimum(1);

   var configFile_OK = """

Host: server1
Port: 80
Port: 443

""";
   var configFile_FAIL = "Port: 80"; // Missing Host

   Console.WriteLine("--- Testing Valid Config ---");
   validator.SetText(configFile_OK).Find();
   if (hostRule.Matches.Count() > 0 && portRule.Matches.Count() > 0) {
      Console.WriteLine("Config file is valid.");
   } else {
      Console.WriteLine("Config file is invalid.");
   }
   Console.WriteLine();
   Console.WriteLine("--- Testing Invalid Config ---");
   validator.SetText(configFile_FAIL).Find();
   // The Minimum(1) on hostRule causes it to have 0 matches if none are found.
   if (hostRule.Matches.Count() > 0 && portRule.Matches.Count() > 0) {
      Console.WriteLine("Config file is valid.");
   } else {
      Console.WriteLine("Config file is invalid.");
      if (hostRule.Matches.Count() == 0) {
         Console.WriteLine($"- Reason: Host rule failed (found {hostRule.Matches.Count()}, expected 1).");
      }
   }
}
				
			
--- Testing Valid Config ---
Config file is valid.

--- Testing Invalid Config ---
Config file is invalid.
- Reason: Host rule failed (found 0, expected 1).
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

int main() {
   uCalc uc;
   {
      uCalc::Transformer validator;
      validator.Owned(); // Causes validator to be released when it goes out of scope

      // Rule 1: Must contain exactly one 'Host' setting.
      auto hostRule = validator.Pattern("Host: {@Alpha}").SetMinimum(1).SetMaximum(1);

      // Rule 2: Must contain at least one 'Port' setting.
      auto portRule = validator.Pattern("Port: {@Number}").SetMinimum(1);

      auto configFile_OK = R"(
Host: server1
Port: 80
Port: 443
)";
      auto configFile_FAIL = "Port: 80"; // Missing Host

      cout << "--- Testing Valid Config ---" << endl;
      validator.SetText(configFile_OK).Find();
      if (hostRule.Matches().Count() > 0 && portRule.Matches().Count() > 0) {
         cout << "Config file is valid." << endl;
      } else {
         cout << "Config file is invalid." << endl;
      }
      cout << endl;
      cout << "--- Testing Invalid Config ---" << endl;
      validator.SetText(configFile_FAIL).Find();
      // The Minimum(1) on hostRule causes it to have 0 matches if none are found.
      if (hostRule.Matches().Count() > 0 && portRule.Matches().Count() > 0) {
         cout << "Config file is valid." << endl;
      } else {
         cout << "Config file is invalid." << endl;
         if (hostRule.Matches().Count() == 0) {
            cout << "- Reason: Host rule failed (found " << hostRule.Matches().Count() << ", expected 1)." << endl;
         }
      }
   }
}
				
			
--- Testing Valid Config ---
Config file is valid.

--- Testing Invalid Config ---
Config file is invalid.
- Reason: Host rule failed (found 0, expected 1).
				
					Imports System
Imports uCalcSoftware
Public Module Program
   Public Sub Main()
      Dim uc As New uCalc()
      Using validator As New uCalc.Transformer()
         
         '// Rule 1: Must contain exactly one 'Host' setting.
         Dim hostRule = validator.Pattern("Host: {@Alpha}").SetMinimum(1).SetMaximum(1)
         
         '// Rule 2: Must contain at least one 'Port' setting.
         Dim portRule = validator.Pattern("Port: {@Number}").SetMinimum(1)
         
         Dim configFile_OK = "
Host: server1
Port: 80
Port: 443
"
         Dim configFile_FAIL = "Port: 80" '// Missing Host
         
         Console.WriteLine("--- Testing Valid Config ---")
         validator.SetText(configFile_OK).Find()
         If hostRule.Matches.Count() > 0 And portRule.Matches.Count() > 0 Then
            Console.WriteLine("Config file is valid.")
         Else
            Console.WriteLine("Config file is invalid.")
         End If
         Console.WriteLine()
         Console.WriteLine("--- Testing Invalid Config ---")
         validator.SetText(configFile_FAIL).Find()
         '// The Minimum(1) on hostRule causes it to have 0 matches if none are found.
         If hostRule.Matches.Count() > 0 And portRule.Matches.Count() > 0 Then
            Console.WriteLine("Config file is valid.")
         Else
            Console.WriteLine("Config file is invalid.")
            If hostRule.Matches.Count() = 0 Then
               Console.WriteLine($"- Reason: Host rule failed (found {hostRule.Matches.Count()}, expected 1).")
            End If
         End If
      End Using
   End Sub
End Module
				
			
--- Testing Valid Config ---
Config file is valid.

--- Testing Invalid Config ---
Config file is invalid.
- Reason: Host rule failed (found 0, expected 1).