diff --git a/.editorconfig b/.editorconfig index a319623..3ba6837 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,21 +1,114 @@ -; EditorConfig to support per-solution formatting. -; Use the EditorConfig VS add-in to make this work. -; http://editorconfig.org/ +# EditorConfig is awesome:http://EditorConfig.org -; This is the default for the codeline. +# top-most EditorConfig file root = true +# Don't use tabs for indentation. [*] -end_of_line = CRLF +indent_style = space +# (Please don't specify an indent_size here; that has too many unintended consequences.) -[*.{cs,txt,md}] -indent_style = tab +# Code files +[*.{cs,csx,vb,vbx}] indent_size = 4 -[*.{sln,proj,props,targets,xml,config,nuspec}] -indent_style = tab -indent_size = 4 +# Xml project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,msbuildproj,props,targets}] +indent_size = 2 -[*.{csproj,wixproj,resx,user}] -indent_style = space -indent_size = 2 \ No newline at end of file +# Xml config files +[*.{ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +# YAML files +[*.{yaml,yml}] +indent_size = 2 + +# JSON files +[*.json] +indent_size = 2 + +# Dotnet code style settings: +[*.{cs,vb}] +tab_width = 4 + +# Sort using and Import directives with System.* appearing first +dotnet_sort_system_directives_first = true +# Avoid "this." and "Me." if not necessary +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# Use language keywords instead of framework type names for type references +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Suggest more modern language features when available +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion + +# CSharp code style settings: + +# IDE0040: Add accessibility modifiers +dotnet_style_require_accessibility_modifiers = omit_if_default:error + +# IDE0040: Add accessibility modifiers +dotnet_diagnostic.IDE0040.severity = error + +# IDE1100: Error reading content of source file 'Project.TargetFrameworkMoniker' (i.e. from ThisAssembly) +dotnet_diagnostic.IDE1100.severity = none + +[*.cs] +# Top-level files are definitely OK +csharp_using_directive_placement = outside_namespace:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent + +# Prefer "var" everywhere +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = true:suggestion + +# Prefer method-like constructs to have an expression-body +csharp_style_expression_bodied_methods = true:none +csharp_style_expression_bodied_constructors = true:none +csharp_style_expression_bodied_operators = true:none + +# Prefer property-like constructs to have an expression-body +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none + +# Suggest more modern language features when available +csharp_style_pattern_matching_over_is_with_cast_check = true:error +csharp_style_pattern_matching_over_as_with_null_check = true:error +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Newline settings +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true + +# Test settings +[**/*Tests*/**{.cs,.vb}] +# xUnit1013: Public method should be marked as test. Allows using records as test classes +dotnet_diagnostic.xUnit1013.severity = none + +# CS9113: Parameter is unread (usually, ITestOutputHelper) +dotnet_diagnostic.CS9113.severity = none + +# Default severity for analyzer diagnostics with category 'Style' +dotnet_analyzer_diagnostic.category-Style.severity = none + +# VSTHRD200: Use "Async" suffix for async methods +dotnet_diagnostic.VSTHRD200.severity = none diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3095556 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +# normalize by default +* text=auto encoding=UTF-8 +*.sh text eol=lf +*.sbn eol=lf + +# These are windows specific files which we may as well ensure are +# always crlf on checkout +*.bat text eol=crlf +*.cmd text eol=crlf diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..11c5d7d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,48 @@ +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: +- package-ecosystem: nuget + directory: / + schedule: + interval: daily + groups: + Azure: + patterns: + - "Azure*" + - "Microsoft.Azure*" + Identity: + patterns: + - "System.IdentityModel*" + - "Microsoft.IdentityModel*" + System: + patterns: + - "System*" + exclude-patterns: + - "System.IdentityModel*" + Extensions: + patterns: + - "Microsoft.Extensions*" + exclude-patterns: + - "Microsoft.Extensions.AI*" + ExtensionsAI: + patterns: + - "Microsoft.Extensions.AI*" + Web: + patterns: + - "Microsoft.AspNetCore*" + Tests: + patterns: + - "Microsoft.NET.Test*" + - "xunit*" + - "coverlet*" + ThisAssembly: + patterns: + - "ThisAssembly*" + ProtoBuf: + patterns: + - "protobuf-*" + Spectre: + patterns: + - "Spectre.Console*" diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..c178589 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,32 @@ +changelog: + exclude: + labels: + - bydesign + - dependencies + - duplicate + - question + - invalid + - wontfix + - need info + - techdebt + authors: + - devlooped-bot + - dependabot + - github-actions + categories: + - title: ✨ Implemented enhancements + labels: + - enhancement + - title: πŸ› Fixed bugs + labels: + - bug + - title: πŸ“ Documentation updates + labels: + - docs + - documentation + - title: πŸ”¨ Other + labels: + - '*' + exclude: + labels: + - dependencies diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..94db3ff --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,108 @@ +ο»Ώ# Builds and runs tests in all three supported OSes +# Pushes CI feed if secrets.SLEET_CONNECTION is provided + +name: build +on: + workflow_dispatch: + inputs: + configuration: + type: choice + description: Configuration + options: + - Release + - Debug + push: + branches: [ main, 'feature/*', 'rel/*' ] + paths-ignore: + - changelog.md + - readme.md + pull_request: + types: [opened, synchronize, reopened] + +env: + DOTNET_NOLOGO: true + PackOnBuild: true + GeneratePackageOnBuild: true + VersionPrefix: 42.42.${{ github.run_number }} + VersionLabel: ${{ github.ref }} + GH_TOKEN: ${{ secrets.GH_TOKEN }} + MSBUILDTERMINALLOGGER: auto + Configuration: ${{ github.event.inputs.configuration || 'Release' }} + SLEET_FEED_URL: ${{ vars.SLEET_FEED_URL }} + +defaults: + run: + shell: bash + +jobs: + os-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.lookup.outputs.matrix }} + steps: + - name: 🀘 checkout + uses: actions/checkout@v4 + + - name: πŸ”Ž lookup + id: lookup + shell: pwsh + run: | + $path = './.github/workflows/os-matrix.json' + $os = if (test-path $path) { cat $path } else { '["ubuntu-latest"]' } + echo "matrix=$os" >> $env:GITHUB_OUTPUT + + build: + needs: os-matrix + name: build-${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: ${{ fromJSON(needs.os-matrix.outputs.matrix) }} + steps: + - name: 🀘 checkout + uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + + - name: βš™ dotnet + uses: devlooped/actions-dotnet-env@v1 + + - name: πŸ™ build + run: dotnet build -m:1 -bl:build.binlog + + - name: πŸ§ͺ test + shell: pwsh + run: dnx --yes retest -- --no-build + + - name: πŸ› logs + uses: actions/upload-artifact@v4 + if: runner.debug && always() + with: + name: logs + path: '*.binlog' + + - name: πŸš€ sleet + env: + SLEET_CONNECTION: ${{ secrets.SLEET_CONNECTION }} + if: env.SLEET_CONNECTION != '' + run: | + dotnet tool update sleet -g --allow-downgrade --version $(curl -s --compressed ${{ vars.SLEET_FEED_URL }} | jq '.["sleet:version"]' -r) + sleet push bin --config none -f --verbose -p "SLEET_FEED_CONTAINER=nuget" -p "SLEET_FEED_CONNECTIONSTRING=${{ secrets.SLEET_CONNECTION }}" -p "SLEET_FEED_TYPE=azure" || echo "No packages found" + + dotnet-format: + runs-on: ubuntu-latest + steps: + - name: 🀘 checkout + uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + + - name: βš™ dotnet + uses: devlooped/actions-dotnet-env@v1 + + - name: βœ“ ensure format + run: | + dotnet format whitespace --verify-no-changes -v:diag --exclude ~/.nuget + dotnet format style --verify-no-changes -v:diag --exclude ~/.nuget diff --git a/.github/workflows/changelog.config b/.github/workflows/changelog.config new file mode 100644 index 0000000..e47bccd --- /dev/null +++ b/.github/workflows/changelog.config @@ -0,0 +1,9 @@ +usernames-as-github-logins=true +issues_wo_labels=true +pr_wo_labels=true +exclude-labels=bydesign,dependencies,duplicate,discussion,question,invalid,wontfix,need info,docs +enhancement-label=:sparkles: Implemented enhancements: +bugs-label=:bug: Fixed bugs: +issues-label=:hammer: Other: +pr-label=:twisted_rightwards_arrows: Merged: +unreleased=false diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml new file mode 100644 index 0000000..ca50e5a --- /dev/null +++ b/.github/workflows/changelog.yml @@ -0,0 +1,39 @@ +ο»Ώname: changelog +on: + workflow_dispatch: + release: + types: [released] + +jobs: + changelog: + runs-on: ubuntu-latest + steps: + - name: πŸ€– defaults + uses: devlooped/actions-bot@v1 + with: + name: ${{ secrets.BOT_NAME }} + email: ${{ secrets.BOT_EMAIL }} + gh_token: ${{ secrets.GH_TOKEN }} + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: 🀘 checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: main + token: ${{ env.GH_TOKEN }} + + - name: βš™ ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.0.3 + + - name: βš™ changelog + run: | + gem install github_changelog_generator + github_changelog_generator --user ${GITHUB_REPOSITORY%/*} --project ${GITHUB_REPOSITORY##*/} --token $GH_TOKEN --o changelog.md --config-file .github/workflows/changelog.config + + - name: πŸš€ changelog + run: | + git add changelog.md + (git commit -m "πŸ–‰ Update changelog with ${GITHUB_REF#refs/*/}" && git push) || echo "Done" \ No newline at end of file diff --git a/.github/workflows/dotnet-env.yml b/.github/workflows/dotnet-env.yml new file mode 100644 index 0000000..a76d0fd --- /dev/null +++ b/.github/workflows/dotnet-env.yml @@ -0,0 +1,44 @@ +name: dotnet-env +on: + workflow_dispatch: + push: + branches: + - main + paths: + - '**/*.*proj' + +jobs: + which-dotnet: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - name: πŸ€– defaults + uses: devlooped/actions-bot@v1 + with: + name: ${{ secrets.BOT_NAME }} + email: ${{ secrets.BOT_EMAIL }} + gh_token: ${{ secrets.GH_TOKEN }} + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: 🀘 checkout + uses: actions/checkout@v4 + with: + token: ${{ env.GH_TOKEN }} + + - name: 🀌 dotnet + uses: devlooped/actions-which-dotnet@v1 + + - name: ✍ pull request + uses: peter-evans/create-pull-request@v7 + with: + base: main + branch: which-dotnet + delete-branch: true + labels: dependencies + title: "βš™ Update dotnet versions" + body: "Update dotnet versions" + commit-message: "Update dotnet versions" + token: ${{ env.GH_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/dotnet-file.yml b/.github/workflows/dotnet-file.yml new file mode 100644 index 0000000..402f6ba --- /dev/null +++ b/.github/workflows/dotnet-file.yml @@ -0,0 +1,21 @@ +# Synchronizes .netconfig-configured files with dotnet-file +name: dotnet-file +on: + workflow_dispatch: + schedule: + - cron: "0 0 * * *" + push: + branches: [ 'dotnet-file' ] + +env: + DOTNET_NOLOGO: true + +jobs: + run: + permissions: + contents: write + uses: devlooped/oss/.github/workflows/dotnet-file-core.yml@main + secrets: + BOT_NAME: ${{ secrets.BOT_NAME }} + BOT_EMAIL: ${{ secrets.BOT_EMAIL }} + GH_TOKEN: ${{ secrets.GH_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/includes.yml b/.github/workflows/includes.yml new file mode 100644 index 0000000..fc6dbeb --- /dev/null +++ b/.github/workflows/includes.yml @@ -0,0 +1,67 @@ +name: +Mᐁ includes +on: + workflow_dispatch: + push: + branches: + - 'main' + paths: + - '**.md' + - '!changelog.md' + - 'osmfeula.txt' + +jobs: + includes: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: πŸ€– defaults + uses: devlooped/actions-bot@v1 + with: + name: ${{ secrets.BOT_NAME }} + email: ${{ secrets.BOT_EMAIL }} + gh_token: ${{ secrets.GH_TOKEN }} + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: 🀘 checkout + uses: actions/checkout@v4 + with: + token: ${{ env.GH_TOKEN }} + + - name: +Mᐁ includes + uses: devlooped/actions-includes@v1 + + - name: πŸ“ OSMF EULA + shell: pwsh + run: | + $file = "osmfeula.txt" + $props = "src/Directory.Build.props" + if (-not (test-path $file) -or -not (test-path $props)) { + exit 0 + } + + $product = dotnet msbuild $props -getproperty:Product + if (-not $product) { + write-error 'To use OSMF EULA, ensure the $(Product) property is set in Directory.props' + exit 1 + } + + ((get-content -raw $file) -replace '\$product\$',$product).trim() | set-content $file + + - name: ✍ pull request + uses: peter-evans/create-pull-request@v8 + with: + add-paths: | + **.md + *.txt + base: main + branch: markdown-includes + delete-branch: true + labels: dependencies + author: ${{ env.BOT_AUTHOR }} + committer: ${{ env.BOT_AUTHOR }} + commit-message: +Mᐁ includes + title: +Mᐁ includes + body: +Mᐁ includes + token: ${{ env.GH_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..035d811 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,60 @@ +ο»Ώ# Builds a final release version and pushes to nuget.org +# whenever a release is published. +# Requires: secrets.NUGET_API_KEY + +name: publish +on: + release: + types: [prereleased, released] + +env: + DOTNET_NOLOGO: true + Configuration: Release + PackOnBuild: true + GeneratePackageOnBuild: true + VersionLabel: ${{ github.ref }} + GH_TOKEN: ${{ secrets.GH_TOKEN }} + MSBUILDTERMINALLOGGER: auto + SLEET_FEED_URL: https://api.nuget.org/v3/index.json + +jobs: + publish: + runs-on: ${{ vars.PUBLISH_AGENT || 'ubuntu-latest' }} + steps: + - name: 🀘 checkout + uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + + - name: βš™ dotnet + uses: devlooped/actions-dotnet-env@v1 + + - name: πŸ™ build + run: dotnet build -m:1 -bl:build.binlog + + - name: πŸ§ͺ test + shell: pwsh + run: dnx --yes retest -- --no-build + + - name: πŸ› logs + uses: actions/upload-artifact@v4 + if: runner.debug && always() + with: + name: logs + path: '*.binlog' + + - name: πŸš€ nuget + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} + if: ${{ env.NUGET_API_KEY != '' && github.event.action != 'prereleased' }} + working-directory: bin + run: dotnet nuget push *.nupkg -s https://api.nuget.org/v3/index.json -k ${{secrets.NUGET_API_KEY}} --skip-duplicate + + - name: πŸš€ sleet + env: + SLEET_CONNECTION: ${{ secrets.SLEET_CONNECTION }} + if: env.SLEET_CONNECTION != '' + run: | + dotnet tool update sleet -g --allow-downgrade --version $(curl -s --compressed ${{ vars.SLEET_FEED_URL }} | jq '.["sleet:version"]' -r) + sleet push bin --config none -f --verbose -p "SLEET_FEED_CONTAINER=nuget" -p "SLEET_FEED_CONNECTIONSTRING=${{ secrets.SLEET_CONNECTION }}" -p "SLEET_FEED_TYPE=azure" || echo "No packages found" diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml new file mode 100644 index 0000000..56ff299 --- /dev/null +++ b/.github/workflows/triage.yml @@ -0,0 +1,103 @@ +name: 'triage' +on: + schedule: + - cron: '42 0 * * *' + + workflow_dispatch: + # Manual triggering through the GitHub UI, API, or CLI + inputs: + daysBeforeClose: + description: "Days before closing stale or need info issues" + required: true + default: "30" + daysBeforeStale: + description: "Days before labeling stale" + required: true + default: "180" + daysSinceClose: + description: "Days since close to lock" + required: true + default: "30" + daysSinceUpdate: + description: "Days since update to lock" + required: true + default: "30" + +permissions: + actions: write # For managing the operation state cache + issues: write + contents: read + +jobs: + stale: + # Do not run on forks + if: github.repository_owner == 'devlooped' + runs-on: ubuntu-latest + steps: + - name: βŒ› rate + shell: pwsh + if: github.event_name != 'workflow_dispatch' + env: + GH_TOKEN: ${{ secrets.DEVLOOPED_TOKEN }} + run: | + # add random sleep since we run on fixed schedule + $wait = get-random -max 180 + echo "Waiting random $wait seconds to start" + sleep $wait + # get currently authenticated user rate limit info + $rate = gh api rate_limit | convertfrom-json | select -expandproperty rate + # if we don't have at least 100 requests left, wait until reset + if ($rate.remaining -lt 100) { + $wait = ($rate.reset - (Get-Date (Get-Date).ToUniversalTime() -UFormat %s)) + echo "Rate limit remaining is $($rate.remaining), waiting for $($wait / 1000) seconds to reset" + sleep $wait + $rate = gh api rate_limit | convertfrom-json | select -expandproperty rate + echo "Rate limit has reset to $($rate.remaining) requests" + } + + - name: ✏️ stale labeler + # pending merge: https://github.com/actions/stale/pull/1176 + uses: kzu/stale@c8450312ba97b204bf37545cb249742144d6ca69 + with: + ascending: true # Process the oldest issues first + stale-issue-label: 'stale' + stale-issue-message: | + Due to lack of recent activity, this issue has been labeled as 'stale'. + It will be closed if no further activity occurs within ${{ fromJson(inputs.daysBeforeClose || 30 ) }} more days. + Any new comment will remove the label. + close-issue-message: | + This issue will now be closed since it has been labeled 'stale' without activity for ${{ fromJson(inputs.daysBeforeClose || 30 ) }} days. + days-before-stale: ${{ fromJson(inputs.daysBeforeStale || 180) }} + days-before-close: ${{ fromJson(inputs.daysBeforeClose || 30 ) }} + days-before-pr-close: -1 # Do not close PRs labeled as 'stale' + exempt-all-milestones: true + exempt-all-assignees: true + exempt-issue-labels: priority,sponsor,backed + exempt-authors: kzu + + - name: 🀘 checkout actions + uses: actions/checkout@v4 + with: + repository: 'microsoft/vscode-github-triage-actions' + ref: v42 + + - name: βš™ install actions + run: npm install --production + + - name: πŸ”’ issues locker + uses: ./locker + with: + token: ${{ secrets.DEVLOOPED_TOKEN }} + ignoredLabel: priority + daysSinceClose: ${{ fromJson(inputs.daysSinceClose || 30) }} + daysSinceUpdate: ${{ fromJson(inputs.daysSinceUpdate || 30) }} + + - name: πŸ”’ need info closer + uses: ./needs-more-info-closer + with: + token: ${{ secrets.DEVLOOPED_TOKEN }} + label: 'need info' + closeDays: ${{ fromJson(inputs.daysBeforeClose || 30) }} + closeComment: "This issue has been closed automatically because it needs more information and has not had recent activity.\n\nHappy Coding!" + pingDays: 80 + pingComment: "Hey @${assignee}, this issue might need further attention.\n\n@${author}, you can help us out by closing this issue if the problem no longer exists, or adding more information." \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8313753..dfe1fe0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,42 @@ -.nuget -out -build bin obj -packages +artifacts +pack +TestResults +results +BenchmarkDotNet.Artifacts +/app +.vs +.vscode +.genaiscript +.idea +local.settings.json +.env +*.local + *.suo +*.sdf +*.userprefs *.user -.vs -.* \ No newline at end of file +*.nupkg +*.metaproj +*.tmp +*.log +*.cache +*.binlog +*.zip +__azurite*.* +__*__ + +.nuget +*.lock.json +*.nuget.props +*.nuget.targets + +node_modules +_site +.jekyll-metadata +.jekyll-cache +.sass-cache +Gemfile.lock +package-lock.json diff --git a/.netconfig b/.netconfig new file mode 100644 index 0000000..3b1c3a3 --- /dev/null +++ b/.netconfig @@ -0,0 +1,120 @@ +[file] + url = https://github.com/devlooped/oss +[file ".netconfig"] + url = https://github.com/devlooped/oss/blob/main/.netconfig + skip +[file "readme.md"] + url = https://github.com/devlooped/oss/blob/main/readme.md + skip +[file ".github/workflows/combine-prs.yml"] + url = https://github.com/devlooped/oss/blob/main/.github/workflows/combine-prs.yml + skip +[file "src/kzu.snk"] + url = https://github.com/devlooped/oss/blob/main/src/kzu.snk + skip +[file ".github/workflows/dotnet-file-core.yml"] + url = https://github.com/devlooped/oss/blob/main/.github/workflows/dotnet-file-core.yml + skip +[file ".github/actions/dotnet/action.yml"] + url = https://github.com/devlooped/oss/blob/main/.github/actions/dotnet/action.yml + skip +[file ".editorconfig"] + url = https://github.com/devlooped/oss/blob/main/.editorconfig + sha = a62c45934ac2952f2f5d54d8aea4a7ebc1babaff + etag = b5e919b472a52d4b522f86494f0f2c0ba74a6d9601454e20e4cbaf744317ff62 + weak +[file ".gitattributes"] + url = https://github.com/devlooped/oss/blob/main/.gitattributes + sha = 4a9aa321c4982b83c185cf8dffed181ff84667d5 + etag = 09cad18280ed04b67f7f87591e5481510df04d44c3403231b8af885664d8fd58 + weak +[file ".github/dependabot.yml"] + url = https://github.com/devlooped/oss/blob/main/.github/dependabot.yml + sha = e733294084fb3e75d517a2e961e87df8faae7dc6 + etag = 3bf8d9214a15c049ca5cfe80d212a8cbe4753b8a638a9804ef73d34c7def9618 + weak +[file ".github/release.yml"] + url = https://github.com/devlooped/oss/blob/main/.github/release.yml + sha = 0c23e24704625cf75b2cb1fdc566cef7e20af313 + etag = 310df162242c95ed19ed12e3c96a65f77e558b46dced676ad5255eb12caafe75 + weak +[file ".github/workflows/build.yml"] + url = https://github.com/devlooped/oss/blob/main/.github/workflows/build.yml + sha = 5da103cfbc1c4f9b5f59cfa698d2afbd744a7525 + etag = 851af098748f7cfa5bc3cfd4cc404a6de930532b59ceb2b3b535282c41226f3a + weak +[file ".github/workflows/changelog.config"] + url = https://github.com/devlooped/oss/blob/main/.github/workflows/changelog.config + sha = 08d83cb510732f861416760d37702f9f55bd7f9e + etag = 556a28914eeeae78ca924b1105726cdaa211af365671831887aec81f5f4301b4 + weak +[file ".github/workflows/changelog.yml"] + url = https://github.com/devlooped/oss/blob/main/.github/workflows/changelog.yml + sha = 5fb172362c767bef7c36478f1a6bdc264723f8f9 + etag = ad1efa56d6024ee1add2bcda81a7e4e38d0e9069473c6ff70374d5ce06af1f5a + weak +[file ".github/workflows/dotnet-env.yml"] + url = https://github.com/devlooped/oss/blob/main/.github/workflows/dotnet-env.yml + sha = 77e83f238196d2723640abef0c7b6f43994f9747 + etag = fcb9759a96966df40dcd24906fd328ddec05953b7e747a6bb8d0d1e4c3865274 + weak +[file ".github/workflows/dotnet-file.yml"] + url = https://github.com/devlooped/oss/blob/main/.github/workflows/dotnet-file.yml + sha = 8fa147d4799d73819040736c399d0b1db2c2d86c + etag = 1ca805a23656e99c03f9d478dba8ccef6e571f5de2ac0e9bb7e3c5216c99a694 + weak +[file ".github/workflows/includes.yml"] + url = https://github.com/devlooped/oss/blob/main/.github/workflows/includes.yml + sha = 06628725a6303bb8c4cf3076a384fc982a91bc0b + etag = 478f91d4126230e57cc601382da1ba23f9daa054645b4af89800d8dd862e64fd + weak +[file ".github/workflows/publish.yml"] + url = https://github.com/devlooped/oss/blob/main/.github/workflows/publish.yml + sha = 7f5f9ee9f362f7e8f951d618f8f799033550e687 + etag = c60411d1aa4e98e7f69e2d34cbccb8eb7e387ec11f6f8e78ee8d8b92122d7025 + weak +[file ".github/workflows/triage.yml"] + url = https://github.com/devlooped/oss/blob/main/.github/workflows/triage.yml + sha = 33000c0c4ab4eb4e0e142fa54515b811a189d55c + etag = 013a47739e348f06891f37c45164478cca149854e6cd5c5158e6f073f852b61a + weak +[file ".gitignore"] + url = https://github.com/devlooped/oss/blob/main/.gitignore + sha = a225b7a9f609f26bcc24e0d84f663387be251a7d + etag = 20a8b49d57024abbd85aac5b0020c30e5eb68e0384b2761e93727c8780c4a991 + weak +[file "Directory.Build.rsp"] + url = https://github.com/devlooped/oss/blob/main/Directory.Build.rsp + sha = 0f7f7f7e8a29de9b535676f75fe7c67e629a5e8c + etag = 0ccae83fc51f400bfd7058170bfec7aba11455e24a46a0d7e6a358da6486e255 + weak +[file "_config.yml"] + url = https://github.com/devlooped/oss/blob/main/_config.yml + sha = 68b409c486842062e0de0e5b11e6fdb7cd12d6e2 + etag = d608aa0ddaedc2d8a87260f50756e8d8314964ad4671b76bd085bcb458757010 + weak +[file "assets/css/style.scss"] + url = https://github.com/devlooped/oss/blob/main/assets/css/style.scss + sha = 9db26e2710b084d219d6355339d822f159bf5780 + etag = f710d8919abfd5a8d00050b74ba7d0bb05c6d02e40842a3012eb96555c208504 + weak +[file "license.txt"] + url = https://github.com/devlooped/oss/blob/main/license.txt + sha = 0683ee777d7d878d4bf013d7deea352685135a05 + etag = 2c6335b37e4ae05eea7c01f5d0c9d82b49c488f868a8b5ba7bff7c6ff01f3994 + weak +[file "src/Directory.Build.props"] + url = https://github.com/devlooped/oss/blob/main/src/Directory.Build.props + sha = 0ff8b7b79a82112678326d1dc5543ed890311366 + etag = 3ebde0a8630d526b80f15801179116e17a857ff880a4442e7db7b075efa4fd63 + weak +[file "src/Directory.Build.targets"] + url = https://github.com/devlooped/oss/blob/main/src/Directory.Build.targets + sha = 4339749ef4b8f66def75931df09ef99c149f8421 + etag = 8b4492765755c030c4c351e058a92f53ab493cab440c1c0ef431f6635c4dae0e + weak +[file "src/nuget.config"] + url = https://github.com/devlooped/oss/blob/main/src/nuget.config + sha = 032439dbf180fca0539a5bd3a019f18ab3484b76 + etag = da7c0104131bd474b52fc9bc9f9bda6470e24ae38d4fb9f5c4f719bc01370ab5 + weak diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..dd435e7 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,54 @@ +# AGENTS.md + +## Project Overview + +Schematron.NET is a C# high-performance implementation of the [Schematron ISO/IEC standard](http://schematron.com/) for XML validation. It supports standalone Schematron schemas, schemas embedded inside W3C XML Schema (WXS), and SOAP web-service validation via a SoapExtension. + +## Repository Structure + +``` +Schematron.slnx # SDK-style XML solution (slnx format) +src/ + Directory.Build.props # Shared MSBuild properties (authors, copyright, LangVersion, GitInfo) + Schematron/ # Core library (net48) + Schematron.Tests/ # xUnit test project (net48) + Schematron.WebServices/ # SOAP SoapExtension integration (net48, requires System.Web.Services) +VERSION # Base version file read by GitInfo (e.g. 0.6.0) +``` + +## Key Components + +### Schematron (core) + +| File | Purpose | +|------|---------| +| `Validator.cs` | Main entry point β€” loads schemas, validates XML/Schematron, collects errors | +| `Schema.cs` / `SchemaLoader.cs` | Represent and load a Schematron schema document | +| `SyncEvaluationContext.cs` | Synchronous evaluation engine; walks Phases β†’ Patterns β†’ Rules β†’ Asserts/Reports | +| `EvaluationContextBase.cs` | Abstract base for evaluation strategies | +| `Formatters/` | Pluggable output formatters: `BooleanFormatter`, `LogFormatter`, `SimpleFormatter`, `XmlFormatter` | +| `Phase.cs`, `Pattern.cs`, `Rule.cs`, `Assert.cs`, `Report.cs` | Domain model for Schematron elements | +| `IMatchedNodes.cs` + implementations | Strategy for tracking already-matched nodes per pattern | + +### Schematron.WebServices + +`ValidationExtension` (a `SoapExtension`) and `ValidationAttribute` allow Schematron validation to be applied declaratively to SOAP web methods. + +### Schematron.Tests + +xUnit tests covering `Validator` behaviour with embedded XSD+Schematron schemas and standalone Schematron schemas. + +## Build & Test + +``` +dotnet build Schematron.slnx +dotnet test Schematron.slnx +``` + +## Conventions + +- **Target framework**: `net48` for all projects (WebServices requires .NET Framework due to `System.Web.Services`). +- **Versioning**: Managed by the [GitInfo](https://github.com/devlooped/GitInfo) package; base version comes from the `VERSION` file at the repo root. +- **NuGet metadata**: Defined in `src/Directory.Build.props` and per-project ``. +- **C# language version**: `latest` β€” use modern idioms (expression-bodied members, `var`, `string.IsNullOrEmpty`, `throw;`, pattern matching). +- **No custom build scripts**: Use standard `dotnet` CLI; no `build.cmd` / `build.proj`. diff --git a/Directory.Build.rsp b/Directory.Build.rsp new file mode 100644 index 0000000..509cc66 --- /dev/null +++ b/Directory.Build.rsp @@ -0,0 +1,5 @@ +# See https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-response-files +-nr:false +-m:1 +-v:m +-clp:Summary;ForceNoAlign diff --git a/README.md b/README.md index 5d2685c..977dcbb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ # Schematron.NET This is a [Schematron ISO/IEC standard](http://www.schematron.com/) processor -for .NET, written in C#. - -[![Build status](https://ci.appveyor.com/api/projects/status/74wvk575m9ofgpuy?svg=true)](https://ci.appveyor.com/project/MobileEssentials/schematron) +for .NET, written in C#. \ No newline at end of file diff --git a/Schematron.slnx b/Schematron.slnx new file mode 100644 index 0000000..4535df1 --- /dev/null +++ b/Schematron.slnx @@ -0,0 +1,4 @@ + + + + diff --git a/VERSION b/VERSION deleted file mode 100644 index 09a3acf..0000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.6.0 \ No newline at end of file diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..04e6093 --- /dev/null +++ b/_config.yml @@ -0,0 +1,3 @@ +theme: jekyll-theme-slate + +exclude: [ 'src/', '*.sln', '*.slnx', 'Gemfile*', '*.rsp' ] diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 04bb9db..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,23 +0,0 @@ -os: Visual Studio 2015 - -init: - - git config --global core.autocrlf input - -build_script: - - build.cmd - -deploy: - - provider: NuGet - api_key: - secure: 6IOnCr0vcNuRan0YWj7JRzsjZINxy7QuKAsg1HvdmYIOvTMV/ZbFyaRbL5X8OOmY - artifact: /.*\.nupkg/ - on: - branch: master - -nuget: - project_feed: true - disable_publish_on_pr: true - -artifacts: - - path: '*.nupkg' - name: Packages diff --git a/assets/css/style.scss b/assets/css/style.scss new file mode 100644 index 0000000..5e165a3 --- /dev/null +++ b/assets/css/style.scss @@ -0,0 +1,26 @@ +--- +--- + +@import "jekyll-theme-slate"; + +.inner { + max-width: 960px; +} + +pre, code { + background-color: unset; + font-size: unset; +} + + code { + font-size: 0.80em; +} + +h1 > img { + border: unset; + box-shadow: unset; + vertical-align: middle; + -moz-box-shadow: unset; + -o-box-shadow: unset; + -ms-box-shadow: unset; +} diff --git a/build.cmd b/build.cmd deleted file mode 100644 index b7b7131..0000000 --- a/build.cmd +++ /dev/null @@ -1,23 +0,0 @@ -@echo off -rem Only need to run this the first time after clone. Subsequent builds can be just "msbuild" or "xbuild". - -cd %~dp0 - -SETLOCAL -SET CACHED_NUGET=%LocalAppData%\NuGet\NuGet.exe - -IF EXIST %CACHED_NUGET% goto copynuget -echo Downloading latest version of NuGet.exe... -IF NOT EXIST %LocalAppData%\NuGet md %LocalAppData%\NuGet -@powershell -NoProfile -ExecutionPolicy unrestricted -Command "$ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest 'https://www.nuget.org/nuget.exe' -OutFile '%CACHED_NUGET%'" - -:copynuget -IF EXIST src\.nuget\nuget.exe goto restore -md src\.nuget -copy %CACHED_NUGET% src\.nuget\nuget.exe > nul - -:restore -src\.nuget\NuGet.exe install build\packages.config -OutputDirectory build\packages -ExcludeVersion - -:run -msbuild build.proj /v:normal %1 %2 %3 %4 %5 %6 %7 %8 %9 \ No newline at end of file diff --git a/build.proj b/build.proj deleted file mode 100644 index b17124e..0000000 --- a/build.proj +++ /dev/null @@ -1,83 +0,0 @@ - - - - - build\ - Release - $([System.IO.Path]::Combine($(MSBuildThisFileDirectory), 'out')) - high - VERSION - - - - - - - - - - - $(GitSemVerMajor).$(GitSemVerMinor).$(GitSemVerPatch)$(GitSemVerDashLabel) - - - - - - %(RootDir)%(Directory)%(Filename).nuspec - %(Filename) - $(Version) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - build\packages\ - $(PackagesDir)xunit.runner.msbuild\build - - $([System.IO.Directory]::GetDirectories($(XunitBuildDir))[0]) - - $([System.IO.Path]::Combine($(XunitPlatformDir), 'xunit.runner.msbuild.props')) - $(PackagesDir)GitInfo\build\GitInfo.targets - true - - - - - - - - - - - - diff --git a/build/packages.config b/build/packages.config deleted file mode 100644 index 2daca23..0000000 --- a/build/packages.config +++ /dev/null @@ -1,5 +0,0 @@ -ο»Ώ - - - - \ No newline at end of file diff --git a/doc/Notes.txt b/doc/Notes.txt deleted file mode 100644 index 8b37343..0000000 --- a/doc/Notes.txt +++ /dev/null @@ -1,17 +0,0 @@ -1 - Multithreading left for the future. Not sure how to WaitAll for the threads in the pool to finish before returning. Adds complexity. Benchmark performance gains! - -2 - Abstract rules with an ID can be anywhere in the schematron schema, and it will work anyway, according to the XSLT implementations. We do so too, for compatibility. - -3 - elements under are currently ignored. An additional expression could be created to offer support for it. Check. UPDATE: no need for this element as WXS have good support for keys/unique/keyref - -4 - Make NDoc stylesheet to show , , , and elements. - -5 - MUST DO: create the XmlFormatter, define a schema and format for the output. This is VERY important for WebService scenarios!! - -6 - SHOULD DO: create the HtmlFormatter, for user friendly use!! - - ----- OLD NOTES ---- -1 - SchematronSchema.Read() will use standard XmlSchema validation to check for a valid schematron definition. -2 - It is a Composite implementation. -3 - Should it use Visitor design pattern for evaluation? May ease the process of processing the tree for other uses, such as diagnostics, conformance reports, etc. \ No newline at end of file diff --git a/doc/Schematron.xml b/doc/Schematron.xml deleted file mode 100644 index ea33334..0000000 --- a/doc/Schematron.xml +++ /dev/null @@ -1,3351 +0,0 @@ -ο»Ώ - - - - - - - - - - IFormatter - - - Initializes a new instance of the BaseFormatter class. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IFormatter - - - Initializes a new instance of the BooleanFormatter class. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializes a new instance of the FormattingUtils class. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IFormatter - - - Initializes a new instance of the LogFormatter class. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IFormatter - - - Initializes a new instance of the SimpleFormatter class. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This is the NMatrix Schematron.NET, an implementation of the Schematron standard by Rick Jeliffe. - - - - - - - - - - - - - - Initializes a new instance of the EvaluableExpression class. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Base class for testing units of Schematron, such as Assert or Report elements. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Represents an assert element of the Schematron schema. - - - - - As stated in the specificacion, this is - the lowest element in a Schematron schema. This element contains the expression - to execute in the context of its parent . - If the results of the execution of the expression are False, the - assert fails and the correponding message will be displayed. - Constructor is not public. To programatically create an instance of this - class use the factory method. - - - - - - - - Constructs a new Assert object. - XPath expression to test. - Message to display if the assert fails. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializes a new instance of the EvaluationContextBase class. - - - - - - - - - - - - - Keeps a list of nodes already matched. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Evaluates and elements asynchronously. - - - See for a description of the purpose and - of evaluation contexts, and where are they used. - - - Currently this class is not working properly. We're getting thread contention - when the method is called. We have tried - almost every options there is to retrieve the handles. The one with the contention - problem is the one using method - to enqueue the method execution. - - Another approach tried (and commented in the code) is asynchronous execution of the - evaluation method using delegate. This produces - and exception because apparently an invalid is returned. - - So currently, only the is in use. - - - - - - - Creates the evaluation context - - - - - - - - - - - - - - - - Starts the evaluation process. - - - When the process is finished, the results are placed - in the property. - - - - - - - - - - - - - Evaluates the selected . - - - Processing is synchronous, as patterns must - me evaluated in document order. - - As most of the other evaluation methods, it repositions the - navigator on the root node. - - The to evaluate. - The messages accumulated by the evaluation of all the child - , or if there are no messages. - - - - - - - Evaluates the selected . - - - Processing is synchronous, as rules must me evaluated in document order. - - As most of the other evaluation methods, it repositions the - navigator on the root node. - - Clears the object before - proceeding, as the restriction about node mathing (see ) - applies only inside a single pattern. - - The to evaluate. - The messages accumulated by the evaluation of all the child - , or if there are no messages. - - - - - - - Evaluates the selected . - - - Here is where asynchronous becomes. and - are queued using the class. - - Nodes matched by this are added to the list of - nodes to skip in the next rule, using the method. - This object is a strategy object which implements different algorithms for matching and - saving node references, as the actual implementation provides - different methods for accessing the underlying source. - - This makes the implementation both performant and compliant with - the restriction about node mathing (see ) in the spec. - - As most of the other evaluation methods, it repositions the - navigator on the root node. - Here is where the multithreading problems arise, which are not - due to the schema design itself, but this specific evaluation process. - The intent it to evaluate asserts and reports in parallel, to get the - most out of the CPU. - - The to evaluate. - The messages accumulated by the evaluation of all the child - and . - - The rule to evaluate is abstract (see ). - - - - - - - - Asynchronous evaluation of an - - This method is used as the delegate for the - version of the asynchonous call. - - This just retrieves the state, casts it to and executes the - method, passing the values received. It locks on the received - to append the messages from evaluation. - - State for the execution. - If the timed out. - - - - - - - - Asynchronous evaluation of a - - This method is used as the delegate for the - version of the asynchonous call. - - This just retrieves the state, casts it to and executes the - method, passing the values received. It locks on the received - to append the messages from evaluation. - - State for the execution. - If the timed out. - - - - - - - - Performs the evaluation of the . - - - This is where the actual assert expression is evaluated. - Is called synchonously from the or - using the delegate asynchronously, with - BeginInvoke and EndInvoke. If the - returns false, a formated message is generated - from the property. - - The to evaluate. - The context node for the execution. - The formatted message for a failing , or - . - - - - - - - - - Performs the evaluation of the . - - - This is where the actual report expression is evaluated. - Is called synchonously from the or - using the delegate asynchronously, with - BeginInvoke and EndInvoke. If - the returns true, - a formated message is generated from the property. - - The to evaluate. - The context node for the execution. - The formatted message for a succesful , or - . - - - - - - - - - Callback for asynchonous delegate execution. - - - This is the to use when executing - asynchronously the method. - It completes the call by calling EndInvoke, retrieving the results - and appending the messages to the received - in the property. - - - This in an example of the delegate creation and asynchronous execution. - AsyncAssertEvaluate eval = new AsyncAssertEvaluate(EvaluateAssert); - eval.BeginInvoke(asr, ctx, - new AsyncCallback(OnAssertCompleted), sb); - - The object to extract state information from. - - - - - - - Callback for asynchonous delegate execution. - - - This is the to use when executing - asynchronously the method. - It completes the call by calling EndInvoke, retrieving the results - and appending the messages to the received - in the property. - - - This in an example of the delegate creation and asynchronous execution. - AsyncReportEvaluate eval = new AsyncReportEvaluate(EvaluateReport); - eval.BeginInvoke(rpt, ctx, new AsyncCallback(OnReportCompleted), sb); - - The object to extract state information from. - - - - - - - - - - Represents the an error in the Schematron schema. - - - - - - - - - ISerializable - - - - Initializes a new instance of the class. - - - - - - - Initializes a new instance of the class. - - The error message that explains the reason for the exception. - - - - - - - Initializes a new instance of the class. - - Info - Context - - - - - - - - Initializes a new instance of the class. - - The error message that explains the reason for the exception. - The exception that is the cause of the current exception. - - - - - - - - Backer for property P:Schematron.BadSchemaException.HResult - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Changed - - - - - - - - - - - - - - - - - - - ISerializable - - - Initializes a new instance of the AnInventedExceptionNew class. - - - - - - - Backer for property P:Schematron.AnInventedExceptionNew.HResult - - - - - - - - - - - - - - - - - - - - - - - - - - - - This class keeps static precompiled expressions used - in the Schematron schema loading and validation processes. - - - - - All expressions are compiled against the - object. All the objects are initialized with - the for schematron and XML Schema - namespaces resolution. - - - - - Initializes a new instance of the CompiledExpressions class. - - - - - - - - - - - - - - - - - - - - - - - - Provides global settings for Schematron validation. - - - - - This class is public to allow inheritors of Schematron elements - to use these global settings. - - - - - Initializes a new instance of the Config class. - - - - - - - - - - - - The default object to use to format messages from validation. - - - - - - - A default empty navigator used to pre-compile XPath expressions. - - - Compiling doesn't involve any namespace, - name table or other specific processing. It's only a parsing procedure that - builds the abstract syntax tree for later evaluation. So we can safely - use an empty to compile them against. - - - expr = Config.DefaultNavigator.Compile("//sch:pattern"); - other code; - - - - - - - - - - - Manager to use when executing expressions that validate or - load Schematron and Embedded Schematron schemas. - - - - - - - A cached schema in Schematron format to validate schematron schemas. - - This is the version for standalone schemas. - - - - - - A cached schema in Schematron format to validate schematron schemas. - - This is the version for embedded schemas. - - - - - - A unique identifier to use for internal keys. - - - - - - - - - - - - - - - - - IMatchedNodes - - - Initializes a new instance of the DomMatchedNodes class. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializes a new instance of the Driver class. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IMatchedNodes - - - Initializes a new instance of the GenericMatchedNodes class. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IList - ICollection - IEnumerable - - - Initializes a new instance of the Int32Collection class. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IEnumerator - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The exception that is thrown when an invalid XPath expression is used. - - - - - - ISerializable - - - Initializes a new instance of the InvalidExpressionException class. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Backer for property P:Schematron.InvalidExpressionException.HResult - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A collection of Pattern elements. - - - - - - - - - IList - ICollection - IEnumerable - - - Initializes a new instance of the PatternCollection class. - - - - - - Returns the Pattern element at the specified position. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializes a new instance of the Phase class. - - - - - - - - - The identifier to check for All phases. - - Causes all the patterns in a schema to be checked, - irrespective of the phases where they are activated. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A collection of Phase elements - - - - - - - - - IDictionary - ICollection - IEnumerable - - - Initializes a new instance of the PhaseCollection class. - - - - - - Required indexer. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Performs reports using XPath expressions. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TODO: add support to child elements. - Lacks support for key elements in schematron. - - - - - Creates an abstract rule, without context. - - - - - - - - - If passed a null or empty string, this is implicitly an abstract rule. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Only abstract rules can be used as a base for extensions. - - - - - - - - - - - - - - - - A collection of Rule elements. - - - - - - - - - IList - ICollection - IEnumerable - - - Initializes a new instance of the RuleCollection class. - - - - - - Returns the Rule element at the specified position. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Lacks attributes defined in Schematron, but not in use currently. - - - - Initializes a new instance of the Schema class. - - - - - - - - - - - - - - - - - - - The Schematron namespace. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A collection of schematron schemas. - - - - - - - - - IList - ICollection - IEnumerable - - - Initializes a new instance of the SchemaCollection class. - - - - - - Returns the Schema element at the specified position. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Evaluates all the schema elements synchronously. - - - - - See for a description of the purpose and - of evaluation contexts, and where are they used. - - - - - - Creates the evaluation context - - - - - - - - - - - - - - - - Starts the evaluation process. - - - When the process is finished, the results are placed - in the property. - - - - - - - - - - - - - Evaluates the selected . - - - As most of the other evaluation methods, it repositions the - navigator on the root node. - - The to evaluate. - The messages accumulated by the evaluation of all the child - , or if there are no messages. - - - - - - - Evaluates the selected . - - - As most of the other evaluation methods, it repositions the - navigator on the root node. - - Clears the object before - proceeding, as the restriction about node mathing (see ) - applies only inside a single pattern. - - The to evaluate. - The messages accumulated by the evaluation of all the child - , or if there are no messages. - - - - - - - Evaluates the selected . - - - and are iterated - and each and is executed against - the context selected by the . - - Nodes matched are added to the list of - nodes to skip in the next rule, using the method. - This object is a strategy object which implements different algorithms for matching and - saving node references, as the actual implementation provides - different methods for accessing the underlying source. - - This makes the implementation both performant and compliant with - the restriction about node mathing (see ) in the spec. - - As most of the other evaluation methods, it repositions the - navigator on the root node. - - The to evaluate. - The messages accumulated by the evaluation of all the child - and , or - if there are no messages to show. - - The rule to evaluate is abstract (see ). - - - - - - - - Performs the evaluation of the . - - - This is where the actual assert expression is evaluated. If - the returns false, - a formated message is generated from the property. - - The to evaluate. - The context node for the execution. - The formatted message for a failing , or - . - - - - - - - - - Performs the evaluation of the . - - - This is where the actual report expression is evaluated. If - the returns true, - a formated message is generated from the property. - - The to evaluate. - The context node for the execution. - The formatted message for a succesful , or - . - - - - - - - - - - - - - - - Initializes a new instance of the TagExpressions class. - - - - - - The compiled regular expression to replace the special name tag inside a message. - - - Replaces each instance of name tags with the value un the current context element. - - - - - - - - - - - - - - - - - - - - - A collection of Test elements. - - - - - - - - - IList - ICollection - IEnumerable - - - Initializes a new instance of the TestCollection class. - - - - - - Returns the Test element at the specified position. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializes a new instance of the Validator class. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Where the actual work takes place - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Where the actual work takes place - - - - - - - - - - - - - - - - - - - - - IMatchedNodes - - - Initializes a new instance of the XPathMatchedNodes class. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Contains assembly level documentation. - - MPL1.1 - Project starter. - Schematron Specification - - - - - - - - - - - - - - - - - - - - - - - - - - - - Structure to pass state around for asynchonous execution. - - - - - - - - - - - - - - - - - - - - - - - - Structure to pass state around for asynchonous execution. - - - - - - - - - - - - - - - - - - - - - - - - Private delegate used in to call - evaluation asynchronously against an object. - - - - - This in an example of the delegate creation and asynchronous execution. - AsyncAssertEvaluate eval = new AsyncAssertEvaluate(EvaluateAssert); - eval.BeginInvoke( - assert, - context, - new AsyncCallback(OnAssertCompleted), - state); - - - - - - - - - Private delegate used in to call - evaluation asynchronously against an object. - - - - - This in an example of the delegate creation and asynchronous execution. - AsyncReportEvaluate eval = new AsyncReportEvaluate(EvaluateReport); - eval.BeginInvoke( - report, - context, - new AsyncCallback(OnReportCompleted), - state); - - - - - - - - - Evaluates Test - - - Para 1? - - Test para1 - - Test para2 - - - This is an example - - this is code; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A Schematron schema is embedded in another XML schema, such as XSD or XDR. - Both schemas will be loaded. - - - - - - - W3C XML Schema only. - - - - - - - Schematron schema only. - - - - - - - MSXML XDR schema only. - - - - - - - - \ No newline at end of file diff --git a/doc/~$ndings.doc b/doc/~$ndings.doc new file mode 100644 index 0000000..b3d1835 Binary files /dev/null and b/doc/~$ndings.doc differ diff --git a/LICENSE b/license.txt similarity index 93% rename from LICENSE rename to license.txt index 5fdba2e..83969dc 100644 --- a/LICENSE +++ b/license.txt @@ -1,6 +1,6 @@ -MIT License +The MIT License (MIT) -Copyright (c) 2017 Daniel Cazzulino +Copyright (c) Daniel Cazzulino and Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -19,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/src/Before.Schematron.sln.targets b/src/Before.Schematron.sln.targets deleted file mode 100644 index 44ff2c6..0000000 --- a/src/Before.Schematron.sln.targets +++ /dev/null @@ -1,10 +0,0 @@ -ο»Ώ - - - - .\ - - - - - \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 0000000..29281ee --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,187 @@ + + + + + false + + true + + + + + $(CI) + + + + Daniel Cazzulino + Devlooped + Copyright (C) Daniel Cazzulino and Contributors. All rights reserved. + false + MIT + + + icon.png + readme.md + + icon.png + readme.md + + true + true + + $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\bin')) + + + true + true + + + true + + false + + true + + + + Release + Latest + + + false + + embedded + true + enable + + strict + + + $(MSBuildProjectName) + $(MSBuildProjectName.IndexOf('.')) + $(MSBuildProjectName.Substring(0, $(RootNamespaceDot))) + + + $(DefaultItemExcludes);*.binlog;*.zip;*.rsp;*.items;**/TestResults/**/*.* + + true + true + true + true + + + true + + + false + + + NU5105;$(NoWarn) + + true + + + true + + + LatestMinor + + + + + $(MSBuildThisFileDirectory)kzu.snk + + 002400000480000094000000060200000024000052534131000400000100010051155fd0ee280be78d81cc979423f1129ec5dd28edce9cd94fd679890639cad54c121ebdb606f8659659cd313d3b3db7fa41e2271158dd602bb0039a142717117fa1f63d93a2d288a1c2f920ec05c4858d344a45d48ebd31c1368ab783596b382b611d8c92f9c1b3d338296aa21b12f3bc9f34de87756100c172c52a24bad2db + 00352124762f2aa5 + true + + + + + 42.42.42 + + + + <_VersionLabel>$(VersionLabel.Replace('refs/heads/', '')) + <_VersionLabel>$(_VersionLabel.Replace('refs/tags/v', '')) + + + <_VersionLabel Condition="$(_VersionLabel.Contains('refs/pull/'))">$(VersionLabel.TrimEnd('.0123456789')) + + <_VersionLabel>$(_VersionLabel.Replace('refs/pull/', 'pr')) + + <_VersionLabel>$(_VersionLabel.Replace('/merge', '')) + + <_VersionLabel>$(_VersionLabel.Replace('/', '-')) + + <_VersionLabel>$(_VersionLabel.Replace('_', '-')) + + + $(_VersionLabel) + + $(_VersionLabel) + + + true + 42.42.0 + $(VersionSuffix).$(GITHUB_RUN_NUMBER) + + + + + + + + + + + + + + + + + + + + + + + + + 1.0.0 + $(VersionPrefix)-$(VersionSuffix) + $(VersionPrefix) + + + + + diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets new file mode 100644 index 0000000..083afa6 --- /dev/null +++ b/src/Directory.Build.targets @@ -0,0 +1,191 @@ + + + + + CI;$(DefineConstants) + + + + + false + false + true + + + + true + true + + + + + false + true + + + + + + + + + + + + + + + + + + + + 1.0.0 + $(VersionPrefix)-$(VersionSuffix) + $(VersionPrefix) + + + + + $(PackFolder) + $(PackFolderPath.Replace('\$(TargetFramework)', '')) + $(IntermediateOutputPath)$(PackFolderPath)\ + $(OutputPath)$(PackFolderPath)\ + $(OutputPath) + + + + + pr$(GITHUB_REF.Replace('refs/pull/', '').Replace('/merge', '')) + $(GITHUB_REF.Replace('refs/heads/', '').Replace('refs/tags/', '')) + + $(BUILD_SOURCEBRANCH.Replace('refs/heads/', '').Replace('refs/tags/', '')) + + pr$(APPVEYOR_PULL_REQUEST_NUMBER) + $(APPVEYOR_REPO_TAG_NAME) + $(APPVEYOR_REPO_BRANCH) + + $(TEAMCITY_BUILD_BRANCH) + + pr$(TRAVIS_PULL_REQUEST) + $(TRAVIS_BRANCH) + + pr$(CIRCLE_PR_NUMBER) + $(CIRCLE_TAG) + $(CIRCLE_BRANCH) + + $(CI_COMMIT_TAG) + pr$(CI_MERGE_REQUEST_IID) + pr$(CI_EXTERNAL_PULL_REQUEST_IID) + $(CI_COMMIT_BRANCH) + + pr$(BUDDY_EXECUTION_PULL_REQUEST_NO) + $(BUDDY_EXECUTION_TAG) + $(BUDDY_EXECUTION_BRANCH) + + + + + CoreResGen;$(CoreCompileDependsOn) + + + + + + + + + $(IntermediateOutputPath)\$([MSBuild]::ValueOrDefault('%(RelativeDir)', '').Replace('\', '.').Replace('/', '.'))%(Filename).g$(DefaultLanguageSourceExtension) + $(Language) + $(RootNamespace) + $(RootNamespace).$([MSBuild]::ValueOrDefault('%(RelativeDir)', '').Replace('\', '.').Replace('/', '.').TrimEnd('.')) + %(Filename) + + + + + + + + + + + + + + + + + + + + + + + + $(PrivateRepositoryUrl) + + + + $(SourceRevisionId) + $(SourceRevisionId.Substring(0, 9)) + + $(RepositorySha) + + + + + <_GitSourceRoot Include="@(SourceRoot -> WithMetadataValue('SourceControl', 'git'))" /> + + + + @(_GitSourceRoot) + + $([System.IO.Path]::GetFileNameWithoutExtension($(PrivateRepositoryUrl))) + $(ProductFromUrl) + + + + + + + $(RepositoryUrl.Replace('.git', '')) + $(Description) + $(RepositoryUrl.Replace('.git', ''))/blob/main/changelog.md + + + + + + + + diff --git a/src/Directory.props b/src/Directory.props new file mode 100644 index 0000000..0bd5eac --- /dev/null +++ b/src/Directory.props @@ -0,0 +1,10 @@ + + + + Schematron.NET + A C# high-performance implementation of Schematron ISO/IEC standard + https://github.com/kzu/Schematron + true + + + diff --git a/src/GlobalAssemblyInfo.cs b/src/GlobalAssemblyInfo.cs deleted file mode 100644 index 92201a8..0000000 --- a/src/GlobalAssemblyInfo.cs +++ /dev/null @@ -1,42 +0,0 @@ -ο»Ώ#pragma warning disable 0436 - -using System.Reflection; - -[assembly: AssemblyCompany ("Schematron.NET")] -[assembly: AssemblyProduct ("Schematron.NET")] -[assembly: AssemblyCopyright ("Copyright Β© Daniel Cazzulino 2015")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] - -#if DEBUG -[assembly: AssemblyConfiguration ("DEBUG")] -#else -[assembly: AssemblyConfiguration ("RELEASE")] -#endif - -// AssemblyVersion = full version info, since it's used to determine agents versions -[assembly: AssemblyVersion (ThisAssembly.Version)] -// FileVersion = release-like simple version (i.e. 3.11.2 for cycle 5, SR2). -[assembly: AssemblyFileVersion (ThisAssembly.SimpleVersion)] -// InformationalVersion = full version + branch + commit sha. -[assembly: AssemblyInformationalVersion (ThisAssembly.InformationalVersion)] - -partial class ThisAssembly -{ - /// - /// Simple release-like version number, like 4.0.1 for a cycle 5, SR1 build. - /// - public const string SimpleVersion = Git.BaseVersion.Major + "." + Git.BaseVersion.Minor + "." + Git.BaseVersion.Patch; - - /// - /// Full version, including commits since base version file, like 4.0.1.598 - /// - public const string Version = SimpleVersion + "." + Git.Commits; - - /// - /// Full version, plus branch and commit short sha. - /// - public const string InformationalVersion = Version + "-" + Git.Branch + "+" + Git.Commit; -} - -#pragma warning restore 0436 \ No newline at end of file diff --git a/src/NuGet.Restore.targets b/src/NuGet.Restore.targets deleted file mode 100644 index 8bd5940..0000000 --- a/src/NuGet.Restore.targets +++ /dev/null @@ -1,86 +0,0 @@ - - - - - $(MSBuildThisFileDirectory).nuget - - - - $(NuGetPath)\NuGet.exe - $(LocalAppData)\NuGet\NuGet.exe - - - - nuget - - - - - - - - - - Restore - - - - - - - - - - - - - - <_NuGet>$(NuGet.Replace('$(RestoreDir)', '').TrimStart('/').TrimStart('\')) - - - - - - - - - - - - - - - - - <_RestoreSolution Condition=" '$(OS)' != 'Unix' ">$(_RestoreSolution.Replace('$(RestoreDir)', '').TrimStart('/').TrimStart('\')) - <_RestoreOutput Condition=" '$(OS)' != 'Unix' And '$(_RestoreOutput)' != ''">$(_RestoreOutput.Replace('$(RestoreDir)', '').TrimStart('/').TrimStart('\')) - - <_RestoreCommand Condition=" '$(_RestoreCommand)' == '' ">Restore - - - - - - - - - - - %WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe - - - - - - - - - diff --git a/src/Schematron.Tests/Properties/AssemblyInfo.cs b/src/Schematron.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index e4681cf..0000000 --- a/src/Schematron.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -ο»Ώusing System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Schematron.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("Schematron.Tests")] -[assembly: AssemblyCopyright("Copyright Β© Microsoft 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("bd9d55cc-716c-4e68-86ab-89fd4be643b5")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Schematron.Tests/Schematron.Tests.csproj b/src/Schematron.Tests/Schematron.Tests.csproj index e264af2..ca1e5ab 100644 --- a/src/Schematron.Tests/Schematron.Tests.csproj +++ b/src/Schematron.Tests/Schematron.Tests.csproj @@ -1,107 +1,26 @@ -ο»Ώ - - - - + + - Debug - AnyCPU - {0CE72032-070F-479E-8ADD-4C04AE53C1B6} - Library - Properties - Schematron.Tests - Schematron.Tests - v4.5 - 512 + net10.0 - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - ..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll - True - - - ..\packages\xunit.assert.2.1.0\lib\portable-net45+win8+wp8+wpa81\xunit.assert.dll - True - - - ..\packages\xunit.extensibility.core.2.1.0\lib\portable-net45+win8+wp8+wpa81\xunit.core.dll - True - - - ..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll - True - - - - - - + - + PreserveNewest + - - Designer - PreserveNewest - - - Designer - PreserveNewest - - - Designer - PreserveNewest - - - - - - {db7369f1-69cf-4821-98e3-9d6422a7cc36} - Schematron - + + + + all + runtime; build; native; contentfiles; analyzers + + - + - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - \ No newline at end of file + + diff --git a/src/Schematron.Tests/ValidatorTests.cs b/src/Schematron.Tests/ValidatorTests.cs index dcb4375..83a5d9a 100644 --- a/src/Schematron.Tests/ValidatorTests.cs +++ b/src/Schematron.Tests/ValidatorTests.cs @@ -1,156 +1,153 @@ -ο»Ώusing System; -using System.IO; using System.Xml; -using System.Xml.XPath; -using System.Xml.Serialization; -using System.Text; using System.Xml.Schema; +using System.Xml.Serialization; +using System.Xml.XPath; using Xunit; -namespace Schematron.Tests +namespace Schematron.Tests; + +public class ValidatorTests { - public class ValidatorTests - { - const string XsdLocation = "./Content/po-schema.xsd"; - const string XsdWithPartialSchemaLocation = "./Content/po-schema-with-schema-import.xsd"; - const string XmlContentLocation = "./Content/po-instance.xml"; - const string TargetNamespace = "http://example.com/po-schematron"; - - [Fact] - public void NewAddSchemaSignatureShouldNotBreakCode() - { - var validatorA = new Validator(OutputFormatting.XML); - validatorA.AddSchema(XmlReader.Create(XsdLocation)); - - var validatorB = new Validator(OutputFormatting.XML); - validatorB.AddSchema(TargetNamespace, XsdLocation); - - var resultA = default(string); - var resultB = default(string); - - try - { - var result = validatorA.Validate(XmlReader.Create(XmlContentLocation)); - } - catch (ValidationException ex) - { - resultA = ex.Message; - - System.Diagnostics.Debug.WriteLine(ex.Message); - } - - try - { - var result = validatorB.Validate(XmlReader.Create(XmlContentLocation)); - } - catch (ValidationException ex) - { - resultB = ex.Message; - - Xunit.Assert.True(resultA == resultB); - - System.Diagnostics.Debug.WriteLine(ex.Message); - } - - } - - [Fact] - public void ValidateShouldReturnSchematronValidationResultWhenSchematronConstraintsAreNotMet() - { - //Arrange - var validator = new Validator(OutputFormatting.XML); - - //Act - validator.AddSchema(TargetNamespace, XsdLocation); - - using (var doc = XmlReader.Create(XmlContentLocation)) - { - var result = default(IXPathNavigable); - - try - { - result = validator.Validate(doc); - } - catch (ValidationException ex) - { - System.Diagnostics.Debug.WriteLine(ex.Message); - - var serializer = new XmlSerializer(typeof(Schematron.Serialization.SchematronValidationResultTempObjectModel.output)); - - using (var stream = new MemoryStream(System.Text.Encoding.Unicode.GetBytes(ex.Message))) - using (var reader = XmlReader.Create(stream)) - { - var obj = (Schematron.Serialization.SchematronValidationResultTempObjectModel.output)serializer.Deserialize(reader); - - // Assert - - - Xunit.Assert.NotNull(obj); - Xunit.Assert.NotNull(obj.xml); - Xunit.Assert.NotNull(obj.schematron); - } - } - } - } - - [Fact] - public void WhenUsingTheXmlReaderApproach_ToSupplyASchema_TypesFromImportsAreNotResolved() - { - // arrange - var validator = new Schematron.Validator(); - - // act, (assert) - Xunit.Assert.Throws(() => validator.AddSchema(XmlReader.Create(XsdWithPartialSchemaLocation))); - } - - [Fact] - public void WhenUsingTheXmlSchemaSetBasedApproach_ToSupplyASchema_TypesFromImportsAreResolved() - { - // arrange - var validator = new Schematron.Validator(); - - var count = validator.XmlSchemas != null ? validator.XmlSchemas.Count : 0; - - // act, (assert) - validator.AddSchema(TargetNamespace, XsdWithPartialSchemaLocation); - - Xunit.Assert.True(validator.Schemas.Count == count + 1); - - //var res = validator.Validate(XmlContentLocation); - } - - //[Fact] - public void DoTheRawXmlValidation() - { - throw new NotImplementedException(); - } - - [Fact] - public void SchematronValidationResultIncludesExpandedValueElements() - { - //Arrange - var validator = new Validator(OutputFormatting.XML); - - //Act - validator.AddSchema(TargetNamespace, XsdLocation); - - using (var doc = XmlReader.Create(XmlContentLocation)) - { - var result = default(IXPathNavigable); - - try - { - result = validator.Validate(doc); - } - catch (ValidationException ex) - { - System.Diagnostics.Debug.WriteLine(ex.Message); - string expectedMessage = "Attributes sex (Female) and title (Mr) must have compatible values on element customer."; - Xunit.Assert.True(ex.Message.Contains(expectedMessage)); - } - Xunit.Assert.Null(result); - } - } - - } + const string XsdLocation = "./Content/po-schema.xsd"; + const string XsdWithPartialSchemaLocation = "./Content/po-schema-with-schema-import.xsd"; + const string XmlContentLocation = "./Content/po-instance.xml"; + const string TargetNamespace = "http://example.com/po-schematron"; + + [Fact] + public void NewAddSchemaSignatureShouldNotBreakCode() + { + var validatorA = new Validator(OutputFormatting.XML); + validatorA.AddSchema(XmlReader.Create(XsdLocation)); + + var validatorB = new Validator(OutputFormatting.XML); + validatorB.AddSchema(TargetNamespace, XsdLocation); + + string? resultA = default; + string? resultB = default; + + try + { + var result = validatorA.Validate(XmlReader.Create(XmlContentLocation)); + } + catch (ValidationException ex) + { + resultA = ex.Message; + + System.Diagnostics.Debug.WriteLine(ex.Message); + } + + try + { + var result = validatorB.Validate(XmlReader.Create(XmlContentLocation)); + } + catch (ValidationException ex) + { + resultB = ex.Message; + + Xunit.Assert.True(resultA == resultB); + + System.Diagnostics.Debug.WriteLine(ex.Message); + } + + } + + [Fact] + public void ValidateShouldReturnSchematronValidationResultWhenSchematronConstraintsAreNotMet() + { + //Arrange + var validator = new Validator(OutputFormatting.XML); + + //Act + validator.AddSchema(TargetNamespace, XsdLocation); + + using (var doc = XmlReader.Create(XmlContentLocation)) + { + IXPathNavigable? result = default; + + try + { + result = validator.Validate(doc); + } + catch (ValidationException ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + + var serializer = new XmlSerializer(typeof(Schematron.Serialization.SchematronValidationResultTempObjectModel.Output)); + + using (var stream = new MemoryStream(System.Text.Encoding.Unicode.GetBytes(ex.Message))) + using (var reader = XmlReader.Create(stream)) + { + var obj = (Schematron.Serialization.SchematronValidationResultTempObjectModel.Output)serializer.Deserialize(reader); + + // Assert + + + Xunit.Assert.NotNull(obj); + Xunit.Assert.NotNull(obj.Xml); + Xunit.Assert.NotNull(obj.Schematron); + } + } + } + } + + [Fact] + public void WhenUsingTheXmlReaderApproach_ToSupplyASchema_TypesFromImportsAreNotResolved() + { + // arrange + var validator = new Schematron.Validator(); + + // act, (assert) + Xunit.Assert.Throws(() => validator.AddSchema(XmlReader.Create(XsdWithPartialSchemaLocation))); + } + + [Fact] + public void WhenUsingTheXmlSchemaSetBasedApproach_ToSupplyASchema_TypesFromImportsAreResolved() + { + // arrange + var validator = new Schematron.Validator(); + + var count = validator.XmlSchemas != null ? validator.XmlSchemas.Count : 0; + + // act, (assert) + validator.AddSchema(TargetNamespace, XsdWithPartialSchemaLocation); + + Xunit.Assert.True(validator.Schemas.Count == count + 1); + + //var res = validator.Validate(XmlContentLocation); + } + + //[Fact] + public void DoTheRawXmlValidation() + { + throw new NotImplementedException(); + } + + [Fact] + public void SchematronValidationResultIncludesExpandedValueElements() + { + //Arrange + var validator = new Validator(OutputFormatting.XML); + + //Act + validator.AddSchema(TargetNamespace, XsdLocation); + + using (var doc = XmlReader.Create(XmlContentLocation)) + { + var result = (IXPathNavigable)null; + + try + { + result = validator.Validate(doc); + } + catch (ValidationException ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + string expectedMessage = "Attributes sex (Female) and title (Mr) must have compatible values on element customer."; + Xunit.Assert.True(ex.Message.Contains(expectedMessage)); + } + Xunit.Assert.Null(result); + } + } + } + diff --git a/src/Schematron.Tests/packages.config b/src/Schematron.Tests/packages.config deleted file mode 100644 index dcbf45e..0000000 --- a/src/Schematron.Tests/packages.config +++ /dev/null @@ -1,10 +0,0 @@ -ο»Ώ - - - - - - - - - \ No newline at end of file diff --git a/src/Schematron.WebServices/Properties/AssemblyInfo.cs b/src/Schematron.WebServices/Properties/AssemblyInfo.cs deleted file mode 100644 index 5138a9b..0000000 --- a/src/Schematron.WebServices/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ -ο»Ώusing System.Reflection; - -[assembly: AssemblyTitle ("Schematron.NET WebServices Extension")] -[assembly: AssemblyDescription ("Extensions for SOAP-based .NET WebServices")] \ No newline at end of file diff --git a/src/Schematron.WebServices/Schematron.WebServices.csproj b/src/Schematron.WebServices/Schematron.WebServices.csproj deleted file mode 100644 index c50c909..0000000 --- a/src/Schematron.WebServices/Schematron.WebServices.csproj +++ /dev/null @@ -1,60 +0,0 @@ -ο»Ώ - - - - Debug - AnyCPU - {D55599AF-DA8B-4FFE-8F99-C2A2DCEE45EC} - Library - Properties - Schematron.WebServices - Schematron.WebServices - v4.0 - ..\bin\$(Configuration)\ - $(OutputPath)Schematron.WebServices.xml - true - full - prompt - 4 - VERSION - - - false - DEBUG;TRACE - - - true - TRACE - - - - - - - - - - Properties\GlobalAssemblyInfo.cs - - - - - - - - {db7369f1-69cf-4821-98e3-9d6422a7cc36} - Schematron - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - \ No newline at end of file diff --git a/src/Schematron.WebServices/ValidationAttribute.cs b/src/Schematron.WebServices/ValidationAttribute.cs deleted file mode 100644 index b47bd48..0000000 --- a/src/Schematron.WebServices/ValidationAttribute.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Web.Services.Protocols; - -namespace Schematron -{ - /// - /// Attribute applied to webmethods that require Schematron validation. - /// - [AttributeUsage(AttributeTargets.Method)] - public class ValidationAttribute : SoapExtensionAttribute - { - #region Ctor Overloads - - /// - /// Initializes the attribute for a certain schema. - /// - /// The schema to validate the webmethod with. - public ValidationAttribute(string schema) : this(new string[] { schema }, OutputFormatting.Simple) - { - } - - /// - /// Initializes the attribute for a certain schema. - /// - /// The set of schemas to validate the webmethod with. - public ValidationAttribute(string[] schemas) : this(schemas, OutputFormatting.Simple) - { - } - - /// - /// Initializes the attribute for a certain schema. - /// - /// The schema to validate the webmethod with. - /// The formatting to use for the validation output. - public ValidationAttribute(string schema, OutputFormatting formatting) : this(new string[] { schema }, formatting) - { - } - - /// - /// Initializes the attribute for a certain schema. - /// - /// The set of schemas to validate the webmethod with. - /// The formatting to use for the validation output. - public ValidationAttribute(string[] schemas, OutputFormatting formatting) - { - this.schemas = schemas; - this.formatting = formatting; - } - - #endregion Ctor Overloads - - #region Properties - - /// - /// Gets the location of the schemas to use. - /// - public string[] Schemas - { - get { return schemas; } - set { schemas = value; } - } string[] schemas; - - /// - /// Gets/Sets the phase to use for validation. - /// - public string Phase - { - get { return phase; } - set { phase = value; } - } string phase; - - /// - /// Gets/Sets the formatting to use for validation errors. - /// - public OutputFormatting Formatting - { - get { return formatting; } - set { formatting = value; } - } OutputFormatting formatting; - - /// - /// Gets/Sets a type that implements to use - /// to format error messages. If set, this property takes precedence - /// over . - /// - public Type FormattingType - { - get { return formatType; } - set { formatType = value; } - } Type formatType; - - #endregion Properties - - #region Overrides - - /// - /// Gets the type of this extension attribute, which is always - /// . - /// - public override Type ExtensionType - { - get { return typeof(ValidationExtension); } - } - - /// - /// Gets/Sets the priority of the extension. - /// - public override int Priority - { - get { return priority; } - set { priority = value; } - } int priority = 0; - - #endregion Overrides - } -} diff --git a/src/Schematron.WebServices/ValidationExtension.cs b/src/Schematron.WebServices/ValidationExtension.cs deleted file mode 100644 index c3160ce..0000000 --- a/src/Schematron.WebServices/ValidationExtension.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.IO; -using System.Reflection; -using System.Xml; -using System.Xml.Schema; -using System.Web.Services.Protocols; - -namespace Schematron -{ - /// - /// Extension that allows Schematron validation on webmethods. - /// - internal class ValidationExtension : SoapExtension - { - ValidationExtension.ExtensionInitializer initializer; - - public override object GetInitializer(Type serviceType) - { - throw new NotImplementedException("Schematron validation must be specified through the ValidationAttribute."); - } - - public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute) - { - if (!(attribute is ValidationAttribute)) - throw new NotSupportedException("Schematron validation must be enabled through the ValidationAttribute."); - - return new ExtensionInitializer(methodInfo.DeclaringType.Assembly, (ValidationAttribute)attribute); - } - - public override void Initialize(object initializer) - { - this.initializer = (ExtensionInitializer) initializer; - } - - public override void ProcessMessage(SoapMessage message) - { - if (message is SoapClientMessage) - throw new NotSupportedException("Schematron validation is only available on the server side."); - - if (message.Stage == SoapMessageStage.BeforeDeserialize) - { - Validator v = new Validator(initializer.Validation.Formatting); - if (initializer.Formatter != null) - v.Formatter = initializer.Formatter; - - // Add cached schemas. - v.Schemas.AddRange(initializer.SchematronSchemas); - v.XmlSchemas.Add(initializer.XmlSchemas); - if (initializer.Validation.Phase != null) - v.Phase = initializer.Validation.Phase; - - try - { - // Only validate body. - XmlTextReader reader = new XmlTextReader(message.Stream); - object body = reader.NameTable.Add("Body"); - reader.MoveToContent(); - // First read will get us into the envelope. - while (reader.Read()) - { - if ((object)reader.LocalName == body) - { - // Skip the webmethod wrapper element too. - reader.Read(); - string inner = reader.ReadInnerXml(); - v.Validate(new StringReader(inner)); - } - } - } - catch (ValidationException vex) - { - throw new SoapException("Incoming document is invalid.\n" + - vex.Message, SoapException.ClientFaultCode); - } - finally - { - // Reset stream position for normal processing to continue. - message.Stream.Position = 0; - } - } - } - - #region ExtensionInitializer - - private class ExtensionInitializer - { - public Assembly SchemaAssembly; - public ValidationAttribute Validation; - public SchemaCollection SchematronSchemas = new SchemaCollection(); - public XmlSchemaSet XmlSchemas = new XmlSchemaSet(); - public Formatters.IFormatter Formatter; - public ExtensionInitializer(Assembly schemaAssembly, ValidationAttribute validation) - { - this.SchemaAssembly = schemaAssembly; - this.Validation = validation; - - // Use temporary validator to load schemas and validate them at the same time. - Validator val = new Validator(OutputFormatting.Log); - - // Cache all schemas. - foreach (string schema in validation.Schemas) - { - // First try full resource name. - Stream memstream = schemaAssembly.GetManifestResourceStream(schema); - if (memstream == null) - { - // Retry appending assembly name. - string resourcebase = schemaAssembly.FullName.Substring( - 0, schemaAssembly.FullName.IndexOf(',')); - memstream = schemaAssembly.GetManifestResourceStream( - resourcebase + "." + schema); - } - - if (memstream == null) - throw new FileNotFoundException("Schema not found.", schema); - - XmlTextReader reader = new XmlTextReader(memstream); - val.AddSchema(reader); - } - - // Pull schemas out so that validator can be GC-collected; - XmlSchema[] xsds = new XmlSchema[val.XmlSchemas.Count]; - val.XmlSchemas.CopyTo(xsds, 0); - Schema[] schs = new Schema[val.Schemas.Count]; - val.Schemas.CopyTo(schs, 0); - - SchematronSchemas.AddRange(schs); - foreach (XmlSchema xsd in xsds) - { - XmlSchemas.Add(xsd); - } - - // Detemine custom formatter. - if (validation.FormattingType != null) - { - if (!typeof(Formatters.IFormatter).IsAssignableFrom(validation.FormattingType)) - { - throw new ArgumentException("Assigned formatter type " + validation.FormattingType.FullName + - " does not implement the required " + typeof(Formatters.IFormatter).FullName + " interface."); - } - // Try to retrieve empty constructor - ConstructorInfo ctor = validation.FormattingType.GetConstructor(Type.EmptyTypes); - if (ctor == null) - { - throw new ArgumentException("Assigned formatter type " + validation.FormattingType.FullName + - " doesn't have a parameterless constructor"); - } - - Formatter = (Formatters.IFormatter) Activator.CreateInstance(validation.FormattingType); - } - } - } - - #endregion ExtensionInitializer - } -} diff --git a/src/Schematron.WebServices/packages.config b/src/Schematron.WebServices/packages.config deleted file mode 100644 index bd6f14c..0000000 --- a/src/Schematron.WebServices/packages.config +++ /dev/null @@ -1,4 +0,0 @@ -ο»Ώ - - - \ No newline at end of file diff --git a/src/Schematron.nuspec b/src/Schematron.nuspec deleted file mode 100644 index a42b30a..0000000 --- a/src/Schematron.nuspec +++ /dev/null @@ -1,17 +0,0 @@ - - - - $id$ - $version$ - Schematron.NET: a C# high-performance implementation of Schematron ISO/IEC standard - Daniel Cazzulino - kzu - A C# high-performance implementation of Schematron ISO/IEC standard - 2015 Β© Daniel Cazzulino - - - - - - - \ No newline at end of file diff --git a/src/Schematron.sln b/src/Schematron.sln deleted file mode 100644 index 7c7b5f6..0000000 --- a/src/Schematron.sln +++ /dev/null @@ -1,46 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Schematron", "Schematron\Schematron.csproj", "{DB7369F1-69CF-4821-98E3-9D6422A7CC36}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{CEE7139C-CEDE-4967-825B-CC6DC0188685}" - ProjectSection(SolutionItems) = preProject - ..\doc\Findings.doc = ..\doc\Findings.doc - ..\doc\Notes.txt = ..\doc\Notes.txt - ..\doc\Schematron.xml = ..\doc\Schematron.xml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Schematron.WebServices", "Schematron.WebServices\Schematron.WebServices.csproj", "{D55599AF-DA8B-4FFE-8F99-C2A2DCEE45EC}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{494E6692-1CC3-4DBE-BF35-09E356E52608}" - ProjectSection(SolutionItems) = preProject - ..\build.cmd = ..\build.cmd - ..\build.proj = ..\build.proj - Schematron.nuspec = Schematron.nuspec - ..\VERSION = ..\VERSION - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Schematron.Tests", "Schematron.Tests\Schematron.Tests.csproj", "{0CE72032-070F-479E-8ADD-4C04AE53C1B6}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DB7369F1-69CF-4821-98E3-9D6422A7CC36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DB7369F1-69CF-4821-98E3-9D6422A7CC36}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DB7369F1-69CF-4821-98E3-9D6422A7CC36}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DB7369F1-69CF-4821-98E3-9D6422A7CC36}.Release|Any CPU.Build.0 = Release|Any CPU - {D55599AF-DA8B-4FFE-8F99-C2A2DCEE45EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D55599AF-DA8B-4FFE-8F99-C2A2DCEE45EC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D55599AF-DA8B-4FFE-8F99-C2A2DCEE45EC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D55599AF-DA8B-4FFE-8F99-C2A2DCEE45EC}.Release|Any CPU.Build.0 = Release|Any CPU - {0CE72032-070F-479E-8ADD-4C04AE53C1B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0CE72032-070F-479E-8ADD-4C04AE53C1B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0CE72032-070F-479E-8ADD-4C04AE53C1B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0CE72032-070F-479E-8ADD-4C04AE53C1B6}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/Schematron/Assert.cs b/src/Schematron/Assert.cs index aebc157..e735f49 100644 --- a/src/Schematron/Assert.cs +++ b/src/Schematron/Assert.cs @@ -1,30 +1,28 @@ -using System; +namespace Schematron; -namespace Schematron +/// +/// Represents an assert element of the Schematron schema. +/// +/// +/// As stated in the , this is +/// the lowest element in a Schematron schema. This element contains the expression +/// to execute in the context of its parent . +/// If the results of the execution of the expression are False, the +/// assert fails and the correponding message will be displayed. +/// +/// Constructor is not public. To programatically create an instance of this +/// class use the factory method. +/// +/// +/// +/// +public class Assert : Test { - /// - /// Represents an assert element of the Schematron schema. - /// - /// - /// As stated in the , this is - /// the lowest element in a Schematron schema. This element contains the expression - /// to execute in the context of its parent . - /// If the results of the execution of the expression are False, the - /// assert fails and the correponding message will be displayed. - /// - /// Constructor is not public. To programatically create an instance of this - /// class use the factory method. - /// - /// - /// - /// - public class Assert : Test - { - /// Constructs a new Assert object. - /// XPath expression to test. - /// Message to display if the assert fails. - internal protected Assert(string test, string message) : base(test, message) - { - } - } + /// Constructs a new Assert object. + /// XPath expression to test. + /// Message to display if the assert fails. + internal protected Assert(string test, string message) : base(test, message) + { + } } + diff --git a/src/Schematron/AsyncAssertEvaluate.cs b/src/Schematron/AsyncAssertEvaluate.cs deleted file mode 100644 index 5d8195c..0000000 --- a/src/Schematron/AsyncAssertEvaluate.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Xml.XPath; - -namespace NMatrix.Schematron -{ - /// - /// Private delegate used in to call - /// evaluation asynchronously against an object. - /// - /// - /// This in an example of the delegate creation and asynchronous execution. - /// AsyncAssertEvaluate eval = new AsyncAssertEvaluate(EvaluateAssert); - /// eval.BeginInvoke( - /// assert, - /// context, - /// new AsyncCallback(OnAssertCompleted), - /// state); - /// - /// - /// - /// - internal delegate string AsyncAssertEvaluate(Assert assert, XPathNavigator context); -} diff --git a/src/Schematron/AsyncEvaluationContext.cs b/src/Schematron/AsyncEvaluationContext.cs deleted file mode 100644 index 3d45be5..0000000 --- a/src/Schematron/AsyncEvaluationContext.cs +++ /dev/null @@ -1,497 +0,0 @@ -using System; -using System.IO; -using System.Text; -using System.Xml; -using System.Xml.Schema; -using System.Xml.XPath; -using System.Collections; -using System.Threading; -using System.Runtime.Remoting.Messaging; -using NMatrix.Schematron.Formatters; - -namespace NMatrix.Schematron -{ - /// - /// Evaluates and elements asynchronously. - /// - /// - /// See for a description of the purpose and - /// of evaluation contexts, and where are they used. - /// - /// - /// Currently this class is not working properly. We're getting thread contention - /// when the method is called. We have tried - /// almost every options there is to retrieve the handles. The one with the contention - /// problem is the one using method - /// to enqueue the method execution. - /// - /// Another approach tried (and commented in the code) is asynchronous execution of the - /// evaluation method using delegate. This produces - /// and exception because apparently an invalid is returned. - /// - /// So currently, only the is in use. - /// - /// - internal class AsyncEvaluationContext : EvaluationContextBase - { - /// Creates the evaluation context - public AsyncEvaluationContext() - { - } - - /// - /// Starts the evaluation process. - /// - /// - /// When the process is finished, the results are placed - /// in the property. - /// - public override void Start() - { - Messages = String.Empty; - - // If no phase was received, try the default phase defined for the schema. - // If no default phase is defined, all patterns will be tested (see the - // Schema object comments. - if (Phase == String.Empty) - { - if (Schema.DefaultPhase == String.Empty) - { - Phase = Schematron.Phase.All; - } - else - { - Phase = Schema.DefaultPhase; - } - } - - string res = Evaluate(Schema.Phases[Phase]); - Messages = Formatter.Format(Schema, res, Source); - } - - /// - /// Evaluates the selected . - /// - /// - /// Processing is synchronous, as patterns must - /// me evaluated in document order. - /// - /// As most of the other evaluation methods, it repositions the - /// navigator on the root node. - /// - /// - /// The to evaluate. - /// The messages accumulated by the evaluation of all the child - /// , or if there are no messages. - private string Evaluate(Phase phase) - { - Source.MoveToRoot(); - StringBuilder sb = new StringBuilder(); - - foreach (Pattern pt in phase.Patterns) - { - sb.Append(Evaluate(pt)); - } - - string res = sb.ToString(); - return Formatter.Format(phase, res, Source); - } - - /// - /// Evaluates the selected . - /// - /// - /// Processing is synchronous, as rules must me evaluated in document order. - /// - /// As most of the other evaluation methods, it repositions the - /// navigator on the root node. - /// - /// - /// Clears the object before - /// proceeding, as the restriction about node mathing (see ) - /// applies only inside a single pattern. - /// - /// - /// The to evaluate. - /// The messages accumulated by the evaluation of all the child - /// , or if there are no messages. - private string Evaluate(Pattern pattern) - { - Source.MoveToRoot(); - StringBuilder sb = new StringBuilder(); - - // Reset matched nodes, as across patters, nodes can be - // evaluated more than once. - Matched.Clear(); - - foreach (Rule rule in pattern.Rules) - { - sb.Append(Evaluate(rule)); - } - - string res = sb.ToString(); - return Formatter.Format(pattern, res, Source); - } - - /// - /// Evaluates the selected . - /// - /// - /// Here is where asynchronous becomes. and - /// are queued using the class. - /// - /// Nodes matched by this are added to the list of - /// nodes to skip in the next rule, using the method. - /// This object is a strategy object which implements different algorithms for matching and - /// saving node references, as the actual implementation provides - /// different methods for accessing the underlying source. - /// - /// This makes the implementation both performant and compliant with - /// the restriction about node mathing (see ) in the spec. - /// - /// - /// - /// - /// - /// - /// As most of the other evaluation methods, it repositions the - /// navigator on the root node. - /// - /// Here is where the multithreading problems arise, which are not - /// due to the schema design itself, but this specific evaluation process. - /// The intent it to evaluate asserts and reports in parallel, to get the - /// most out of the CPU. - /// - /// - /// The to evaluate. - /// The messages accumulated by the evaluation of all the child - /// and . - /// - /// The rule to evaluate is abstract (see ). - /// - private string Evaluate(Rule rule) - { - if (rule.IsAbstract) - throw new InvalidOperationException("The Rule is abstract, so it can't be evaluated."); - - StringBuilder sb = new StringBuilder(); - Source.MoveToRoot(); - XPathNodeIterator nodes = Source.Select(rule.CompiledExpression); - ArrayList evaluables = new ArrayList(nodes.Count); - - while (nodes.MoveNext()) - { - if (!Matched.IsMatched(nodes.Current)) - { - // Add the navigator to the list to be evaluated and to - // the list of pattern-level nodes matched so far. - XPathNavigator curr = nodes.Current.Clone(); - evaluables.Add(curr); - Matched.AddMatched(curr); - } - } - - ArrayList handles = new ArrayList(rule.Asserts.Count + rule.Reports.Count); - - foreach (Assert asr in rule.Asserts) - { - //AsyncAssertEvaluate eval = new AsyncAssertEvaluate(EvaluateAssert); - foreach (XPathNavigator node in evaluables) - { - XPathNavigator ctx = node.Clone(); - - WaitHandle wh = new AutoResetEvent(true); - WaitOrTimerCallback cb = new WaitOrTimerCallback(OnAssertEvaluate); - - ThreadPool.RegisterWaitForSingleObject(wh, cb, - new AsyncAssertState(asr, sb, ctx), Timeout.Infinite, true); - - handles.Add(wh); - //handles.Add(eval.BeginInvoke( - // asr, ctx, new AsyncCallback(OnAssertCompleted), - // new AsyncAssertState(asr, sb, ctx)).AsyncWaitHandle); - - //Synchronous evaluation - //string str = EvaluateAssert(asr, node.Clone()); - //if (str != String.Empty) - // sb.Append(str).Append(System.Environment.NewLine); - } - } - - foreach (Report rpt in rule.Reports) - { - //AsyncReportEvaluate eval = new AsyncReportEvaluate(EvaluateReport); - foreach (XPathNavigator node in evaluables) - { - XPathNavigator ctx = node.Clone(); - - WaitHandle wh = new AutoResetEvent(true); - WaitOrTimerCallback cb = new WaitOrTimerCallback(OnReportEvaluate); - - ThreadPool.RegisterWaitForSingleObject(wh, cb, - new AsyncReportState(rpt, sb, ctx), Timeout.Infinite, true); - - handles.Add(wh); - //handles.Add(eval.BeginInvoke( - // rpt, ctx, new AsyncCallback(OnReportCompleted), - // new AsyncReportState(rpt, sb, ctx)).AsyncWaitHandle); - - //Synchronous evaluation - //string str = EvaluateReport(rpt, node.Clone()); - //if (str != String.Empty) - // sb.Append(str).Append(System.Environment.NewLine); - } - } - - try - { - //TODO: I think we are getting contention here. Anyone can help? - WaitHandle[] waithandles = new WaitHandle[handles.Count]; - handles.CopyTo(waithandles); - WaitHandle.WaitAll(waithandles); - } - catch (Exception ex) - { - System.Diagnostics.Debug.Fail(ex.ToString()); - } - - string res = sb.ToString(); - return Formatter.Format(rule, res, Source); - } - - /// - /// Asynchronous evaluation of an - /// - /// - /// This method is used as the delegate for the - /// version of the asynchonous call. - /// - /// This just retrieves the state, casts it to and executes the - /// method, passing the values received. It locks on the received - /// to append the messages from evaluation. - /// - /// - /// State for the execution. - /// If the timed out. - private void OnAssertEvaluate(object state, bool timedOut) - { - AsyncAssertState st = (AsyncAssertState) state; - System.Diagnostics.Debug.WriteLine("Executing assert on thread: " + - Thread.CurrentThread.GetHashCode()); - - lock(st.Builder) - { - st.Builder.Append(EvaluateAssert(st.Assert, st.Context)); - } - } - - /// - /// Asynchronous evaluation of a - /// - /// - /// This method is used as the delegate for the - /// version of the asynchonous call. - /// - /// This just retrieves the state, casts it to and executes the - /// method, passing the values received. It locks on the received - /// to append the messages from evaluation. - /// - /// - /// State for the execution. - /// If the timed out. - private void OnReportEvaluate(object state, bool timedOut) - { - AsyncReportState st = (AsyncReportState) state; - System.Diagnostics.Debug.WriteLine("Executing report on thread: " + - Thread.CurrentThread.GetHashCode()); - - lock(st.Builder) - { - st.Builder.Append(EvaluateReport(st.Report, st.Context)); - } - } - - /// - /// Performs the evaluation of the . - /// - /// - /// This is where the actual assert expression is evaluated. - /// Is called synchonously from the or - /// using the delegate asynchronously, with - /// BeginInvoke and EndInvoke. If the - /// returns false, a formated message is generated - /// from the property. - /// - /// The to evaluate. - /// The context node for the execution. - /// The formatted message for a failing , or - /// . - /// - private string EvaluateAssert(Assert assert, XPathNavigator context) - { - object eval = context.Evaluate(assert.CompiledExpression); - bool result = true; - - if (assert.ReturnType == XPathResultType.Boolean) - result = (bool)eval; - else if (assert.ReturnType == XPathResultType.NodeSet && - ((XPathNodeIterator)eval).Count == 0) - result = false; - - if (!result) - return Formatter.Format(assert, context); - - return String.Empty; - } - - /// - /// Performs the evaluation of the . - /// - /// - /// This is where the actual report expression is evaluated. - /// Is called synchonously from the or - /// using the delegate asynchronously, with - /// BeginInvoke and EndInvoke. If - /// the returns true, - /// a formated message is generated from the property. - /// - /// The to evaluate. - /// The context node for the execution. - /// The formatted message for a succesful , or - /// . - /// - private string EvaluateReport(Report report, XPathNavigator context) - { - object eval = context.Evaluate(report.CompiledExpression); - bool result = false; - - if (report.ReturnType == XPathResultType.Boolean) - result = (bool)eval; - else if (report.ReturnType == XPathResultType.NodeSet && - ((XPathNodeIterator)eval).Count != 0) - result = true; - - if (result) - return Formatter.Format(report, context); - - return String.Empty; - } - - /// - /// Callback for asynchonous delegate execution. - /// - /// - /// This is the to use when executing - /// asynchronously the method. - /// It completes the call by calling EndInvoke, retrieving the results - /// and appending the messages to the received - /// in the property. - /// - /// - /// This in an example of the delegate creation and asynchronous execution. - /// AsyncAssertEvaluate eval = new AsyncAssertEvaluate(EvaluateAssert); - /// eval.BeginInvoke(asr, ctx, - /// new AsyncCallback(OnAssertCompleted), sb); - /// - /// - /// The object to extract state information from. - private void OnAssertCompleted(IAsyncResult result) - { - try - { - AsyncResult ar = (AsyncResult) result; - AsyncAssertState st = (AsyncAssertState) ar.AsyncState; - AsyncAssertEvaluate eval = (AsyncAssertEvaluate) ar.AsyncDelegate; - string res = eval.EndInvoke(ar); - if (res != String.Empty) - { - lock(st.Builder) - { - st.Builder.Append(res).Append(System.Environment.NewLine); - System.Diagnostics.Debug.WriteLine(res); - } - } - } - catch (Exception ex) - { - System.Diagnostics.Debug.Fail(ex.ToString()); - throw ex; - } - } - - /// - /// Callback for asynchonous delegate execution. - /// - /// - /// This is the to use when executing - /// asynchronously the method. - /// It completes the call by calling EndInvoke, retrieving the results - /// and appending the messages to the received - /// in the property. - /// - /// - /// This in an example of the delegate creation and asynchronous execution. - /// AsyncReportEvaluate eval = new AsyncReportEvaluate(EvaluateReport); - /// eval.BeginInvoke(rpt, ctx, new AsyncCallback(OnReportCompleted), sb); - /// - /// - /// The object to extract state information from. - private void OnReportCompleted(IAsyncResult result) - { - try - { - AsyncResult ar = (AsyncResult) result; - AsyncReportState st = (AsyncReportState) ar.AsyncState; - AsyncReportEvaluate eval = (AsyncReportEvaluate) ar.AsyncDelegate; - string res = eval.EndInvoke(ar); - if (res != String.Empty) - { - lock(st.Builder) - { - st.Builder.Append(res).Append(System.Environment.NewLine); - System.Diagnostics.Debug.WriteLine(res); - } - } - } - catch (Exception ex) - { - System.Diagnostics.Debug.Fail(ex.ToString()); - throw ex; - } - } - - /// - /// Structure to pass state around for asynchonous execution. - /// - struct AsyncAssertState - { - public Assert Assert; - public StringBuilder Builder; - public XPathNavigator Context; - - public AsyncAssertState(Assert assert, StringBuilder builder, XPathNavigator context) - { - Assert = assert; - Builder = builder; - Context = context; - } - } - - /// - /// Structure to pass state around for asynchonous execution. - /// - struct AsyncReportState - { - public Report Report; - public StringBuilder Builder; - public XPathNavigator Context; - - public AsyncReportState(Report report, StringBuilder builder, XPathNavigator context) - { - Report = report; - Builder = builder; - Context = context; - } - } - } -} diff --git a/src/Schematron/AsyncReportEvaluate.cs b/src/Schematron/AsyncReportEvaluate.cs deleted file mode 100644 index 85d58d6..0000000 --- a/src/Schematron/AsyncReportEvaluate.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Xml.XPath; - -namespace NMatrix.Schematron -{ - /// - /// Private delegate used in to call - /// evaluation asynchronously against an object. - /// - /// - /// This in an example of the delegate creation and asynchronous execution. - /// AsyncReportEvaluate eval = new AsyncReportEvaluate(EvaluateReport); - /// eval.BeginInvoke( - /// report, - /// context, - /// new AsyncCallback(OnReportCompleted), - /// state); - /// - /// - /// - /// - internal delegate string AsyncReportEvaluate(Report report, XPathNavigator context); -} diff --git a/src/Schematron/BadSchemaException.cs b/src/Schematron/BadSchemaException.cs index 005ea02..2f1949b 100644 --- a/src/Schematron/BadSchemaException.cs +++ b/src/Schematron/BadSchemaException.cs @@ -1,49 +1,48 @@ -using System; using System.Runtime.Serialization; -namespace Schematron +namespace Schematron; + +/// +/// Represents the an error in the Schematron schema. +/// +/// +/// +[Serializable()] +public class BadSchemaException : ApplicationException { - /// - /// Represents the an error in the Schematron schema. - /// - /// - /// - [Serializable()] - public class BadSchemaException : ApplicationException - { - /// - /// Initializes a new instance of the class. - /// - public BadSchemaException() - { - } + /// + /// Initializes a new instance of the class. + /// + public BadSchemaException() + { + } - /// - /// Initializes a new instance of the class. - /// - /// The error message that explains the reason for the exception. - public BadSchemaException(string message) : base(message) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The error message that explains the reason for the exception. + public BadSchemaException(string message) : base(message) + { + } - /// - /// Initializes a new instance of the class. - /// - /// Info - /// Context - protected BadSchemaException(SerializationInfo info, StreamingContext context) : - base(info, context) - { - } + /// + /// Initializes a new instance of the class. + /// + /// Info + /// Context + protected BadSchemaException(SerializationInfo info, StreamingContext context) : + base(info, context) + { + } - /// - /// Initializes a new instance of the class. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception. - public BadSchemaException(string message, Exception innerException) : - base(message, innerException) - { - } - } + /// + /// Initializes a new instance of the class. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. + public BadSchemaException(string message, Exception innerException) : + base(message, innerException) + { + } } + diff --git a/src/Schematron/CompiledExpressions.cs b/src/Schematron/CompiledExpressions.cs index 82364c5..9c2288a 100644 --- a/src/Schematron/CompiledExpressions.cs +++ b/src/Schematron/CompiledExpressions.cs @@ -1,83 +1,82 @@ -using System; using System.Xml; using System.Xml.XPath; -namespace Schematron +namespace Schematron; + +/// +/// This class keeps static precompiled expressions used +/// in the Schematron schema loading and validation processes. +/// +/// +/// All expressions are compiled against the +/// object. All the objects are initialized with +/// the for schematron and XML Schema +/// namespaces resolution. +/// +/// +/// +class CompiledExpressions { - /// - /// This class keeps static precompiled expressions used - /// in the Schematron schema loading and validation processes. - /// - /// - /// All expressions are compiled against the - /// object. All the objects are initialized with - /// the for schematron and XML Schema - /// namespaces resolution. - /// - /// - /// - class CompiledExpressions - { - public static XPathExpression Schema; - public static XPathExpression EmbeddedSchema; - public static XPathExpression Phase; - public static XPathExpression Pattern; - public static XPathExpression AbstractRule; - public static XPathExpression ConcreteRule; - public static XPathExpression RuleExtends; - public static XPathExpression Assert; - public static XPathExpression Report; - - //Not implemented yet. - //public static XPathExpression Diagnostics; - - static CompiledExpressions() - { - XmlNamespaceManager mgr = Config.DefaultNsManager; - - Schema = Config.DefaultNavigator.Compile("//sch:schema"); - Schema.SetContext(mgr); - - EmbeddedSchema = Config.DefaultNavigator.Compile("xsd:schema/xsd:annotation/xsd:appinfo/*"); - EmbeddedSchema.SetContext(mgr); - - // We use descendant-or-self for the XPath because the phase will always be - // contained in a parent element, which is the context for the execution, - // usually the sch:schema element or an xsd:appinfo. - Phase = Config.DefaultNavigator.Compile("descendant-or-self::sch:phase"); - Phase.SetContext(mgr); - - Pattern = Config.DefaultNavigator.Compile("//sch:pattern"); - Pattern.SetContext(mgr); - - // Abstract rules can be anywhere on the schema too. - AbstractRule = Config.DefaultNavigator.Compile("//sch:rule[@abstract=\"true\"]"); - AbstractRule.SetContext(mgr); - - // We use descendant-or-self because we want to search for rules under the current pattern context. - ConcreteRule = Config.DefaultNavigator.Compile("descendant-or-self::sch:rule[not(@abstract) or @abstract=\"false\"]"); - ConcreteRule.SetContext(mgr); - - // We use descendant-or-self because sch:extends is always a child of its context, sch:rule. - RuleExtends = Config.DefaultNavigator.Compile("descendant-or-self::sch:extends"); - RuleExtends.SetContext(mgr); - - // We use descendant-or-self because sch:assert is always a child of its context, sch:rule. - Assert = Config.DefaultNavigator.Compile("descendant-or-self::sch:assert"); - Assert.SetContext(mgr); - - // We use descendant-or-self because sch:report is always a child of its context, sch:rule. - Report = Config.DefaultNavigator.Compile("descendant-or-self::sch:report"); - Report.SetContext(mgr); - - // We use descendant-or-self because sch:diagnostics can only be used inside a sch:schema element. - // Currently not in implemented. - // Diagnostics = Config.DefaultNavigator.Compile("descendant-or-self::sch:diagnostics"); - // Diagnostics.SetContext(mgr); - } - - private CompiledExpressions() - { - } - } + public static XPathExpression Schema; + public static XPathExpression EmbeddedSchema; + public static XPathExpression Phase; + public static XPathExpression Pattern; + public static XPathExpression AbstractRule; + public static XPathExpression ConcreteRule; + public static XPathExpression RuleExtends; + public static XPathExpression Assert; + public static XPathExpression Report; + + //Not implemented yet. + //public static XPathExpression Diagnostics; + + static CompiledExpressions() + { + XmlNamespaceManager mgr = Config.DefaultNsManager; + + Schema = Config.DefaultNavigator.Compile("//sch:schema"); + Schema.SetContext(mgr); + + EmbeddedSchema = Config.DefaultNavigator.Compile("xsd:schema/xsd:annotation/xsd:appinfo/*"); + EmbeddedSchema.SetContext(mgr); + + // We use descendant-or-self for the XPath because the phase will always be + // contained in a parent element, which is the context for the execution, + // usually the sch:schema element or an xsd:appinfo. + Phase = Config.DefaultNavigator.Compile("descendant-or-self::sch:phase"); + Phase.SetContext(mgr); + + Pattern = Config.DefaultNavigator.Compile("//sch:pattern"); + Pattern.SetContext(mgr); + + // Abstract rules can be anywhere on the schema too. + AbstractRule = Config.DefaultNavigator.Compile("//sch:rule[@abstract=\"true\"]"); + AbstractRule.SetContext(mgr); + + // We use descendant-or-self because we want to search for rules under the current pattern context. + ConcreteRule = Config.DefaultNavigator.Compile("descendant-or-self::sch:rule[not(@abstract) or @abstract=\"false\"]"); + ConcreteRule.SetContext(mgr); + + // We use descendant-or-self because sch:extends is always a child of its context, sch:rule. + RuleExtends = Config.DefaultNavigator.Compile("descendant-or-self::sch:extends"); + RuleExtends.SetContext(mgr); + + // We use descendant-or-self because sch:assert is always a child of its context, sch:rule. + Assert = Config.DefaultNavigator.Compile("descendant-or-self::sch:assert"); + Assert.SetContext(mgr); + + // We use descendant-or-self because sch:report is always a child of its context, sch:rule. + Report = Config.DefaultNavigator.Compile("descendant-or-self::sch:report"); + Report.SetContext(mgr); + + // We use descendant-or-self because sch:diagnostics can only be used inside a sch:schema element. + // Currently not in implemented. + // Diagnostics = Config.DefaultNavigator.Compile("descendant-or-self::sch:diagnostics"); + // Diagnostics.SetContext(mgr); + } + + CompiledExpressions() + { + } } + diff --git a/src/Schematron/Config.cs b/src/Schematron/Config.cs index c99076c..b6a5ca2 100644 --- a/src/Schematron/Config.cs +++ b/src/Schematron/Config.cs @@ -1,176 +1,173 @@ -using System; using System.Xml; using System.Xml.XPath; -using System.Text.RegularExpressions; -using System.Runtime.Remoting.Messaging; using Schematron.Formatters; -namespace Schematron +namespace Schematron; + +/// +/// Provides global settings for Schematron validation. +/// +/// +/// This class is public to allow inheritors of Schematron elements +/// to use these global settings. +/// +/// +/// +public class Config { - /// - /// Provides global settings for Schematron validation. - /// - /// - /// This class is public to allow inheritors of Schematron elements - /// to use these global settings. - /// - /// - /// - public class Config - { - /// - /// Initializes global settings. - /// - static Config() - { - // Default formatter outputs in text format a log with results. - _formatter = new LogFormatter(); - - //TODO: create and load the schematron full and embedded versions for validation. - _embedded = new Schema(); - _embedded.Phases.Add(_embedded.CreatePhase(Phase.All)); - _full = new Schema(); - _full.Phases.Add(_full.CreatePhase(Phase.All)); - - //TODO: should we move all the schema language elements to a resource file? - _navigator = new XmlDocument().CreateNavigator(); - _navigator.NameTable.Add("active"); - _navigator.NameTable.Add("pattern"); - _navigator.NameTable.Add("assert"); - _navigator.NameTable.Add("test"); - _navigator.NameTable.Add("role"); - _navigator.NameTable.Add("id"); - _navigator.NameTable.Add("diagnostics"); - _navigator.NameTable.Add("icon"); - _navigator.NameTable.Add("subject"); - _navigator.NameTable.Add("diagnostic"); - _navigator.NameTable.Add("dir"); - _navigator.NameTable.Add("emph"); - _navigator.NameTable.Add("extends"); - _navigator.NameTable.Add("rule"); - _navigator.NameTable.Add("key"); - _navigator.NameTable.Add("name"); - _navigator.NameTable.Add("path"); - _navigator.NameTable.Add("ns"); - _navigator.NameTable.Add("uri"); - _navigator.NameTable.Add("prefix"); - _navigator.NameTable.Add("p"); - _navigator.NameTable.Add("class"); - _navigator.NameTable.Add("see"); - _navigator.NameTable.Add("phase"); - _navigator.NameTable.Add("fpi"); - _navigator.NameTable.Add("report"); - _navigator.NameTable.Add("context"); - _navigator.NameTable.Add("abstract"); - _navigator.NameTable.Add("schema"); - _navigator.NameTable.Add("schemaVersion"); - _navigator.NameTable.Add("defaultPhase"); - _navigator.NameTable.Add("version"); - _navigator.NameTable.Add("span"); - _navigator.NameTable.Add("title"); - _navigator.NameTable.Add("value-of"); - _navigator.NameTable.Add("select"); - - //Namespace manager initialization - _nsmanager = new XmlNamespaceManager(_navigator.NameTable); - _nsmanager.AddNamespace(String.Empty, Schema.Namespace); - _nsmanager.AddNamespace("sch", Schema.Namespace); - _nsmanager.AddNamespace("xsd", System.Xml.Schema.XmlSchema.Namespace); - } - - private Config() - { - } - - static IFormatter _formatter; - - /// - /// The default object to use to format messages from validation. - /// - public static IFormatter DefaultFormatter - { - get { return _formatter; } - } - - static XPathNavigator _navigator; - - /// - /// A default empty navigator used to pre-compile XPath expressions. - /// - /// - /// Compiling doesn't involve any namespace, - /// name table or other specific processing. It's only a parsing procedure that - /// builds the abstract syntax tree for later evaluation. So we can safely - /// use an empty to compile them against. - /// - /// - /// expr = Config.DefaultNavigator.Compile("//sch:pattern"); - /// other code; - /// - /// - /// - /// - /// - internal static XPathNavigator DefaultNavigator - { - // Returning a cloned navigator appeared to solve the threading issues - // we had, because a single navigator was being used to compile all the - // expressions in all potential threads. - get { return _navigator.Clone(); } - } - - static XmlNamespaceManager _nsmanager; - - /// - /// Manager to use when executing expressions that validate or - /// load Schematron and Embedded Schematron schemas. - /// - public static XmlNamespaceManager DefaultNsManager - { - get { return _nsmanager; } - } - - static Schema _full; - - /// - /// A cached schema in Schematron format to validate schematron schemas. - /// - /// This is the version for standalone schemas. - public static Schema FullSchematron - { - get { return _full; } - } - - static Schema _embedded; - - /// - /// A cached schema in Schematron format to validate schematron schemas. - /// - /// This is the version for embedded schemas. - public static Schema EmbeddedSchematron - { - get { return _embedded; } - } - - static string _uid = String.Intern(Guid.NewGuid().ToString()); - - /// - /// A unique identifier to use for internal keys. - /// - public static string UniqueKey - { - get { return _uid; } - } - - /// - /// Force all static constructors in the library. - /// - public static void Setup() - { - System.Diagnostics.Trace.Write("Loading schematron statics..."); - System.Diagnostics.Trace.Write(CompiledExpressions.Schema.ReturnType); - System.Diagnostics.Trace.Write(TagExpressions.Dir.RightToLeft); - System.Diagnostics.Trace.WriteLine(FormattingUtils.XmlErrorPosition.RightToLeft); - } - } + /// + /// Initializes global settings. + /// + static Config() + { + // Default formatter outputs in text format a log with results. + _formatter = new LogFormatter(); + + //TODO: create and load the schematron full and embedded versions for validation. + _embedded = new Schema(); + _embedded.Phases.Add(_embedded.CreatePhase(Phase.All)); + _full = new Schema(); + _full.Phases.Add(_full.CreatePhase(Phase.All)); + + //TODO: should we move all the schema language elements to a resource file? + _navigator = new XmlDocument().CreateNavigator(); + _navigator.NameTable.Add("active"); + _navigator.NameTable.Add("pattern"); + _navigator.NameTable.Add("assert"); + _navigator.NameTable.Add("test"); + _navigator.NameTable.Add("role"); + _navigator.NameTable.Add("id"); + _navigator.NameTable.Add("diagnostics"); + _navigator.NameTable.Add("icon"); + _navigator.NameTable.Add("subject"); + _navigator.NameTable.Add("diagnostic"); + _navigator.NameTable.Add("dir"); + _navigator.NameTable.Add("emph"); + _navigator.NameTable.Add("extends"); + _navigator.NameTable.Add("rule"); + _navigator.NameTable.Add("key"); + _navigator.NameTable.Add("name"); + _navigator.NameTable.Add("path"); + _navigator.NameTable.Add("ns"); + _navigator.NameTable.Add("uri"); + _navigator.NameTable.Add("prefix"); + _navigator.NameTable.Add("p"); + _navigator.NameTable.Add("class"); + _navigator.NameTable.Add("see"); + _navigator.NameTable.Add("phase"); + _navigator.NameTable.Add("fpi"); + _navigator.NameTable.Add("report"); + _navigator.NameTable.Add("context"); + _navigator.NameTable.Add("abstract"); + _navigator.NameTable.Add("schema"); + _navigator.NameTable.Add("schemaVersion"); + _navigator.NameTable.Add("defaultPhase"); + _navigator.NameTable.Add("version"); + _navigator.NameTable.Add("span"); + _navigator.NameTable.Add("title"); + _navigator.NameTable.Add("value-of"); + _navigator.NameTable.Add("select"); + + //Namespace manager initialization + _nsmanager = new XmlNamespaceManager(_navigator.NameTable); + _nsmanager.AddNamespace(String.Empty, Schema.Namespace); + _nsmanager.AddNamespace("sch", Schema.Namespace); + _nsmanager.AddNamespace("xsd", System.Xml.Schema.XmlSchema.Namespace); + } + + Config() + { + } + + static IFormatter _formatter; + + /// + /// The default object to use to format messages from validation. + /// + public static IFormatter DefaultFormatter + { + get { return _formatter; } + } + + static XPathNavigator _navigator; + + /// + /// A default empty navigator used to pre-compile XPath expressions. + /// + /// + /// Compiling doesn't involve any namespace, + /// name table or other specific processing. It's only a parsing procedure that + /// builds the abstract syntax tree for later evaluation. So we can safely + /// use an empty to compile them against. + /// + /// + /// expr = Config.DefaultNavigator.Compile("//sch:pattern"); + /// other code; + /// + /// + /// + /// + /// + internal static XPathNavigator DefaultNavigator + { + // Returning a cloned navigator appeared to solve the threading issues + // we had, because a single navigator was being used to compile all the + // expressions in all potential threads. + get { return _navigator.Clone(); } + } + + static XmlNamespaceManager _nsmanager; + + /// + /// Manager to use when executing expressions that validate or + /// load Schematron and Embedded Schematron schemas. + /// + public static XmlNamespaceManager DefaultNsManager + { + get { return _nsmanager; } + } + + static Schema _full; + + /// + /// A cached schema in Schematron format to validate schematron schemas. + /// + /// This is the version for standalone schemas. + public static Schema FullSchematron + { + get { return _full; } + } + + static Schema _embedded; + + /// + /// A cached schema in Schematron format to validate schematron schemas. + /// + /// This is the version for embedded schemas. + public static Schema EmbeddedSchematron + { + get { return _embedded; } + } + + static string _uid = String.Intern(Guid.NewGuid().ToString()); + + /// + /// A unique identifier to use for internal keys. + /// + public static string UniqueKey + { + get { return _uid; } + } + + /// + /// Force all static constructors in the library. + /// + public static void Setup() + { + System.Diagnostics.Trace.Write("Loading schematron statics..."); + System.Diagnostics.Trace.Write(CompiledExpressions.Schema.ReturnType); + System.Diagnostics.Trace.Write(TagExpressions.Dir.RightToLeft); + System.Diagnostics.Trace.WriteLine(FormattingUtils.XmlErrorPosition.RightToLeft); + } } + diff --git a/src/Schematron/DomMatchedNodes.cs b/src/Schematron/DomMatchedNodes.cs index 99e7e78..b67b190 100644 --- a/src/Schematron/DomMatchedNodes.cs +++ b/src/Schematron/DomMatchedNodes.cs @@ -1,47 +1,47 @@ -using System; +using System.Collections.Generic; using System.Xml; using System.Xml.XPath; -namespace Schematron +namespace Schematron; + +/// +/// Strategy class for matching and keeping references to nodes in an xml document. +/// +/// +/// When an is created from an , +/// it implements the interface, which is used to gain +/// access to the underlying node. +/// +/// +/// +class DomMatchedNodes : IMatchedNodes { - /// - /// Strategy class for matching and keeping references to nodes in an xml document. - /// - /// - /// When an is created from an , - /// it implements the interface, which is used to gain - /// access to the underlying node. - /// - /// - /// - class DomMatchedNodes : IMatchedNodes - { - /// - /// We use an optimized collection for saving the hash codes. - /// - Int32Collection _matched = new Int32Collection(); + /// + /// We use a collection for saving the hash codes. + /// + List _matched = new List(); - /// Initializes an instance of the class. - public DomMatchedNodes() - { - } + /// Initializes an instance of the class. + public DomMatchedNodes() + { + } - /// See . - public bool IsMatched(XPathNavigator node) - { - return _matched.Contains(((IHasXmlNode)node).GetNode().GetHashCode()); - } + /// See . + public bool IsMatched(XPathNavigator node) + { + return _matched.Contains(((IHasXmlNode)node).GetNode().GetHashCode()); + } - /// See . - public void AddMatched(XPathNavigator node) - { - _matched.Add(((IHasXmlNode)node).GetNode().GetHashCode()); - } + /// See . + public void AddMatched(XPathNavigator node) + { + _matched.Add(((IHasXmlNode)node).GetNode().GetHashCode()); + } - /// See . - public void Clear() - { - _matched.Clear(); - } - } + /// See . + public void Clear() + { + _matched.Clear(); + } } + diff --git a/src/Schematron/EvaluableExpression.cs b/src/Schematron/EvaluableExpression.cs index 91cd39a..f12a26f 100644 --- a/src/Schematron/EvaluableExpression.cs +++ b/src/Schematron/EvaluableExpression.cs @@ -1,89 +1,88 @@ -using System; using System.Xml; using System.Xml.XPath; -namespace Schematron +namespace Schematron; + +/// +/// Base class for elements that can be evaluated by an XPath expression. +/// +/// +/// This class performs the expression compilation, and provides +/// access to the context through two methods. +/// +/// +/// +public abstract class EvaluableExpression { - /// - /// Base class for elements that can be evaluated by an XPath expression. - /// - /// - /// This class performs the expression compilation, and provides - /// access to the context through two methods. - /// - /// - /// - public abstract class EvaluableExpression - { - string _xpath; - XPathExpression _expr; - XmlNamespaceManager _ns; + string _xpath = null!; + XPathExpression _expr = null!; + XmlNamespaceManager? _ns; - /// - /// Cache the return type to avoid cloning the expression. - /// - XPathResultType _ret; + /// + /// Cache the return type to avoid cloning the expression. + /// + XPathResultType _ret; - /// Initializes a new instance of the element with the expression specified. - /// The expression to evaluate. - internal protected EvaluableExpression(string xpathExpression) - { - InitializeExpression(xpathExpression); - } + /// Initializes a new instance of the element with the expression specified. + /// The expression to evaluate. + internal protected EvaluableExpression(string xpathExpression) + { + InitializeExpression(xpathExpression); + } - /// Initializes a new instance of the element. - internal protected EvaluableExpression() - { - } + /// Initializes a new instance of the element. + internal protected EvaluableExpression() + { + } - /// Reinitializes the element with a new expression, - /// after the class has already been constructed - /// The expression to evaluate. - protected void InitializeExpression(string xpathExpression) - { - _xpath = xpathExpression; - _expr = Config.DefaultNavigator.Compile(xpathExpression); - _ret = _expr.ReturnType; - if (_ns != null) _expr.SetContext(_ns); - } + /// Reinitializes the element with a new expression, + /// after the class has already been constructed + /// The expression to evaluate. + protected void InitializeExpression(string xpathExpression) + { + _xpath = xpathExpression; + _expr = Config.DefaultNavigator.Compile(xpathExpression); + _ret = _expr.ReturnType; + if (_ns != null) _expr.SetContext(_ns); + } - /// Contains the compiled version of the expression. - /// - /// A clone of the expression is always returned, because the compiled - /// expression is not thread-safe for evaluation. - /// - public XPathExpression CompiledExpression - { - get - { - if (_expr != null) return _expr.Clone(); - else return null; - } - } + /// Contains the compiled version of the expression. + /// + /// A clone of the expression is always returned, because the compiled + /// expression is not thread-safe for evaluation. + /// + public XPathExpression CompiledExpression + { + get + { + if (_expr != null) return _expr.Clone(); + else return null!; + } + } - /// Contains the string version of the expression. - public string Expression - { - get { return _xpath; } - } + /// Contains the string version of the expression. + public string Expression + { + get { return _xpath; } + } - /// Contains the string version of the expression. - public XPathResultType ReturnType - { - get { return _ret; } - } + /// Contains the string version of the expression. + public XPathResultType ReturnType + { + get { return _ret; } + } - /// Returns the manager in use to resolve expression namespaces. - public XmlNamespaceManager GetContext() - { - return _ns; - } + /// Returns the manager in use to resolve expression namespaces. + public XmlNamespaceManager? GetContext() + { + return _ns; + } - /// Sets the manager to use to resolve expression namespaces. - public void SetContext(XmlNamespaceManager nsManager) - { - if (_expr != null) _expr.SetContext(nsManager); - _ns = nsManager; - } - } + /// Sets the manager to use to resolve expression namespaces. + public void SetContext(XmlNamespaceManager nsManager) + { + if (_expr != null) _expr.SetContext(nsManager); + _ns = nsManager; + } } + diff --git a/src/Schematron/EvaluationContextBase.cs b/src/Schematron/EvaluationContextBase.cs index b2a4e3f..0525c26 100644 --- a/src/Schematron/EvaluationContextBase.cs +++ b/src/Schematron/EvaluationContextBase.cs @@ -1,157 +1,155 @@ -using System; -using System.Collections; using System.Text; using System.Xml; using System.Xml.XPath; using Schematron.Formatters; -namespace Schematron +namespace Schematron; + +/// +/// Base class for Schematron evaluation contexts. +/// +/// +/// The schematron elements don't provide evaluation code. They just +/// represent the elements in the . +/// +/// For evaluation purposes, this class provides the iteration and execution +/// process, accumulates results, moves the cursor, etc. Here is where the +/// actual evaluation takes place. This way we isolate the schema design +/// from the different execution models we can use, for example, +/// (we may add asynchonous execution later). +/// +/// +/// The validator classes provided use instances of this strategy object to +/// validate documents provided by the user. +/// +/// +/// +/// +public abstract class EvaluationContextBase { - /// - /// Base class for Schematron evaluation contexts. - /// - /// - /// The schematron elements don't provide evaluation code. They just - /// represent the elements in the . - /// - /// For evaluation purposes, this class provides the iteration and execution - /// process, accumulates results, moves the cursor, etc. Here is where the - /// actual evaluation takes place. This way we isolate the schema design - /// from the different execution models we can use, for example, - /// (we may add asynchonous execution later). - /// - /// - /// The validator classes provided use instances of this strategy object to - /// validate documents provided by the user. - /// - /// - /// - /// - public abstract class EvaluationContextBase - { - /// - /// Keeps a list of nodes already matched. - /// - /// - /// When the property is set, the appropriate - /// strategy for matching nodes is initialized, depending on the specific - /// implementation of the in use. - /// - protected IMatchedNodes Matched; - - /// Creates the evaluation context - public EvaluationContextBase() - { - } - - #region Properties - IFormatter _formatter = Config.DefaultFormatter; - - /// Gets or sets the class to use to format messages. - /// - /// This object is initialized to the instance. - /// Usually, it will be changed based on parameters passed to the validator class, - /// or exposed directly by it. - /// - public IFormatter Formatter - { - get { return _formatter; } - set { _formatter = value; } - } - - StringBuilder _messages = new StringBuilder(); - - /// Gets or sets the messages generated by the validation process. - /// - /// Specific implementations of this class read/write this property - /// while they accumulate validation messages. - /// - public StringBuilder Messages - { - get { return _messages; } - set { _messages = value; } - } - - bool _haserrors = false; - - /// Indicates if errors were found during the current evaluation. - public bool HasErrors - { - get { return _haserrors; } - set { _haserrors = value; } - } - - string _phase = String.Empty; - - /// Gets or sets the specific validation phase to run. - /// - /// Schematron supports the concept of phases, where different sets of - /// patterns can be executed at different times. This phase is initialized - /// to , which will mean all patterns are run. - /// - public string Phase - { - get { return _phase; } - set { _phase = value; } - } - - Schema _schema; - - /// Gets or sets the schema to use for the validation. - public Schema Schema - { - get { return _schema; } - set { _schema = value; } - } - - XPathNavigator _source; - - /// Gets or sets the document to validate. - /// - /// When this property is set, the appropriate - /// strategy is picked, to perform optimum for various navigator implementations. - /// - public XPathNavigator Source - { - get { return _source; } - set - { - _source = value; - if (value is IHasXmlNode) - { - Matched = new DomMatchedNodes(); - } - else if (value is IXmlLineInfo) - { - Matched = new XPathMatchedNodes(); - } - else - { - Matched = new GenericMatchedNodes(); - } - } - } - #endregion - - /// - /// Starts the evaluation process. - /// - /// - /// When the process is finished, the results are placed - /// in the property. - /// - public abstract void Start(); - - /// - /// Resets the state of the current context. - /// - /// - /// By default, it clears the and sets to false. - /// - protected void Reset() - { - _messages = new StringBuilder(); - - } - } -} \ No newline at end of file + /// + /// Keeps a list of nodes already matched. + /// + /// + /// When the property is set, the appropriate + /// strategy for matching nodes is initialized, depending on the specific + /// implementation of the in use. + /// + protected IMatchedNodes Matched = null!; + + /// Creates the evaluation context + public EvaluationContextBase() + { + } + + #region Properties + IFormatter _formatter = Config.DefaultFormatter; + + /// Gets or sets the class to use to format messages. + /// + /// This object is initialized to the instance. + /// Usually, it will be changed based on parameters passed to the validator class, + /// or exposed directly by it. + /// + public IFormatter Formatter + { + get { return _formatter; } + set { _formatter = value; } + } + + StringBuilder _messages = new StringBuilder(); + + /// Gets or sets the messages generated by the validation process. + /// + /// Specific implementations of this class read/write this property + /// while they accumulate validation messages. + /// + public StringBuilder Messages + { + get { return _messages; } + set { _messages = value; } + } + + bool _haserrors = false; + + /// Indicates if errors were found during the current evaluation. + public bool HasErrors + { + get { return _haserrors; } + set { _haserrors = value; } + } + + string _phase = String.Empty; + + /// Gets or sets the specific validation phase to run. + /// + /// Schematron supports the concept of phases, where different sets of + /// patterns can be executed at different times. This phase is initialized + /// to , which will mean all patterns are run. + /// + public string Phase + { + get { return _phase; } + set { _phase = value; } + } + + Schema _schema = null!; + + /// Gets or sets the schema to use for the validation. + public Schema Schema + { + get { return _schema; } + set { _schema = value; } + } + + XPathNavigator _source = null!; + + /// Gets or sets the document to validate. + /// + /// When this property is set, the appropriate + /// strategy is picked, to perform optimum for various navigator implementations. + /// + public XPathNavigator Source + { + get { return _source; } + set + { + _source = value; + if (value is IHasXmlNode) + { + Matched = new DomMatchedNodes(); + } + else if (value is IXmlLineInfo) + { + Matched = new XPathMatchedNodes(); + } + else + { + Matched = new GenericMatchedNodes(); + } + } + } + #endregion + + /// + /// Starts the evaluation process. + /// + /// + /// When the process is finished, the results are placed + /// in the property. + /// + public abstract void Start(); + + /// + /// Resets the state of the current context. + /// + /// + /// By default, it clears the and sets to false. + /// + protected void Reset() + { + _messages = new StringBuilder(); + + } +} + diff --git a/src/Schematron/Formatters/BooleanFormatter.cs b/src/Schematron/Formatters/BooleanFormatter.cs index fbd4df6..909c6b6 100644 --- a/src/Schematron/Formatters/BooleanFormatter.cs +++ b/src/Schematron/Formatters/BooleanFormatter.cs @@ -1,28 +1,24 @@ -using System; using System.Text; -using System.Text.RegularExpressions; -using System.Xml; using System.Xml.XPath; -using System.Xml.Schema; -namespace Schematron.Formatters +namespace Schematron.Formatters; + +/// +/// Provides a simple failure message, without any details of specific validation errors. +/// +public class BooleanFormatter : FormatterBase { - /// - /// Provides a simple failure message, without any details of specific validation errors. - /// - public class BooleanFormatter : FormatterBase - { - /// - public BooleanFormatter() - { - } + /// + public BooleanFormatter() + { + } - /// - /// Look at documentation. - /// - public override void Format(Schema source, XPathNavigator context, StringBuilder output) - { - output.Append("Validation failed!"); - } - } + /// + /// Look at documentation. + /// + public override void Format(Schema source, XPathNavigator context, StringBuilder output) + { + output.Append("Validation failed!"); + } } + diff --git a/src/Schematron/Formatters/FormatterBase.cs b/src/Schematron/Formatters/FormatterBase.cs index b252d8e..8771853 100644 --- a/src/Schematron/Formatters/FormatterBase.cs +++ b/src/Schematron/Formatters/FormatterBase.cs @@ -1,159 +1,159 @@ -using System; +using System.Text; using System.Xml; -using System.Xml.XPath; using System.Xml.Schema; -using System.Text; +using System.Xml.XPath; + +namespace Schematron.Formatters; -namespace Schematron.Formatters +/// +/// Look at documentation. +/// +public abstract class FormatterBase : IFormatter { - /// - /// Look at documentation. - /// - public abstract class FormatterBase : IFormatter - { - /// - public FormatterBase() - { - } - - /// - /// Look at documentation. - /// - public virtual void Format(Test source, XPathNavigator context, StringBuilder output) - { - } + /// + public FormatterBase() + { + } - /// - /// Look at documentation. - /// - public virtual void Format(Rule source, XPathNavigator context, StringBuilder output) - { - } + /// + /// Look at documentation. + /// + public virtual void Format(Test source, XPathNavigator context, StringBuilder output) + { + } - /// - /// Look at documentation. - /// - public virtual void Format(Pattern source, XPathNavigator context, StringBuilder output) - { - } + /// + /// Look at documentation. + /// + public virtual void Format(Rule source, XPathNavigator context, StringBuilder output) + { + } - /// - /// Look at documentation. - /// - public virtual void Format(Phase source, XPathNavigator context, StringBuilder output) - { - } + /// + /// Look at documentation. + /// + public virtual void Format(Pattern source, XPathNavigator context, StringBuilder output) + { + } - /// - /// Look at documentation. - /// - public virtual void Format(Schema source, XPathNavigator context, StringBuilder output) - { - } - - /// - /// Look at documentation. - /// - public virtual void Format(XmlReader reader, StringBuilder output) - { - } - - /// - /// Look at documentation. - /// - public virtual void Format(ValidationEventArgs source, StringBuilder output) - { - } + /// + /// Look at documentation. + /// + public virtual void Format(Phase source, XPathNavigator context, StringBuilder output) + { + } - /// - /// Look at documentation. - /// - public virtual void Format(XmlSchemaSet schemas, StringBuilder output) - { - } - - /// - /// Look at documentation. - /// - public virtual void Format(SchemaCollection schemas, StringBuilder output) - { - } - - /// - /// Look at documentation. - /// - public virtual void Format(StringBuilder output) - { - } + /// + /// Look at documentation. + /// + public virtual void Format(Schema source, XPathNavigator context, StringBuilder output) + { + } + + /// + /// Look at documentation. + /// + public virtual void Format(XmlReader reader, StringBuilder output) + { + } + + /// + /// Look at documentation. + /// + public virtual void Format(ValidationEventArgs source, StringBuilder output) + { + } + + /// + /// Look at documentation. + /// + public virtual void Format(XmlSchemaSet schemas, StringBuilder output) + { + } - protected static StringBuilder FormatMessage(Test source, XPathNavigator context, string msg) + /// + /// Look at documentation. + /// + public virtual void Format(SchemaCollection schemas, StringBuilder output) + { + } + + /// + /// Look at documentation. + /// + public virtual void Format(StringBuilder output) + { + } + + /// Formats the message from a test, resolving <name> and <value-of> expressions against the given context. + protected static StringBuilder FormatMessage(Test source, XPathNavigator context, string msg) + { + var sb = new StringBuilder(); + XPathExpression? nameExpr; + XPathExpression? selectExpr; + + // As we move on, we have to append starting from the last point, + // skipping the and expressions: Substring(offset, name.Index - offset). + int offset = 0; + + for (int i = 0; i < source.NameValueOfExpressions.Count; i++) { - StringBuilder sb = new StringBuilder(); - XPathExpression nameExpr; - XPathExpression selectExpr; + System.Text.RegularExpressions.Match name = source.NameValueOfExpressions[i]; + nameExpr = source.NamePaths[i]; + selectExpr = source.ValueOfSelects[i]; - // As we move on, we have to append starting from the last point, - // skipping the and expressions: Substring(offset, name.Index - offset). - int offset = 0; + // Append the text without the expression. + sb.Append(msg.Substring(offset, name.Index - offset)); - for (int i = 0; i < source.NameValueOfExpressions.Count; i++) + // Does the name element have a path attribute? + if (nameExpr != null) { - System.Text.RegularExpressions.Match name = source.NameValueOfExpressions[i]; - nameExpr = source.NamePaths[i]; - selectExpr = source.ValueOfSelects[i]; + nameExpr.SetContext(source.GetContext()!); - // Append the text without the expression. - sb.Append(msg.Substring(offset, name.Index - offset)); - - // Does the name element have a path attribute? - if (nameExpr != null) + string? result = null; + if (nameExpr.ReturnType == XPathResultType.NodeSet) { - nameExpr.SetContext(source.GetContext()); - - string result = null; - if (nameExpr.ReturnType == XPathResultType.NodeSet) - { - // It the result of the expression is a nodeset, we only get the element - // name of the first node, which is compatible with XSLT implementation. - XPathNodeIterator nodes = (XPathNodeIterator)context.Evaluate(nameExpr); - if (nodes.MoveNext()) - result = nodes.Current.Name; - } - else - result = context.Evaluate(nameExpr) as string; - - if (result != null) - sb.Append(result); + // It the result of the expression is a nodeset, we only get the element + // name of the first node, which is compatible with XSLT implementation. + var nodes = (XPathNodeIterator)context.Evaluate(nameExpr); + if (nodes.MoveNext()) + result = nodes.Current.Name; } - // Does the value-of element have a select attribute? - else if (selectExpr != null) + else + result = context.Evaluate(nameExpr) as string; + + if (result != null) + sb.Append(result); + } + // Does the value-of element have a select attribute? + else if (selectExpr != null) + { + selectExpr.SetContext(source.GetContext()!); + + string? result = null; + if (selectExpr.ReturnType == XPathResultType.NodeSet) { - selectExpr.SetContext(source.GetContext()); - - string result = null; - if (selectExpr.ReturnType == XPathResultType.NodeSet) - { - XPathNodeIterator nodes = (XPathNodeIterator)context.Evaluate(selectExpr); - result = String.Empty; - while (nodes.MoveNext()) - result += nodes.Current.Value; - } - else - result = context.Evaluate(selectExpr) as string; - - if (result != null) - sb.Append(result); + var nodes = (XPathNodeIterator)context.Evaluate(selectExpr); + result = String.Empty; + while (nodes.MoveNext()) + result += nodes.Current.Value; } - // If there is no path or select expression, there is an empty element. else - sb.Append(context.Name); + result = context.Evaluate(selectExpr) as string; - offset = name.Index + name.Length; + if (result != null) + sb.Append(result); } + // If there is no path or select expression, there is an empty element. + else + sb.Append(context.Name); - sb.Append(msg.Substring(offset)); - return sb; + offset = name.Index + name.Length; } + sb.Append(msg.Substring(offset)); + return sb; } -} \ No newline at end of file + +} + diff --git a/src/Schematron/Formatters/FormattingUtils.cs b/src/Schematron/Formatters/FormattingUtils.cs index 4dfdd54..9927a42 100644 --- a/src/Schematron/Formatters/FormattingUtils.cs +++ b/src/Schematron/Formatters/FormattingUtils.cs @@ -1,210 +1,205 @@ -using System; using System.Collections; -using System.Resources; using System.Text; using System.Text.RegularExpressions; using System.Xml; using System.Xml.XPath; -namespace Schematron.Formatters +namespace Schematron.Formatters; + +/// +public class FormattingUtils { - /// - public class FormattingUtils - { - static Regex _normalize; - static Regex _removeprefix; - - static FormattingUtils() - { - _normalize = new Regex(@"\s+", RegexOptions.Compiled); - _removeprefix = new Regex(" .*", RegexOptions.Compiled); - - //Mangle message for xml validation errors to locate the position in the error message. - ResourceManager m = new ResourceManager("System.XML", typeof(System.Xml.Schema.XmlSchema).Assembly); - string msg = m.GetString("Sch_ErrorPosition"); - Regex rp = new Regex(@"{\d+}"); - msg = rp.Replace(msg, ".*"); - XmlErrorPosition = new Regex(msg, RegexOptions.Compiled); - } - - private FormattingUtils() - { - } - - private static XPathExpression precedingSiblingsExpr = XPathExpression.Compile("preceding-sibling::*"); - - /// - /// Returns the full path to the context node. Clone the navigator to avoid loosing positioning. - /// - public static string GetFullNodePosition(XPathNavigator context, string previous, Test source) - { - return GetFullNodePosition(context, previous, source, new Hashtable()); - } - - /// - /// Returns the full path to the context node. Clone the navigator to avoid loosing positioning. - /// - /// - /// Cloning is not performed inside this method because it is called recursively. - /// Keeping positioning is only relevant to the calling procedure, not subsequent - /// recursive calls. This way we avoid creating unnecessary objects. - /// - public static string GetFullNodePosition(XPathNavigator context, string previous, Test source, Hashtable namespaces) - { - string curr = context.Name; - string pref = String.Empty; - - if (context.NamespaceURI != String.Empty) - { - if (context.Prefix == String.Empty) - { - pref = source.GetContext().LookupPrefix(source.GetContext().NameTable.Get(context.NamespaceURI)); - } - else - { - pref = context.Prefix; - } - - if (!namespaces.ContainsKey(context.NamespaceURI)) - { - namespaces.Add(context.NamespaceURI, pref != null ? pref : ""); - } - else if (((String)namespaces[context.NamespaceURI]) != pref && - !namespaces.ContainsKey(context.NamespaceURI + ":" + pref)) - { - namespaces.Add(context.NamespaceURI + " " + pref, pref); - } - } - - int sibs = 1; - foreach (XPathNavigator prev in context.Select(precedingSiblingsExpr)) - if (prev.Name == curr) sibs++; - - if (context.MoveToParent()) - { - StringBuilder sb = new StringBuilder(); - sb.Append("/"); - if (pref != String.Empty) sb.Append(pref).Append(":"); - sb.Append(curr).Append("[").Append(sibs).Append("]").Append(previous); - return GetFullNodePosition(context, sb.ToString(), source, namespaces); - } - else - { - return previous; - } - } - - /// - /// Returns line positioning information if supported by the XPathNavigator implementation. - /// - public static string GetPositionInFile(XPathNavigator context, string spacing) - { - if (!(context is IXmlLineInfo)) - return String.Empty; - - StringBuilder sb = new StringBuilder(); - sb.Append(spacing); - - IXmlLineInfo info = (IXmlLineInfo) context; - - sb.Append("(Line: ").Append(info.LineNumber); - sb.Append(", Column: ").Append(info.LinePosition).Append(")"); - - return sb.ToString(); - } - - /// - /// Returns abreviated node information, including attribute values. - /// - public static string GetNodeSummary(XPathNavigator context, string spacing) - { - return GetNodeSummary(context, new Hashtable(), spacing); - } - - /// - /// Returns abreviated node information, including attribute values. - /// - /// - /// The namespaces param is optionally filled in . - /// - public static string GetNodeSummary(XPathNavigator context, Hashtable namespaces, string spacing) - { - XPathNavigator ctx = context.Clone(); - StringBuilder sb = new StringBuilder(); - - sb.Append(spacing).Append("<"); - - // Get the element name - XmlQualifiedName name; - if (ctx.NamespaceURI != String.Empty) - name = new XmlQualifiedName(ctx.LocalName, namespaces[ctx.NamespaceURI].ToString()); - else - name = new XmlQualifiedName(ctx.LocalName); - - sb.Append(name.ToString()); - if (ctx.MoveToFirstAttribute()) - { - do - { - sb.Append(" ").Append(ctx.LocalName); - sb.Append("=\"").Append(ctx.Value); - sb.Append("\""); - }while (ctx.MoveToNextAttribute()); - } - sb.Append(">..."); - return sb.ToString(); - } - - /// - /// Outputs the xmlns declaration for each namespace found in the parameter. - /// - public static string GetNamespaceSummary(XPathNavigator context, Hashtable namespaces, string spacing) - { - if (namespaces.Count == 0) return String.Empty; - - StringBuilder sb = new StringBuilder(); - ICollection keys = namespaces.Keys; - string pref = String.Empty; - - foreach (object key in keys) - { - sb.Append(spacing).Append("xmlns"); - pref = namespaces[key].ToString(); - - if (pref != String.Empty) - sb.Append(":").Append(namespaces[key]); - - sb.Append("=\""); - - if (pref != String.Empty) - sb.Append(_removeprefix.Replace(key.ToString(), String.Empty)); - else - sb.Append(key); - - sb.Append("\" "); - } - - return sb.ToString(); - } - - /// - /// Allows to match the string stating the node position from System.Xml error messages. - /// - /// - /// This regular expression is used to remove the node position from the validation error - /// message, to maintain consistency with schematron messages. - /// - public static Regex XmlErrorPosition; - - /// - /// Returns a decoded string, with spaces trimmed. - /// - public static string NormalizeString(string input) - { - // Account for encoded strings, such as < (<) and > (>). - return System.Web.HttpUtility.HtmlDecode( - _normalize.Replace(input, " ").Trim()); - } - } + static Regex _normalize; + static Regex _removeprefix; + + static FormattingUtils() + { + _normalize = new Regex(@"\s+", RegexOptions.Compiled); + _removeprefix = new Regex(" .*", RegexOptions.Compiled); + + // Match the position suffix appended by XmlSchemaException.Message: + // " An error occurred at {uri}({line}, {col})." + XmlErrorPosition = new Regex(@" An error occurred at [^(]*\(\d+,\s*\d+\)\.", RegexOptions.Compiled); + } + + FormattingUtils() + { + } + + static XPathExpression precedingSiblingsExpr = XPathExpression.Compile("preceding-sibling::*"); + + /// + /// Returns the full path to the context node. Clone the navigator to avoid loosing positioning. + /// + public static string GetFullNodePosition(XPathNavigator context, string previous, Test source) + { + return GetFullNodePosition(context, previous, source, new Hashtable()); + } + + /// + /// Returns the full path to the context node. Clone the navigator to avoid loosing positioning. + /// + /// + /// Cloning is not performed inside this method because it is called recursively. + /// Keeping positioning is only relevant to the calling procedure, not subsequent + /// recursive calls. This way we avoid creating unnecessary objects. + /// + public static string GetFullNodePosition(XPathNavigator context, string previous, Test source, Hashtable namespaces) + { + string curr = context.Name; + string pref = String.Empty; + + if (context.NamespaceURI != String.Empty) + { + if (context.Prefix == String.Empty) + { + pref = source.GetContext()!.LookupPrefix(source.GetContext()!.NameTable.Get(context.NamespaceURI)); + } + else + { + pref = context.Prefix; + } + + if (!namespaces.ContainsKey(context.NamespaceURI)) + { + namespaces.Add(context.NamespaceURI, pref != null ? pref : ""); + } + else if (((String)namespaces[context.NamespaceURI]) != pref && + !namespaces.ContainsKey(context.NamespaceURI + ":" + pref)) + { + namespaces.Add(context.NamespaceURI + " " + pref, pref); + } + } + + int sibs = 1; + foreach (XPathNavigator prev in context.Select(precedingSiblingsExpr)) + if (prev.Name == curr) sibs++; + + if (context.MoveToParent()) + { + var sb = new StringBuilder(); + sb.Append("/"); + if (pref != String.Empty) sb.Append(pref).Append(":"); + sb.Append(curr).Append("[").Append(sibs).Append("]").Append(previous); + return GetFullNodePosition(context, sb.ToString(), source, namespaces); + } + else + { + return previous; + } + } + + /// + /// Returns line positioning information if supported by the XPathNavigator implementation. + /// + public static string GetPositionInFile(XPathNavigator context, string spacing) + { + if (!(context is IXmlLineInfo)) + return String.Empty; + + var sb = new StringBuilder(); + sb.Append(spacing); + + var info = (IXmlLineInfo)context; + + sb.Append("(Line: ").Append(info.LineNumber); + sb.Append(", Column: ").Append(info.LinePosition).Append(")"); + + return sb.ToString(); + } + + /// + /// Returns abreviated node information, including attribute values. + /// + public static string GetNodeSummary(XPathNavigator context, string spacing) + { + return GetNodeSummary(context, new Hashtable(), spacing); + } + + /// + /// Returns abreviated node information, including attribute values. + /// + /// + /// The namespaces param is optionally filled in . + /// + public static string GetNodeSummary(XPathNavigator context, Hashtable namespaces, string spacing) + { + XPathNavigator ctx = context.Clone(); + var sb = new StringBuilder(); + + sb.Append(spacing).Append("<"); + + // Get the element name + XmlQualifiedName name; + if (ctx.NamespaceURI != String.Empty) + name = new XmlQualifiedName(ctx.LocalName, namespaces[ctx.NamespaceURI].ToString()); + else + name = new XmlQualifiedName(ctx.LocalName); + + sb.Append(name.ToString()); + if (ctx.MoveToFirstAttribute()) + { + do + { + sb.Append(" ").Append(ctx.LocalName); + sb.Append("=\"").Append(ctx.Value); + sb.Append("\""); + } while (ctx.MoveToNextAttribute()); + } + sb.Append(">..."); + return sb.ToString(); + } + + /// + /// Outputs the xmlns declaration for each namespace found in the parameter. + /// + public static string GetNamespaceSummary(XPathNavigator context, Hashtable namespaces, string spacing) + { + if (namespaces.Count == 0) return String.Empty; + + var sb = new StringBuilder(); + ICollection keys = namespaces.Keys; + string pref = String.Empty; + + foreach (object key in keys) + { + sb.Append(spacing).Append("xmlns"); + pref = namespaces[key].ToString(); + + if (pref != String.Empty) + sb.Append(":").Append(namespaces[key]); + + sb.Append("=\""); + + if (pref != String.Empty) + sb.Append(_removeprefix.Replace(key.ToString(), String.Empty)); + else + sb.Append(key); + + sb.Append("\" "); + } + + return sb.ToString(); + } + + /// + /// Allows to match the string stating the node position from System.Xml error messages. + /// + /// + /// This regular expression is used to remove the node position from the validation error + /// message, to maintain consistency with schematron messages. + /// + public static Regex XmlErrorPosition; + + /// + /// Returns a decoded string, with spaces trimmed. + /// + public static string NormalizeString(string input) + { + // Account for encoded strings, such as < (<) and > (>). + return System.Web.HttpUtility.HtmlDecode( + _normalize.Replace(input, " ").Trim()); + } } + diff --git a/src/Schematron/Formatters/IFormatter.cs b/src/Schematron/Formatters/IFormatter.cs index 87d62d2..8a1004f 100644 --- a/src/Schematron/Formatters/IFormatter.cs +++ b/src/Schematron/Formatters/IFormatter.cs @@ -1,145 +1,144 @@ -using System; +using System.Text; using System.Xml; -using System.Xml.XPath; using System.Xml.Schema; -using System.Text; +using System.Xml.XPath; + +namespace Schematron.Formatters; -namespace Schematron.Formatters +/// +/// Interface for custom formatters, which are used to generate +/// output from schema validation. +/// +/// +/// Provides formatting methods for Schematron specific elements and +/// for validation through the . +/// Custom formatters implement methods to provide message formatting. +///

+/// Am abstract base implementation is provided in to ease +/// the extending process. +///

+///
+public interface IFormatter { - /// - /// Interface for custom formatters, which are used to generate - /// output from schema validation. - /// - /// - /// Provides formatting methods for Schematron specific elements and - /// for validation through the . - /// Custom formatters implement methods to provide message formatting. - ///

- /// Am abstract base implementation is provided in to ease - /// the extending process. - ///

- ///
- public interface IFormatter - { - /// - /// Provides formatting of both and elements. - /// - /// - /// Implementations may use the received context to add details to the output message. - /// The contains messages accumulated so far in the validation process. - /// - /// The current navigator node where the test failed. - /// The message to output. - /// The or element - /// which failed. - void Format(Test source, XPathNavigator context, StringBuilder output); + /// + /// Provides formatting of both and elements. + /// + /// + /// Implementations may use the received context to add details to the output message. + /// The contains messages accumulated so far in the validation process. + /// + /// The current navigator node where the test failed. + /// The message to output. + /// The or element + /// which failed. + void Format(Test source, XPathNavigator context, StringBuilder output); + + /// + /// Provides formatting for a element. + /// + /// + /// Implementations may use the received context to add details to the output message. + /// The contains messages accumulated so far in the validation process. + /// + /// The navigator where the inner or + /// elements failed. + /// The message to output. + /// The element containing failed + /// or elements. + void Format(Rule source, XPathNavigator context, StringBuilder output); + + /// + /// Provides formatting for a element. + /// + /// + /// Implementations may use the received context to add details to the output message. + /// The contains messages accumulated so far in the validation process. + /// + /// The source document navigator where evaluation took place. + /// The message to output. + /// The element containing failed elements. + void Format(Pattern source, XPathNavigator context, StringBuilder output); + + /// + /// Provides formatting for a element. + /// + /// + /// Implementations may use the received context to add details to the output message. + /// The contains messages accumulated so far in the validation process. + /// + /// The source document navigator where evaluation took place. + /// The message to output. + /// The being evaluated. + void Format(Phase source, XPathNavigator context, StringBuilder output); + + /// + /// Provides formatting for a element. + /// + /// + /// Implementations may use the received context to add details to the output message. + /// The contains messages accumulated so far in the validation process. + /// + /// The source document navigator where evaluation took place. + /// The message to output. + /// The being evaluated. + void Format(Schema source, XPathNavigator context, StringBuilder output); - /// - /// Provides formatting for a element. - /// - /// - /// Implementations may use the received context to add details to the output message. - /// The contains messages accumulated so far in the validation process. - /// - /// The navigator where the inner or - /// elements failed. - /// The message to output. - /// The element containing failed - /// or elements. - void Format(Rule source, XPathNavigator context, StringBuilder output); + /// + /// Provides formatting for a element being validated + /// through a . + /// + /// + /// Usually will output schema-level formatting for XmlSchema validation. Recall that + /// multiple schemas may have been configured with the reader and validated simultaneously. + /// The contains messages accumulated so far in the validation process. + /// + /// The message to output. + /// The reader in use to validate the schema. + void Format(XmlReader reader, StringBuilder output); - /// - /// Provides formatting for a element. - /// - /// - /// Implementations may use the received context to add details to the output message. - /// The contains messages accumulated so far in the validation process. - /// - /// The source document navigator where evaluation took place. - /// The message to output. - /// The element containing failed elements. - void Format(Pattern source, XPathNavigator context, StringBuilder output); + /// + /// Formats the output of XmlSchema validation. + /// + /// + /// The contains messages accumulated so far in the validation process. + /// + /// The message to output. + /// The argument received by the handler + /// during XmlSchema validation. + void Format(ValidationEventArgs source, StringBuilder output); - /// - /// Provides formatting for a element. - /// - /// - /// Implementations may use the received context to add details to the output message. - /// The contains messages accumulated so far in the validation process. - /// - /// The source document navigator where evaluation took place. - /// The message to output. - /// The being evaluated. - void Format(Phase source, XPathNavigator context, StringBuilder output); + /// + /// Enclosing message for all schemas being validated. + /// + /// + /// Usually will add any enclosing message to the results of the global Xml validation. + /// The contains messages accumulated so far in the validation process. + /// + /// The message to output. + /// The collection of schemas in use for validation. + void Format(XmlSchemaSet schemas, StringBuilder output); - /// - /// Provides formatting for a element. - /// - /// - /// Implementations may use the received context to add details to the output message. - /// The contains messages accumulated so far in the validation process. - /// - /// The source document navigator where evaluation took place. - /// The message to output. - /// The being evaluated. - void Format(Schema source, XPathNavigator context, StringBuilder output); - - /// - /// Provides formatting for a element being validated - /// through a . - /// - /// - /// Usually will output schema-level formatting for XmlSchema validation. Recall that - /// multiple schemas may have been configured with the reader and validated simultaneously. - /// The contains messages accumulated so far in the validation process. - /// - /// The message to output. - /// The reader in use to validate the schema. - void Format(XmlReader reader, StringBuilder output); - - /// - /// Formats the output of XmlSchema validation. - /// - /// - /// The contains messages accumulated so far in the validation process. - /// - /// The message to output. - /// The argument received by the handler - /// during XmlSchema validation. - void Format(ValidationEventArgs source, StringBuilder output); + /// + /// Enclosing message for all schemas being validated. + /// + /// + /// Usually will add any enclosing message to the results of the global Schematron validation. + /// The contains messages accumulated so far in the validation process. + /// + /// The message to output. + /// The collection of schemas in use for validation. + void Format(SchemaCollection schemas, StringBuilder output); - /// - /// Enclosing message for all schemas being validated. - /// - /// - /// Usually will add any enclosing message to the results of the global Xml validation. - /// The contains messages accumulated so far in the validation process. - /// - /// The message to output. - /// The collection of schemas in use for validation. - void Format(XmlSchemaSet schemas, StringBuilder output); + /// + /// Formats the whole message built so far. + /// + /// + /// Usually will perform any last-minute formatting of the whole message before + /// being returned by the calling application. For example, the + /// uses this method to enclose the whole message in an <output> element. + /// The contains messages accumulated so far in the validation process. + /// + /// The message to output. + void Format(StringBuilder output); +} - /// - /// Enclosing message for all schemas being validated. - /// - /// - /// Usually will add any enclosing message to the results of the global Schematron validation. - /// The contains messages accumulated so far in the validation process. - /// - /// The message to output. - /// The collection of schemas in use for validation. - void Format(SchemaCollection schemas, StringBuilder output); - - /// - /// Formats the whole message built so far. - /// - /// - /// Usually will perform any last-minute formatting of the whole message before - /// being returned by the calling application. For example, the - /// uses this method to enclose the whole message in an <output> element. - /// The contains messages accumulated so far in the validation process. - /// - /// The message to output. - void Format(StringBuilder output); - } -} \ No newline at end of file diff --git a/src/Schematron/Formatters/LogFormatter.cs b/src/Schematron/Formatters/LogFormatter.cs index e442bbc..28626e2 100644 --- a/src/Schematron/Formatters/LogFormatter.cs +++ b/src/Schematron/Formatters/LogFormatter.cs @@ -1,106 +1,103 @@ -using System; using System.Collections; using System.Text; -using System.Text.RegularExpressions; -using System.Xml; -using System.Xml.XPath; using System.Xml.Schema; +using System.Xml.XPath; -namespace Schematron.Formatters +namespace Schematron.Formatters; + +/// +/// Provides a complete log of validation errors in text format. +/// +public class LogFormatter : FormatterBase { - /// - /// Provides a complete log of validation errors in text format. - /// - public class LogFormatter : FormatterBase - { - /// - public LogFormatter() - { - } + /// + public LogFormatter() + { + } - /// - /// Look at documentation. - /// - public override void Format(Test source, XPathNavigator context, StringBuilder output) - { - StringBuilder sb = FormatMessage(source, context, source.Message); - // Finally remove any non-name schematron tag in the message. - string res = TagExpressions.AllSchematron.Replace(sb.ToString(), String.Empty); - sb = new StringBuilder(); - if (source is Assert) - { - sb.Append("\tAssert fails: "); - } - else - { - sb.Append("\tReport: "); - } - sb.Append(res); + /// + /// Look at documentation. + /// + public override void Format(Test source, XPathNavigator context, StringBuilder output) + { + StringBuilder sb = FormatMessage(source, context, source.Message); + // Finally remove any non-name schematron tag in the message. + string res = TagExpressions.AllSchematron.Replace(sb.ToString(), String.Empty); + sb = new StringBuilder(); + if (source is Assert) + { + sb.Append("\tAssert fails: "); + } + else + { + sb.Append("\tReport: "); + } + sb.Append(res); - //Accumulate namespaces found during traversal of node for its position. - Hashtable ns = new Hashtable(); + //Accumulate namespaces found during traversal of node for its position. + var ns = new Hashtable(); - sb.Append("\r\n\tAt: ").Append(FormattingUtils.GetFullNodePosition(context.Clone(), String.Empty, source, ns)); - sb.Append(FormattingUtils.GetNodeSummary(context, ns, "\r\n\t ")); + sb.Append("\r\n\tAt: ").Append(FormattingUtils.GetFullNodePosition(context.Clone(), String.Empty, source, ns)); + sb.Append(FormattingUtils.GetNodeSummary(context, ns, "\r\n\t ")); - res = FormattingUtils.GetPositionInFile(context, "\r\n\t "); - if (res != String.Empty) sb.Append(res); + res = FormattingUtils.GetPositionInFile(context, "\r\n\t "); + if (res != String.Empty) sb.Append(res); - res = FormattingUtils.GetNamespaceSummary(context, ns, "\r\n\t "); - if (res != string.Empty) sb.Append(res); - sb.Append("\r\n"); - output.Append(sb.ToString()); - } + res = FormattingUtils.GetNamespaceSummary(context, ns, "\r\n\t "); + if (res != string.Empty) sb.Append(res); + sb.Append("\r\n"); + output.Append(sb.ToString()); + } - /// - /// Look at documentation. - /// - public override void Format(Pattern source, XPathNavigator context, StringBuilder output) - { - output.Insert(0, " From pattern \"" + source.Name + "\"\r\n"); - output.Append("\r\n"); - } + /// + /// Look at documentation. + /// + public override void Format(Pattern source, XPathNavigator context, StringBuilder output) + { + output.Insert(0, " From pattern \"" + source.Name + "\"\r\n"); + output.Append("\r\n"); + } - /// - /// Look at documentation. - /// - public override void Format(Schema source, XPathNavigator context, StringBuilder output) - { - if (source.Title != String.Empty) - output.Insert(0, source.Title + "\r\n"); - else - output.Insert(0, "Results from Schematron validation\r\n"); + /// + /// Look at documentation. + /// + public override void Format(Schema source, XPathNavigator context, StringBuilder output) + { + if (source.Title != String.Empty) + output.Insert(0, source.Title + "\r\n"); + else + output.Insert(0, "Results from Schematron validation\r\n"); - output.Append("\r\n"); - } + output.Append("\r\n"); + } - /// - /// Look at documentation. - /// - public override void Format(ValidationEventArgs source, StringBuilder output) - { - output.Append(" Error: "); - output.Append(FormattingUtils.XmlErrorPosition.Replace(source.Message, String.Empty)); - output.Append("\r\n At: (Line: ").Append(source.Exception.LineNumber); - output.Append(", Column: ").Append(source.Exception.LinePosition).Append(")\r\n"); - } + /// + /// Look at documentation. + /// + public override void Format(ValidationEventArgs source, StringBuilder output) + { + output.Append(" Error: "); + output.Append(FormattingUtils.XmlErrorPosition.Replace(source.Message, String.Empty)); + output.Append("\r\n At: (Line: ").Append(source.Exception.LineNumber); + output.Append(", Column: ").Append(source.Exception.LinePosition).Append(")\r\n"); + } - /// - /// Look at documentation. - /// - public override void Format(XmlSchemaSet schemas, StringBuilder output) - { - output.Insert(0, "Results from XML Schema validation:\r\n"); - output.Append("\r\n"); - } + /// + /// Look at documentation. + /// + public override void Format(XmlSchemaSet schemas, StringBuilder output) + { + output.Insert(0, "Results from XML Schema validation:\r\n"); + output.Append("\r\n"); + } - /// - /// Look at documentation. - /// - public override void Format(SchemaCollection schemas, StringBuilder output) - { - output.Insert(0, "Results from Schematron validation:\r\n"); - output.Append("\r\n"); - } - } + /// + /// Look at documentation. + /// + public override void Format(SchemaCollection schemas, StringBuilder output) + { + output.Insert(0, "Results from Schematron validation:\r\n"); + output.Append("\r\n"); + } } + diff --git a/src/Schematron/Formatters/SimpleFormatter.cs b/src/Schematron/Formatters/SimpleFormatter.cs index 9cd63c4..e860342 100644 --- a/src/Schematron/Formatters/SimpleFormatter.cs +++ b/src/Schematron/Formatters/SimpleFormatter.cs @@ -1,44 +1,40 @@ -using System; using System.Collections; using System.Text; -using System.Text.RegularExpressions; -using System.Xml; using System.Xml.XPath; -using System.Xml.Schema; -namespace Schematron.Formatters +namespace Schematron.Formatters; + +/// +/// Provides a simplified log of errors. +/// +/// +/// Similar output as , but doesn't provide +/// node position in file and namespace summary text. +/// +public class SimpleFormatter : LogFormatter { - /// - /// Provides a simplified log of errors. - /// - /// - /// Similar output as , but doesn't provide - /// node position in file and namespace summary text. - /// - public class SimpleFormatter : LogFormatter - { - /// - public SimpleFormatter() - { - } + /// + public SimpleFormatter() + { + } - /// - /// Look at documentation. - /// - public override void Format(Test source, XPathNavigator context, StringBuilder output) - { - StringBuilder sb = FormatMessage(source, context, source.Message); + /// + /// Look at documentation. + /// + public override void Format(Test source, XPathNavigator context, StringBuilder output) + { + StringBuilder sb = FormatMessage(source, context, source.Message); - if (source is Assert) - sb.Insert(0, "\tAssert fails: "); - else - sb.Insert(0, "\tReport: "); + if (source is Assert) + sb.Insert(0, "\tAssert fails: "); + else + sb.Insert(0, "\tReport: "); - Hashtable ns = new Hashtable(); - sb.Append("\r\n\tAt: " + FormattingUtils.GetFullNodePosition(context.Clone(), String.Empty, source, ns)); - sb.Append("\r\n"); + var ns = new Hashtable(); + sb.Append("\r\n\tAt: " + FormattingUtils.GetFullNodePosition(context.Clone(), String.Empty, source, ns)); + sb.Append("\r\n"); - output.Append(sb.ToString()); - } - } + output.Append(sb.ToString()); + } } + diff --git a/src/Schematron/Formatters/XmlFormatter.cs b/src/Schematron/Formatters/XmlFormatter.cs index 577e806..86b29dd 100644 --- a/src/Schematron/Formatters/XmlFormatter.cs +++ b/src/Schematron/Formatters/XmlFormatter.cs @@ -1,221 +1,217 @@ -using System; using System.Collections; -using System.IO; using System.Text; -using System.Text.RegularExpressions; using System.Xml; -using System.Xml.XPath; using System.Xml.Schema; +using System.Xml.XPath; + +namespace Schematron.Formatters; -namespace Schematron.Formatters +/// +/// Provides an Xml output from validation. +/// +public class XmlFormatter : FormatterBase { - /// - /// Provides an Xml output from validation. - /// - public class XmlFormatter : FormatterBase - { - /// - public XmlFormatter() - { - } - - /// - /// Namespace of generated output. - /// - public const string OutputNamespace = "http://sourceforge.net/projects/dotnetopensrc/schematron"; - - /// - /// Look at documentation. - /// - public override void Format(Test source, XPathNavigator context, StringBuilder output) + /// + public XmlFormatter() + { + } + + /// + /// Namespace of generated output. + /// + public const string OutputNamespace = "http://sourceforge.net/projects/dotnetopensrc/schematron"; + + /// + /// Look at documentation. + /// + public override void Format(Test source, XPathNavigator context, StringBuilder output) + { + string msg = source.Message; + var writer = new XmlTextWriter(new StringWriter(output)); + //Temporary disable namespace support. + writer.Namespaces = false; + + // Start element declaration. + writer.WriteStartElement("message"); + + msg = FormatMessage(source, context, msg).ToString(); + + // Finally remove any non-name schematron tag in the message. + string res = TagExpressions.AllSchematron.Replace(msg, String.Empty); + + //Accumulate namespaces found during traversal of node for its position. + var ns = new Hashtable(); + + // Write element. + writer.WriteElementString("text", res); + // Write element. + writer.WriteElementString("path", FormattingUtils.GetFullNodePosition(context.Clone(), String.Empty, source, ns)); + // Write element. + //writer.WriteElementString("summary", FormattingUtils.GetNodeSummary(context, ns, String.Empty)); + writer.WriteStartElement("summary"); + writer.WriteRaw(FormattingUtils.GetNodeSummary(context, ns, String.Empty)); + writer.WriteEndElement(); + + // Write element. + if (context is IXmlLineInfo info) { - string msg = source.Message; - XmlTextWriter writer = new XmlTextWriter(new StringWriter(output)); - //Temporary disable namespace support. - writer.Namespaces = false; - - // Start element declaration. - writer.WriteStartElement("message"); - - msg = FormatMessage(source, context, msg).ToString(); - - // Finally remove any non-name schematron tag in the message. - string res = TagExpressions.AllSchematron.Replace(msg, String.Empty); - - //Accumulate namespaces found during traversal of node for its position. - Hashtable ns = new Hashtable(); - - // Write element. - writer.WriteElementString("text", res); - // Write element. - writer.WriteElementString("path", FormattingUtils.GetFullNodePosition(context.Clone(), String.Empty, source, ns)); - // Write element. - //writer.WriteElementString("summary", FormattingUtils.GetNodeSummary(context, ns, String.Empty)); - writer.WriteStartElement("summary"); - writer.WriteRaw(FormattingUtils.GetNodeSummary(context, ns, String.Empty)); + writer.WriteStartElement("position"); + writer.WriteAttributeString("line", info.LineNumber.ToString()); + writer.WriteAttributeString("column", info.LinePosition.ToString()); writer.WriteEndElement(); + } - // Write element. - if (context is IXmlLineInfo) - { - writer.WriteStartElement("position"); - IXmlLineInfo info = (IXmlLineInfo)context; - writer.WriteAttributeString("line", info.LineNumber.ToString()); - writer.WriteAttributeString("column", info.LinePosition.ToString()); - writer.WriteEndElement(); - } - - // Close element. - writer.WriteEndElement(); - writer.Flush(); + // Close element. + writer.WriteEndElement(); + writer.Flush(); + } + + /// + /// Look at documentation. + /// + public override void Format(Rule source, XPathNavigator context, StringBuilder output) + { + string res = ""); + } + + /// + /// Look at documentation. + /// + public override void Format(Pattern source, XPathNavigator context, StringBuilder output) + { + string res = ""); + } + + /// + /// Look at documentation. + /// + public override void Format(Phase source, XPathNavigator context, StringBuilder output) + { + output.Insert(0, ""); + output.Append(""); + } + + /// + /// Look at documentation. + /// + public override void Format(Schema source, XPathNavigator context, StringBuilder output) + { + var sb = new StringBuilder(); + var writer = new XmlTextWriter(new StringWriter(sb)); + writer.WriteStartElement("schema "); + + foreach (string prefix in source.NsManager) + { + if (!prefix.StartsWith("xml")) + writer.WriteAttributeString("xmlns", prefix, null, + source.NsManager.LookupNamespace(source.NsManager.NameTable.Get(prefix))); } - /// - /// Look at documentation. - /// - public override void Format(Rule source, XPathNavigator context, StringBuilder output) - { - string res = ""); - } - - /// - /// Look at documentation. - /// - public override void Format(Pattern source, XPathNavigator context, StringBuilder output) - { - string res = ""); - } - - /// - /// Look at documentation. - /// - public override void Format(Phase source, XPathNavigator context, StringBuilder output) - { - output.Insert(0, ""); - output.Append(""); - } - - /// - /// Look at documentation. - /// - public override void Format(Schema source, XPathNavigator context, StringBuilder output) - { - StringBuilder sb = new StringBuilder(); - XmlTextWriter writer = new XmlTextWriter(new StringWriter(sb)); - writer.WriteStartElement("schema "); - - foreach (string prefix in source.NsManager) - { - if (!prefix.StartsWith("xml")) - writer.WriteAttributeString("xmlns", prefix, null, - source.NsManager.LookupNamespace(source.NsManager.NameTable.Get(prefix))); - } - - if (source.Title != String.Empty) writer.WriteAttributeString("title", source.Title); - - writer.WriteRaw(output.ToString()); - writer.WriteEndElement(); - writer.Flush(); - output.Remove(0, output.Length); - output.Append(sb.ToString()); - } - - /// - /// Look at documentation. - /// - public override void Format(ValidationEventArgs source, StringBuilder output) - { - XmlTextWriter writer = new XmlTextWriter(new StringWriter(output)); - // Start element declaration. - writer.WriteStartElement("message"); - - // Write element. - writer.WriteElementString("text", FormattingUtils.XmlErrorPosition.Replace(source.Message, String.Empty)); - - // Write element. - writer.WriteStartElement("position"); - writer.WriteAttributeString("line", source.Exception.LineNumber.ToString()); - writer.WriteAttributeString("column", source.Exception.LinePosition.ToString()); - writer.WriteEndElement(); - - // Close element. - writer.WriteEndElement(); - writer.Flush(); - } - - /// - /// Look at documentation. - /// - public override void Format(XmlSchemaSet schemas, StringBuilder output) - { - StringBuilder sb = new StringBuilder(); - XmlTextWriter writer = new XmlTextWriter(new StringWriter(sb)); - - foreach (XmlSchema sch in schemas.Schemas()) - { - writer.WriteStartElement("xmlSchema"); - writer.WriteAttributeString("id", sch.Id); - writer.WriteAttributeString("version", sch.Version); - writer.WriteAttributeString("targetNamespace", sch.TargetNamespace); - writer.WriteEndElement(); - } - writer.Flush(); - output.Insert(0, sb.ToString()); - } - - /// - /// Look at documentation. - /// - public override void Format(SchemaCollection schemas, StringBuilder output) - { - // Enclose putput in an element. - output.Insert(0, ""); - output.Append(""); - } - - /// - /// Look at documentation. - /// - public override void Format(XmlReader reader, StringBuilder output) - { - // Enclose messages in an element. - output.Insert(0, ""); - output.Append(""); - } - - /// - /// Look at documentation. - /// - public override void Format(StringBuilder output) - { - StringBuilder sb = new StringBuilder(); - XmlTextWriter writer = new XmlTextWriter(new StringWriter(sb)); - - writer.WriteStartElement("output", OutputNamespace); - writer.WriteRaw(output.ToString()); - writer.WriteEndElement(); - writer.Flush(); - - // Clean output. - output.Remove(0, output.Length); - - // Create indented output. - writer = new XmlTextWriter(new StringWriter(output)); - writer.Formatting = Formatting.Indented; - writer.WriteStartDocument(); - writer.WriteNode(new XmlTextReader(new StringReader(sb.ToString())), false); - writer.WriteEndDocument(); - writer.Flush(); - } - } + if (source.Title != String.Empty) writer.WriteAttributeString("title", source.Title); + + writer.WriteRaw(output.ToString()); + writer.WriteEndElement(); + writer.Flush(); + output.Remove(0, output.Length); + output.Append(sb.ToString()); + } + + /// + /// Look at documentation. + /// + public override void Format(ValidationEventArgs source, StringBuilder output) + { + var writer = new XmlTextWriter(new StringWriter(output)); + // Start element declaration. + writer.WriteStartElement("message"); + + // Write element. + writer.WriteElementString("text", FormattingUtils.XmlErrorPosition.Replace(source.Message, String.Empty)); + + // Write element. + writer.WriteStartElement("position"); + writer.WriteAttributeString("line", source.Exception.LineNumber.ToString()); + writer.WriteAttributeString("column", source.Exception.LinePosition.ToString()); + writer.WriteEndElement(); + + // Close element. + writer.WriteEndElement(); + writer.Flush(); + } + + /// + /// Look at documentation. + /// + public override void Format(XmlSchemaSet schemas, StringBuilder output) + { + var sb = new StringBuilder(); + var writer = new XmlTextWriter(new StringWriter(sb)); + + foreach (XmlSchema sch in schemas.Schemas()) + { + writer.WriteStartElement("xmlSchema"); + writer.WriteAttributeString("id", sch.Id); + writer.WriteAttributeString("version", sch.Version); + writer.WriteAttributeString("targetNamespace", sch.TargetNamespace); + writer.WriteEndElement(); + } + writer.Flush(); + output.Insert(0, sb.ToString()); + } + + /// + /// Look at documentation. + /// + public override void Format(SchemaCollection schemas, StringBuilder output) + { + // Enclose putput in an element. + output.Insert(0, ""); + output.Append(""); + } + + /// + /// Look at documentation. + /// + public override void Format(XmlReader reader, StringBuilder output) + { + // Enclose messages in an element. + output.Insert(0, ""); + output.Append(""); + } + + /// + /// Look at documentation. + /// + public override void Format(StringBuilder output) + { + var sb = new StringBuilder(); + var writer = new XmlTextWriter(new StringWriter(sb)); + + writer.WriteStartElement("output", OutputNamespace); + writer.WriteRaw(output.ToString()); + writer.WriteEndElement(); + writer.Flush(); + + // Clean output. + output.Remove(0, output.Length); + + // Create indented output. + writer = new XmlTextWriter(new StringWriter(output)); + writer.Formatting = Formatting.Indented; + writer.WriteStartDocument(); + writer.WriteNode(new XmlTextReader(new StringReader(sb.ToString())), false); + writer.WriteEndDocument(); + writer.Flush(); + } } + diff --git a/src/Schematron/GenericMatchedNodes.cs b/src/Schematron/GenericMatchedNodes.cs index c474aa9..57e1738 100644 --- a/src/Schematron/GenericMatchedNodes.cs +++ b/src/Schematron/GenericMatchedNodes.cs @@ -1,55 +1,53 @@ -using System; -using System.Xml; -using System.Xml.XPath; using System.Collections; +using System.Xml.XPath; + +namespace Schematron; -namespace Schematron +/// +/// Strategy class for matching and keeping references to nodes in +/// an unknown implementation of . +/// +/// +/// This implementation uses the standard +/// to know if a navigator has already been matched. This is not optimum because +/// a complete traversal of nodes matched so far has to be performed, but it will +/// work with all implementations of . +/// +/// +/// +class GenericMatchedNodes : IMatchedNodes { - /// - /// Strategy class for matching and keeping references to nodes in - /// an unknown implementation of . - /// - /// - /// This implementation uses the standard - /// to know if a navigator has already been matched. This is not optimum because - /// a complete traversal of nodes matched so far has to be performed, but it will - /// work with all implementations of . - /// - /// - /// - class GenericMatchedNodes : IMatchedNodes - { - /// - /// Uses a simple arraylist to keep the navigators. - /// - ArrayList _matched = new ArrayList(); - - /// Initializes an instance of the class. - public GenericMatchedNodes() - { - } - - /// See . - public bool IsMatched(System.Xml.XPath.XPathNavigator node) - { - foreach (XPathNavigator nav in _matched) - { - if (node.IsSamePosition(nav)) return true; - } - - return false; - } - - /// See . - public void AddMatched(System.Xml.XPath.XPathNavigator node) - { - _matched.Add(node.Clone()); - } - - /// See . - public void Clear() - { - _matched.Clear(); - } - } + /// + /// Uses a simple arraylist to keep the navigators. + /// + ArrayList _matched = new ArrayList(); + + /// Initializes an instance of the class. + public GenericMatchedNodes() + { + } + + /// See . + public bool IsMatched(XPathNavigator node) + { + foreach (XPathNavigator nav in _matched) + { + if (node.IsSamePosition(nav)) return true; + } + + return false; + } + + /// See . + public void AddMatched(XPathNavigator node) + { + _matched.Add(node.Clone()); + } + + /// See . + public void Clear() + { + _matched.Clear(); + } } + diff --git a/src/Schematron/IMatchedNodes.cs b/src/Schematron/IMatchedNodes.cs index ecc6ba2..08e4a93 100644 --- a/src/Schematron/IMatchedNodes.cs +++ b/src/Schematron/IMatchedNodes.cs @@ -1,38 +1,37 @@ -using System; using System.Xml; using System.Xml.XPath; -namespace Schematron +namespace Schematron; + +/// +/// Defines the common interface used by the different node-matching strategies. +/// +/// +/// As different can exist, and even be developed +/// in the future, we have to take into account that the data store can change. +/// So in order to be efficient at keeping nodes matched so far, to satisfy the +/// , we provide a common interface and an +/// implementation optimized for specific stores. +/// +/// Each navigator implementation typically provides an interface to let +/// applications get access to the underlying store, such as the +/// or interfaces, implemented in navigators create by +/// or classes. +/// +/// +/// +/// +public interface IMatchedNodes { - /// - /// Defines the common interface used by the different node-matching strategies. - /// - /// - /// As different can exist, and even be developed - /// in the future, we have to take into account that the data store can change. - /// So in order to be efficient at keeping nodes matched so far, to satisfy the - /// , we provide a common interface and an - /// implementation optimized for specific stores. - /// - /// Each navigator implementation typically provides an interface to let - /// applications get access to the underlying store, such as the - /// or interfaces, implemented in navigators create by - /// or classes. - /// - /// - /// - /// - public interface IMatchedNodes - { - /// Checks if an specific node has already been matched. - /// The node to check. - bool IsMatched(System.Xml.XPath.XPathNavigator node); + /// Checks if an specific node has already been matched. + /// The node to check. + bool IsMatched(XPathNavigator node); - /// Adds a node to the list of nodes matched so far. - /// The node to add. - void AddMatched(System.Xml.XPath.XPathNavigator node); + /// Adds a node to the list of nodes matched so far. + /// The node to add. + void AddMatched(XPathNavigator node); - /// Clears the list of matched nodes. - void Clear(); - } + /// Clears the list of matched nodes. + void Clear(); } + diff --git a/src/Schematron/Int32Collection.cs b/src/Schematron/Int32Collection.cs deleted file mode 100644 index f338e6a..0000000 --- a/src/Schematron/Int32Collection.cs +++ /dev/null @@ -1,452 +0,0 @@ -/* This file has been automatically generated by TextBox -- DO NOT EDIT! */ - - -/* -Int32Collection -Int32Collection.Enumerator - -These C# classes implement a strongly-typed collection of -Int32 objects. - -The internal representation is an array of Int32, so the performance -characteristics will be more like a vector than a list, to use STL terminology. - -The implementation is optimized for value-types, as it goes to great length to -avoid the overhead of boxing and unboxing. But it should also work well for -reference types. - -Mad props to Nick Wienholt for assisting me in -this research, and the testing, the benchmarking, and of course, the -implementation! - -Last but not least, a quick shout out to Kit George, for his generous -contribution to the dotnet mailing list -- a code-generator for -CollectionBase-derived classes: - http://discuss.develop.com/archives/wa.exe?A2=ind0107C&L=DOTNET&P=R35911 -This was the original inspiration for the fine code you are now enjoying. - -- Shawn Van Ness - -Other folks who've contributed: - Ethan Smith (minor perf. improvements) - Joel Mueller (major perf. improvements) - Chris Sells (generative programming guru) - Patrice Lafond (a bug fix -- yikes!) -*/ - - -using System; -using System.Collections; - -using T = System.Int32; - -namespace Schematron -{ - /// - /// An optimized collection for holding values. - /// - /// - /// This class was generated automatically by TextBox. - /// Original author is Shawn Van Ness. The code contains comments by - /// him, together with a summary of features and acknowledgments :). - /// - /// - [Serializable] - public class Int32Collection : ICollection, IList, IEnumerable - { - private const int DefaultMinimumCapacity = 16; - - private T[] m_array = new T[DefaultMinimumCapacity]; - private int m_count = 0; - private int m_version = 0; - - // Construction - - /// - public Int32Collection() - { } - - /// - public Int32Collection(Int32Collection collection) - { AddRange(collection); } - - /// - public Int32Collection(T[] array) - { AddRange(array); } - - // Operations (type-safe ICollection) - - /// - public int Count - { - get - { return m_count; } - } - - /// - public void CopyTo(T[] array) - { - this.CopyTo(array, 0); - } - - /// - public void CopyTo(T[] array, int start) - { - if (m_count > array.GetUpperBound(0)+1-start) - throw new System.ArgumentException("Destination array was not long enough."); - - // for (int i=0; i < m_count; ++i) array[start+i] = m_array[i]; - Array.Copy(m_array, 0, array, start, m_count); - } - - // Operations (type-safe IList) - - /// - public T this[int index] - { - get - { - ValidateIndex(index); // throws - return m_array[index]; - } - set - { - ValidateIndex(index); // throws - - ++m_version; - m_array[index] = value; - } - } - - /// - public int Add(T item) - { - if (NeedsGrowth()) - Grow(); - - ++m_version; - m_array[m_count] = item; - - return m_count++; - } - - /// - public void Clear() - { - ++m_version; - m_array = new T[DefaultMinimumCapacity]; - m_count = 0; - } - - /// - public bool Contains(T item) - { - return ((IndexOf(item) == -1)?false:true); - } - - /// - public int IndexOf(T item) - { - for (int i=0; i < m_count; ++i) - if (m_array[i].Equals(item)) - return i; - return -1; - } - - /// - public void Insert(int position, T item) - { - ValidateIndex(position,true); // throws - - if (NeedsGrowth()) - Grow(); - - ++m_version; - // for (int i=m_count; i > position; --i) m_array[i] = m_array[i-1]; - Array.Copy(m_array, position, m_array, position+1, m_count-position); - - m_array[position] = item; - m_count++; - } - - /// - public void Remove(T item) - { - int index = IndexOf(item); - if (index < 0) - throw new System.ArgumentException("Cannot remove the specified item because it was not found in the specified Collection."); - - RemoveAt(index); - } - - /// - public void RemoveAt(int index) - { - ValidateIndex(index); // throws - - ++m_version; - m_count--; - // for (int i=index; i < m_count; ++i) m_array[i] = m_array[i+1]; - Array.Copy(m_array, index+1, m_array, index, m_count-index); - - if (NeedsTrimming()) - Trim(); - } - - // Operations (type-safe IEnumerable) - - /// - public Enumerator GetEnumerator() - { - return new Enumerator(this); - } - - // Public helpers (just to mimic some nice features of ArrayList) - - /// - public int Capacity - { - get - { return m_array.Length; } - set - { - if (value < m_count) value = m_count; - if (value < DefaultMinimumCapacity) value = DefaultMinimumCapacity; - - if (m_array.Length == value) return; - - ++m_version; - - T[] temp = new T[value]; - // for (int i=0; i < m_count; ++i) temp[i] = m_array[i]; - Array.Copy(m_array, 0, temp, 0, m_count); - m_array = temp; - } - } - - /// - public void AddRange(Int32Collection collection) - { - // for (int i=0; i < collection.Count; ++i) Add(collection[i]); - - ++m_version; - - Capacity += collection.Count; - Array.Copy(collection.m_array, 0, this.m_array, m_count, collection.m_count); - m_count += collection.Count; - } - - /// - public void AddRange(T[] array) - { - // for (int i=0; i < array.Length; ++i) Add(array[i]); - - ++m_version; - - Capacity += array.Length; - Array.Copy(array, 0, this.m_array, m_count, array.Length); - m_count += array.Length; - } - - // Implementation (helpers) - - private void ValidateIndex(int index) - { - ValidateIndex(index,false); - } - - private void ValidateIndex(int index, bool allowEqualEnd) - { - int max = (allowEqualEnd)?(m_count):(m_count-1); - if (index < 0 || index > max) - throw new System.ArgumentOutOfRangeException("Index was out of range. Must be non-negative and less than the size of the collection.", (object)index, "Specified argument was out of the range of valid values."); - } - - private bool NeedsGrowth() - { - return (m_count >= Capacity); - } - - private void Grow() - { - if (NeedsGrowth()) - Capacity = m_count*2; - } - - private bool NeedsTrimming() - { - return (m_count <= Capacity/2); - } - - private void Trim() - { - if (NeedsTrimming()) - Capacity = m_count; - } - - // Implementation (ICollection) - - /* redundant w/ type-safe method - int ICollection.Count - { - get - { return m_count; } - } - */ - - bool ICollection.IsSynchronized - { - get - { return m_array.IsSynchronized; } - } - - object ICollection.SyncRoot - { - get - { return m_array.SyncRoot; } - } - - void ICollection.CopyTo(Array array, int start) - { - this.CopyTo((T[])array, start); - } - - // Implementation (IList) - - bool IList.IsFixedSize - { - get - { return false; } - } - - bool IList.IsReadOnly - { - get - { return false; } - } - - object IList.this[int index] - { - get - { return (object)this[index]; } - set - { this[index] = (T)value; } - } - - int IList.Add(object item) - { - return this.Add((T)item); - } - - /* redundant w/ type-safe method - void IList.Clear() - { - this.Clear(); - } - */ - - bool IList.Contains(object item) - { - return this.Contains((T)item); - } - - int IList.IndexOf(object item) - { - return this.IndexOf((T)item); - } - - void IList.Insert(int position, object item) - { - this.Insert(position, (T)item); - } - - void IList.Remove(object item) - { - this.Remove((T)item); - } - - /* redundant w/ type-safe method - void IList.RemoveAt(int index) - { - this.RemoveAt(index); - } - */ - - // Implementation (IEnumerable) - - IEnumerator IEnumerable.GetEnumerator() - { - return (IEnumerator)(this.GetEnumerator()); - } - - // Nested enumerator class - - /// - public class Enumerator : IEnumerator - { - private Int32Collection m_collection; - private int m_index; - private int m_version; - - // Construction - - /// - public Enumerator(Int32Collection tc) - { - m_collection = tc; - m_index = -1; - m_version = tc.m_version; - } - - // Operations (type-safe IEnumerator) - - /// - public T Current - { - get - { return m_collection[m_index]; } - } - - /// - public bool MoveNext() - { - if (m_version != m_collection.m_version) - throw new System.InvalidOperationException("Collection was modified; enumeration operation may not execute."); - - ++m_index; - return (m_index < m_collection.Count)?true:false; - } - - /// - public void Reset() - { - if (m_version != m_collection.m_version) - throw new System.InvalidOperationException("Collection was modified; enumeration operation may not execute."); - - m_index = -1; - } - - // Implementation (IEnumerator) - - object IEnumerator.Current - { - get - { return (object)(this.Current); } - } - - /* redundant w/ type-safe method - bool IEnumerator.MoveNext() - { - return this.MoveNext(); - } - */ - - /* redundant w/ type-safe method - void IEnumerator.Reset() - { - this.Reset(); - } - */ - } - } -} \ No newline at end of file diff --git a/src/Schematron/InvalidExpressionException.cs b/src/Schematron/InvalidExpressionException.cs index b716cfd..9874bef 100644 --- a/src/Schematron/InvalidExpressionException.cs +++ b/src/Schematron/InvalidExpressionException.cs @@ -1,47 +1,46 @@ -using System; using System.Runtime.Serialization; -namespace Schematron +namespace Schematron; + +/// +/// The exception that is thrown when an invalid XPath expression is used. +/// +/// +/// +public class InvalidExpressionException : ApplicationException { - /// - /// The exception that is thrown when an invalid XPath expression is used. - /// - /// - /// - public class InvalidExpressionException : ApplicationException - { - /// Initializes a new instance of the exception class. - public InvalidExpressionException() : base() - { - } + /// Initializes a new instance of the exception class. + public InvalidExpressionException() : base() + { + } - /// - /// Initializes an instance of the class with a specified error message. - /// - /// The error message that explains the reason for the exception. - public InvalidExpressionException(string message) : base(message) - { - } + /// + /// Initializes an instance of the class with a specified error message. + /// + /// The error message that explains the reason for the exception. + public InvalidExpressionException(string message) : base(message) + { + } - /// - /// For serialization purposes. - /// - /// Info - /// Context - protected InvalidExpressionException(SerializationInfo info, StreamingContext context) : - base(info, context) - { - } + /// + /// For serialization purposes. + /// + /// Info + /// Context + protected InvalidExpressionException(SerializationInfo info, StreamingContext context) : + base(info, context) + { + } - /// - /// Initializes an instance of the class with a specified error message - /// and a reference to the inner exception that is the cause for this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception. - public InvalidExpressionException(string message, Exception innerException) : - base(message, innerException) - { - } - } + /// + /// Initializes an instance of the class with a specified error message + /// and a reference to the inner exception that is the cause for this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. + public InvalidExpressionException(string message, Exception innerException) : + base(message, innerException) + { + } } + diff --git a/src/Schematron/NavigableType.cs b/src/Schematron/NavigableType.cs index 9bce8ed..2f42bbd 100644 --- a/src/Schematron/NavigableType.cs +++ b/src/Schematron/NavigableType.cs @@ -1,23 +1,21 @@ -using System; +namespace Schematron; -namespace Schematron +/// +/// Return type to use as the validation result. +/// +public enum NavigableType { - /// - /// Return type to use as the validation result. - /// - public enum NavigableType - { - /// - /// Use an for validation and return type. - /// - XmlDocument, - /// - /// Use an for validation and return type. - /// - XPathDocument, - /// - /// Use the default type, equal to , for validation and return type. - /// - Default, - } + /// + /// Use an for validation and return type. + /// + XmlDocument, + /// + /// Use an for validation and return type. + /// + XPathDocument, + /// + /// Use the default type, equal to , for validation and return type. + /// + Default, } + diff --git a/src/Schematron/OutputFormatting.cs b/src/Schematron/OutputFormatting.cs index 3dd6aa9..361a122 100644 --- a/src/Schematron/OutputFormatting.cs +++ b/src/Schematron/OutputFormatting.cs @@ -1,32 +1,30 @@ -using System; +namespace Schematron; -namespace Schematron +/// +/// Represents the valid output formats. +/// +/// +/// Items will be added to the list to reflect the +/// additional implementations we +/// will develop. +/// +/// +/// +public enum OutputFormatting { - /// - /// Represents the valid output formats. - /// - /// - /// Items will be added to the list to reflect the - /// additional implementations we - /// will develop. - /// - /// - /// - public enum OutputFormatting - { - /// Use the class. - Boolean, - /// Use the class. - Log, - /// Use the class. - Simple, - /// Use the default formatter, which is the . - Default, - /// Use the class. - XML - /* - /// Use the class. - HTML - */ - } + /// Use the class. + Boolean, + /// Use the class. + Log, + /// Use the class. + Simple, + /// Use the default formatter, which is the . + Default, + /// Use the class. + XML + /* + /// Use the class. + HTML + */ } + diff --git a/src/Schematron/Pattern.cs b/src/Schematron/Pattern.cs index 5e8dc70..f7907e6 100644 --- a/src/Schematron/Pattern.cs +++ b/src/Schematron/Pattern.cs @@ -1,90 +1,85 @@ -using System; -using System.Text; -using System.Xml.XPath; -using System.Collections; +namespace Schematron; -namespace Schematron +/// +/// A Pattern element, containing elements. +/// +/// +/// Constructor is not public. To programatically create an instance of this +/// class use the factory method. +/// +/// +/// +public class Pattern { - /// - /// A Pattern element, containing elements. - /// - /// - /// Constructor is not public. To programatically create an instance of this - /// class use the factory method. - /// - /// - /// - public class Pattern - { - string _name = String.Empty; - string _id = String.Empty; - RuleCollection _rules = new RuleCollection(); + string _name = String.Empty; + string _id = String.Empty; + RuleCollection _rules = new RuleCollection(); - #region Properties - /// Gets or sets the pattern's name. - public string Name - { - get { return _name; } - set { _name = value; } - } + #region Properties + /// Gets or sets the pattern's name. + public string Name + { + get { return _name; } + set { _name = value; } + } - /// Gets or sets the pattern's Id. - /// - /// This property is important because it is used by the - /// to activate certain patterns. - /// - public string Id - { - get { return _id; } - set { _id = value; } - } + /// Gets or sets the pattern's Id. + /// + /// This property is important because it is used by the + /// to activate certain patterns. + /// + public string Id + { + get { return _id; } + set { _id = value; } + } - /// Gets the rules contained in this pattern. - public RuleCollection Rules - { - get { return _rules; } - } - #endregion + /// Gets the rules contained in this pattern. + public RuleCollection Rules + { + get { return _rules; } + } + #endregion - /// Initializes the pattern with the name specified. - /// The name of the new pattern. - internal protected Pattern(string name) - { - _name = name; - } + /// Initializes the pattern with the name specified. + /// The name of the new pattern. + internal protected Pattern(string name) + { + _name = name; + } - /// Initializes the pattern with the name and id specified. - /// The name of the new pattern. - /// The id of the new pattern. - internal protected Pattern(string name, string id) - { - _name = name; - _id = id; - } + /// Initializes the pattern with the name and id specified. + /// The name of the new pattern. + /// The id of the new pattern. + internal protected Pattern(string name, string id) + { + _name = name; + _id = id; + } - #region Overridable Factory Methods - /// Creates a new rule instance. - /// - /// Inheritors should override this method to create instances - /// of their own rule implementations. - /// - public virtual Rule CreateRule() - { - return new Rule(); - } + #region Overridable Factory Methods + /// Creates a new rule instance. + /// + /// Inheritors should override this method to create instances + /// of their own rule implementations. + /// + public virtual Rule CreateRule() + { + return new Rule(); + } - /// Creates a new rule instance with the context specified. - /// - /// Inheritors should override this method to create instances - /// of their own rule implementations. - /// - /// - /// The context for the new rule. - /// - public virtual Rule CreateRule(string context) - { - return new Rule(context); - } - #endregion - } + /// Creates a new rule instance with the context specified. + /// + /// Inheritors should override this method to create instances + /// of their own rule implementations. + /// + /// + /// The context for the new rule. + /// + public virtual Rule CreateRule(string context) + { + return new Rule(context); + } + #endregion } + diff --git a/src/Schematron/PatternCollection.cs b/src/Schematron/PatternCollection.cs index a874dd5..dc62b46 100644 --- a/src/Schematron/PatternCollection.cs +++ b/src/Schematron/PatternCollection.cs @@ -1,77 +1,76 @@ -using System; using System.Collections; -namespace Schematron +namespace Schematron; + +/// A collection of Pattern elements. +/// +/// +public class PatternCollection : CollectionBase { - /// A collection of Pattern elements. - /// - /// - public class PatternCollection : CollectionBase - { - /// - public PatternCollection() - { - } + /// + public PatternCollection() + { + } - /// Returns the Pattern element at the specified position. - public Pattern this[int index] - { - get { return (Pattern)InnerList[index]; } - set { InnerList[index] = value; } - } + /// Returns the Pattern element at the specified position. + public Pattern this[int index] + { + get { return (Pattern)InnerList[index]; } + set { InnerList[index] = value; } + } - /// - public int Add(Pattern value) - { - return InnerList.Add(value); - } + /// + public int Add(Pattern value) + { + return InnerList.Add(value); + } - /// - public void AddRange(Pattern[] values) - { - foreach (Pattern elem in values) - Add(elem); - } + /// + public void AddRange(Pattern[] values) + { + foreach (Pattern elem in values) + Add(elem); + } - /// - public void AddRange(PatternCollection values) - { - foreach (Pattern elem in values) - Add(elem); - } + /// + public void AddRange(PatternCollection values) + { + foreach (Pattern elem in values) + Add(elem); + } - /// - public bool Contains(Pattern value) - { - return InnerList.Contains(value); - } + /// + public bool Contains(Pattern value) + { + return InnerList.Contains(value); + } - /// - public void CopyTo(Pattern[] array, int index) - { - InnerList.CopyTo(array, index); - } + /// + public void CopyTo(Pattern[] array, int index) + { + InnerList.CopyTo(array, index); + } - /// - public int IndexOf(Pattern value) - { - return InnerList.IndexOf(value); - } + /// + public int IndexOf(Pattern value) + { + return InnerList.IndexOf(value); + } - /// - public void Insert(int index, Pattern value) - { - InnerList.Insert(index, value); - } + /// + public void Insert(int index, Pattern value) + { + InnerList.Insert(index, value); + } - /// - public void Remove(Pattern value) - { - int index = IndexOf(value); - if ( index < 0 ) - throw(new ArgumentException("The specified object is not found in the collection")); + /// + public void Remove(Pattern value) + { + int index = IndexOf(value); + if (index < 0) + throw (new ArgumentException("The specified object is not found in the collection")); - RemoveAt(index); - } - } + RemoveAt(index); + } } + diff --git a/src/Schematron/Phase.cs b/src/Schematron/Phase.cs index d417340..9294fa4 100644 --- a/src/Schematron/Phase.cs +++ b/src/Schematron/Phase.cs @@ -1,89 +1,86 @@ -using System; -using System.Xml.XPath; +namespace Schematron; -namespace Schematron +/// +/// A Phase element, containing the active elements. +/// +/// +/// The allows a certaing degree of workflow +/// through the use of phases. A document can have several states, and +/// therefore different sets of rules should be checked against it. +/// +/// This element allows execution of a set of 'active' patterns. +/// +/// +/// Constructor is not public. To programatically create an instance of this +/// class use the factory method. +/// +/// +/// +/// +public class Phase { - /// - /// A Phase element, containing the active elements. - /// - /// - /// The allows a certaing degree of workflow - /// through the use of phases. A document can have several states, and - /// therefore different sets of rules should be checked against it. - /// - /// This element allows execution of a set of 'active' patterns. - /// - /// - /// Constructor is not public. To programatically create an instance of this - /// class use the factory method. - /// - /// - /// - /// - public class Phase - { - string _id = String.Empty; - PatternCollection _patterns = new PatternCollection(); + string _id = String.Empty; + PatternCollection _patterns = new PatternCollection(); - /// - /// The identifier to check for All phases. - /// - /// Causes all the patterns in a schema to be checked, - /// irrespective of the phases where they are activated. - public const string All = "#ALL"; + /// + /// The identifier to check for All phases. + /// + /// Causes all the patterns in a schema to be checked, + /// irrespective of the phases where they are activated. + public const string All = "#ALL"; - /// Initializes a new instance of the class with the specified Id. - /// The Id of the new phase. - internal protected Phase(string id) - { - Id = id; - } + /// Initializes a new instance of the class with the specified Id. + /// The Id of the new phase. + internal protected Phase(string id) + { + Id = id; + } - /// Initializes a new instance of the class. - internal protected Phase() - { - } + /// Initializes a new instance of the class. + internal protected Phase() + { + } - #region Properties - /// Gets or sets the phase identifier. - public string Id - { - get { return _id; } - set { _id = value; } - } + #region Properties + /// Gets or sets the phase identifier. + public string Id + { + get { return _id; } + set { _id = value; } + } - /// Gets the collection of child elements. - public PatternCollection Patterns - { - get { return _patterns; } - } - #endregion + /// Gets the collection of child elements. + public PatternCollection Patterns + { + get { return _patterns; } + } + #endregion - #region Overridable Factory Methods - /// Creates a new pattern instance. - /// - /// Inheritors should override this method to create instances - /// of their own pattern implementations. - /// - /// The name of the new pattern. - /// The unique identifier of the new pattern. - public virtual Pattern CreatePattern(string name, string id) - { - return new Pattern(name, id); - } + #region Overridable Factory Methods + /// Creates a new pattern instance. + /// + /// Inheritors should override this method to create instances + /// of their own pattern implementations. + /// + /// The name of the new pattern. + /// The unique identifier of the new pattern. + public virtual Pattern CreatePattern(string name, string id) + { + return new Pattern(name, id); + } - /// Creates a new pattern instance. - /// - /// This method calls the overloaded version passing a default - /// value for the pattern's id. - /// Inheritors can override this method if they want to provide - /// a different default value. - /// - /// The name of the new pattern. - public virtual Pattern CreatePattern(string name) - { - return CreatePattern(name, String.Empty); - } - #endregion - } + /// Creates a new pattern instance. + /// + /// This method calls the overloaded version passing a default + /// value for the pattern's id. + /// Inheritors can override this method if they want to provide + /// a different default value. + /// + /// The name of the new pattern. + public virtual Pattern CreatePattern(string name) + { + return CreatePattern(name, String.Empty); + } + #endregion } + diff --git a/src/Schematron/PhaseCollection.cs b/src/Schematron/PhaseCollection.cs index 6131837..40969b4 100644 --- a/src/Schematron/PhaseCollection.cs +++ b/src/Schematron/PhaseCollection.cs @@ -1,60 +1,58 @@ -using System; using System.Collections; -using System.Collections.Specialized; -namespace Schematron +namespace Schematron; + +/// A collection of Phase elements +/// +/// +public class PhaseCollection : DictionaryBase { - /// A collection of Phase elements - /// - /// - public class PhaseCollection : DictionaryBase - { - - /// - public PhaseCollection() - { - } - - /// Required indexer. - public Phase this[string key] - { - get { return (Phase)Dictionary[key]; } - set { Dictionary[key] = value; } - } - - /// - public void Add(Phase value) - { - Dictionary.Add(value.Id, value); - } - - /// - public void AddRange(Phase[] values) - { - foreach (Phase elem in values) - Add(elem); - } - - /// - public void AddRange(PhaseCollection values) - { - foreach (Phase elem in values) - Add(elem); - } - - /// - public bool Contains(string key) - { - return Dictionary.Contains(key); - } - - /// - public void Remove(Phase value) - { - if (!Dictionary.Contains(value.Id)) - throw(new ArgumentException("The specified object is not found in the collection")); - - Dictionary.Remove(value.Id); - } - } + + /// + public PhaseCollection() + { + } + + /// Required indexer. + public Phase this[string key] + { + get { return (Phase)Dictionary[key]; } + set { Dictionary[key] = value; } + } + + /// + public void Add(Phase value) + { + Dictionary.Add(value.Id, value); + } + + /// + public void AddRange(Phase[] values) + { + foreach (Phase elem in values) + Add(elem); + } + + /// + public void AddRange(PhaseCollection values) + { + foreach (Phase elem in values) + Add(elem); + } + + /// + public bool Contains(string key) + { + return Dictionary.Contains(key); + } + + /// + public void Remove(Phase value) + { + if (!Dictionary.Contains(value.Id)) + throw (new ArgumentException("The specified object is not found in the collection")); + + Dictionary.Remove(value.Id); + } } + diff --git a/src/Schematron/Properties/AssemblyInfo.cs b/src/Schematron/Properties/AssemblyInfo.cs deleted file mode 100644 index 0c7326a..0000000 --- a/src/Schematron/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ -ο»Ώusing System.Reflection; - -[assembly: AssemblyTitle("Schematron.NET")] -[assembly: AssemblyDescription("A C# native .NET implementation of the Schematron ISO/IEC standard")] \ No newline at end of file diff --git a/src/Schematron/Report.cs b/src/Schematron/Report.cs index 3aa7bbe..17b0eee 100644 --- a/src/Schematron/Report.cs +++ b/src/Schematron/Report.cs @@ -1,31 +1,29 @@ -using System; +namespace Schematron; -namespace Schematron +/// +/// A Report element. +/// +/// +/// As stated in the , this is the other +/// lowest element (aside from ) in a Schematron schema. +/// This element contains the expression +/// to execute in the context of its parent . +/// If the results of the execution of the expression are true, the +/// report succeeds and the correponding message will be displayed. +/// +/// Constructor is not public. To programatically create an instance of this +/// class use the factory method. +/// +/// +/// +/// +public class Report : Test { - /// - /// A Report element. - /// - /// - /// As stated in the , this is the other - /// lowest element (aside from ) in a Schematron schema. - /// This element contains the expression - /// to execute in the context of its parent . - /// If the results of the execution of the expression are true, the - /// report succeeds and the correponding message will be displayed. - /// - /// Constructor is not public. To programatically create an instance of this - /// class use the factory method. - /// - /// - /// - /// - public class Report : Test - { - /// Initializes a new instance of the class with the parameters specified. - /// The XPath expression to test. - /// The message to return. - internal protected Report(string test, string message) : base(test, message) - { - } - } + /// Initializes a new instance of the class with the parameters specified. + /// The XPath expression to test. + /// The message to return. + internal protected Report(string test, string message) : base(test, message) + { + } } + diff --git a/src/Schematron/Rule.cs b/src/Schematron/Rule.cs index 92a1899..fbe6134 100644 --- a/src/Schematron/Rule.cs +++ b/src/Schematron/Rule.cs @@ -1,190 +1,180 @@ -using System; -using System.Xml; -using System.Xml.XPath; -using System.Collections; -using System.Text; -using System.Runtime.Remoting.Messaging; - -namespace Schematron +namespace Schematron; + +/// +/// A Rule element. +/// +/// +/// Rules enclose the and +/// elements, providing the context for their evaluation. +/// According to the , nodes can be evaluated +/// by only one rule inside a , so typical schema +/// design includes placing the most exceptional rules first, down to +/// more generally applicable ones. +/// +/// Constructor is not public. To programatically create an instance of this +/// class use the factory method. +/// +/// +/// +/// Should we add support for the key element? +/// +/// +public class Rule : EvaluableExpression { - /// - /// A Rule element. - /// - /// - /// Rules enclose the and - /// elements, providing the context for their evaluation. - /// According to the , nodes can be evaluated - /// by only one rule inside a , so typical schema - /// design includes placing the most exceptional rules first, down to - /// more generally applicable ones. - /// - /// Constructor is not public. To programatically create an instance of this - /// class use the factory method. - /// - /// - /// - /// Should we add support for the key element? - /// - /// - public class Rule : EvaluableExpression - { - // TODO: add support to child elements? - - TestCollection _asserts = new TestCollection(); - TestCollection _reports = new TestCollection(); - string _id = String.Empty; - bool _abstract = true; - - /// - /// Creates an abstract rule, without context. - /// - internal protected Rule() - { - } - - /// Initializes a new instance of the class, with the received context. - /// The rule's context to evaluate. - /// If passed a null or an , this is implicitly an abstract rule. - internal protected Rule(string context) - { - InitContext(context); - } - - /// Initializes the context for the rule. - /// The rule's context to evaluate. - /// - /// If passed a null or an , this is implicitly an abstract rule. - /// - /// - /// Rules are evaluated through all the document (//), unless they - /// explicitly want to start from the root (/). This is consistent - /// with XSLT template match behavior. So we have to split the expression - /// per union (|) to add the root expression in these cases. - /// - private void InitContext(string context) - { - if (context == null || context == String.Empty) - { - _abstract = true; - return; - } - - - // Rules are evaluated through all the document (//), unless they - // explicitly want to start from the root (/). - // We have to split per union (|) to add the root expression. - string[] parts = context.Split('|'); - - for (int i = 0; i < parts.Length; i++) - { - // Trim to build the string properly. - parts[i] = parts[i].Trim(); - - // Append the slashes as appropriate. - if (!parts[i].StartsWith("/")) - parts[i] = "//" + parts[i]; - } - - // Finally, join again the location paths and initialize the expression. - InitializeExpression(String.Join(" | ", parts)); - _abstract = false; - } - - #region Overridable Factory Methods - /// Creates a new assert instance. - /// - /// Inheritors should override this method to create instances - /// of their own assert implementations. - /// - /// - /// The XPath expression to test for the assert. See - /// . - /// - /// - /// The message to display if the assert fails. See - /// . - /// - public virtual Assert CreateAssert(string test, string message) - { - return new Assert(test, message); - } - - /// Creates a new report instance. - /// - /// Inheritors should override this method to create instances - /// of their own report implementations. - /// - /// - /// The XPath expression to test for the report. See - /// . - /// - /// - /// The message to display if the report succeeds. See - /// . - /// - public virtual Report CreateReport(string test, string message) - { - return new Report(test, message); - } - #endregion - - /// - public string Id - { - get { return _id; } - set { _id = value; } - } - - /// - public string Context - { - get { return base.Expression; } - set { InitContext(value); } - } - - /// - public bool IsAbstract - { - get { return (_abstract); } - } - - /// - public TestCollection Asserts - { - get { return _asserts; } - } - - /// - public TestCollection Reports - { - get { return _reports; } - } - - /// - public void AddAssert(string test, string message) - { - _asserts.Add(CreateAssert(test, message)); - } - - /// - public void AddReport(string test, string message) - { - _reports.Add(CreateReport(test, message)); - } - - /// - /// - /// Only abstract rules can be used as a base for extensions. - public void Extend(Rule parent) - { - if (!parent.IsAbstract) - throw new ArgumentException("The rule to extend must be abstract.", "parent"); - - foreach (Assert asr in parent._asserts) - _asserts.Add(asr); - - foreach (Report rpt in parent._reports) - _reports.Add(rpt); - } - } + // TODO: add support to child elements? + + TestCollection _asserts = new TestCollection(); + TestCollection _reports = new TestCollection(); + string _id = String.Empty; + bool _abstract = true; + + /// + /// Creates an abstract rule, without context. + /// + internal protected Rule() + { + } + + /// Initializes a new instance of the class, with the received context. + /// The rule's context to evaluate. + /// If passed a null or an , this is implicitly an abstract rule. + internal protected Rule(string context) + { + InitContext(context); + } + + /// Initializes the context for the rule. + /// The rule's context to evaluate. + /// + /// If passed a null or an , this is implicitly an abstract rule. + /// + /// + /// Rules are evaluated through all the document (//), unless they + /// explicitly want to start from the root (/). This is consistent + /// with XSLT template match behavior. So we have to split the expression + /// per union (|) to add the root expression in these cases. + /// + void InitContext(string context) + { + if (string.IsNullOrEmpty(context)) + { + _abstract = true; + return; + } + + + // Rules are evaluated through all the document (//), unless they + // explicitly want to start from the root (/). + // We have to split per union (|) to add the root expression. + var parts = context.Split('|'); + + for (int i = 0; i < parts.Length; i++) + { + parts[i] = parts[i].Trim(); + + if (!parts[i].StartsWith("/")) + parts[i] = "//" + parts[i]; + } + + InitializeExpression(string.Join(" | ", parts)); + _abstract = false; + } + + #region Overridable Factory Methods + /// Creates a new assert instance. + /// + /// Inheritors should override this method to create instances + /// of their own assert implementations. + /// + /// + /// The XPath expression to test for the assert. See + /// . + /// + /// + /// The message to display if the assert fails. See + /// . + /// + public virtual Assert CreateAssert(string test, string message) + { + return new Assert(test, message); + } + + /// Creates a new report instance. + /// + /// Inheritors should override this method to create instances + /// of their own report implementations. + /// + /// + /// The XPath expression to test for the report. See + /// . + /// + /// + /// The message to display if the report succeeds. See + /// . + /// + public virtual Report CreateReport(string test, string message) + { + return new Report(test, message); + } + #endregion + + /// + public string Id + { + get { return _id; } + set { _id = value; } + } + + /// + public string Context + { + get { return base.Expression; } + set { InitContext(value); } + } + + /// + public bool IsAbstract + { + get { return (_abstract); } + } + + /// + public TestCollection Asserts + { + get { return _asserts; } + } + + /// + public TestCollection Reports + { + get { return _reports; } + } + + /// + public void AddAssert(string test, string message) + { + _asserts.Add(CreateAssert(test, message)); + } + + /// + public void AddReport(string test, string message) + { + _reports.Add(CreateReport(test, message)); + } + + /// + /// + /// Only abstract rules can be used as a base for extensions. + public void Extend(Rule parent) + { + if (!parent.IsAbstract) + throw new ArgumentException("The rule to extend must be abstract.", "parent"); + + foreach (Assert asr in parent._asserts) + _asserts.Add(asr); + + foreach (Report rpt in parent._reports) + _reports.Add(rpt); + } } + diff --git a/src/Schematron/RuleCollection.cs b/src/Schematron/RuleCollection.cs index ec33ffd..c280c22 100644 --- a/src/Schematron/RuleCollection.cs +++ b/src/Schematron/RuleCollection.cs @@ -1,77 +1,76 @@ -using System; using System.Collections; -namespace Schematron +namespace Schematron; + +/// A collection of Rule elements. +/// +/// +public class RuleCollection : CollectionBase { - /// A collection of Rule elements. - /// - /// - public class RuleCollection : CollectionBase - { - /// - public RuleCollection() - { - } + /// + public RuleCollection() + { + } - /// Returns the Rule element at the specified position. - public Rule this[int index] - { - get { return (Rule)InnerList[index]; } - set { InnerList[index] = value; } - } + /// Returns the Rule element at the specified position. + public Rule this[int index] + { + get { return (Rule)InnerList[index]; } + set { InnerList[index] = value; } + } - /// - public int Add(Rule value) - { - return InnerList.Add(value); - } + /// + public int Add(Rule value) + { + return InnerList.Add(value); + } - /// - public void AddRange(Rule[] values) - { - foreach (Rule elem in values) - Add(elem); - } + /// + public void AddRange(Rule[] values) + { + foreach (Rule elem in values) + Add(elem); + } - /// - public void AddRange(RuleCollection values) - { - foreach (Rule elem in values) - Add(elem); - } + /// + public void AddRange(RuleCollection values) + { + foreach (Rule elem in values) + Add(elem); + } - /// - public bool Contains(Rule value) - { - return InnerList.Contains(value); - } + /// + public bool Contains(Rule value) + { + return InnerList.Contains(value); + } - /// - public void CopyTo(Rule[] array, int index) - { - InnerList.CopyTo(array, index); - } + /// + public void CopyTo(Rule[] array, int index) + { + InnerList.CopyTo(array, index); + } - /// - public int IndexOf(Rule value) - { - return InnerList.IndexOf(value); - } + /// + public int IndexOf(Rule value) + { + return InnerList.IndexOf(value); + } - /// - public void Insert(int index, Rule value) - { - InnerList.Insert(index, value); - } + /// + public void Insert(int index, Rule value) + { + InnerList.Insert(index, value); + } - /// - public void Remove(Rule value) - { - int index = IndexOf(value); - if ( index < 0 ) - throw(new ArgumentException("The specified object is not found in the collection")); + /// + public void Remove(Rule value) + { + int index = IndexOf(value); + if (index < 0) + throw (new ArgumentException("The specified object is not found in the collection")); - RemoveAt(index); - } - } + RemoveAt(index); + } } + diff --git a/src/Schematron/Schema.cs b/src/Schematron/Schema.cs index 35cf6dd..afcc68f 100644 --- a/src/Schematron/Schema.cs +++ b/src/Schematron/Schema.cs @@ -1,148 +1,144 @@ -using System; -using System.IO; using System.Xml; using System.Xml.XPath; -namespace Schematron +namespace Schematron; + +/// +/// +/// +/// Lacks attributes defined in Schematron, but not in use currently. +public class Schema { - /// - /// - /// - /// Lacks attributes defined in Schematron, but not in use currently. - public class Schema - { - /// - /// The Schematron namespace. - /// - public const string Namespace = "http://www.ascc.net/xml/schematron"; - - SchemaLoader _loader; - string _title = String.Empty; - - string _defaultphase = String.Empty; - PhaseCollection _phases = new PhaseCollection(); - PatternCollection _patterns = new PatternCollection(); - XmlNamespaceManager _ns; - - /// - public Schema() - { - _loader = CreateLoader(); - } - - /// - public Schema(string title) : this() - { - _title = title; - } - - #region Overridable Factory Methods - /// - internal protected virtual SchemaLoader CreateLoader() - { - return new SchemaLoader(this); - } - - /// - public virtual Phase CreatePhase(string id) - { - return new Phase(id); - } - - /// - public virtual Phase CreatePhase() - { - return new Phase(); - } - #endregion - - #region Overloaded Load methods - /// - /// Loads the schema from the specified URI. - /// - public void Load(string uri) - { - using (FileStream fs = new FileStream(uri, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - Load(new XmlTextReader(fs)); - } - } - - /// - /// Loads the schema from the reader. Closing the reader is responsibility of the caller. - /// - public void Load(TextReader reader) - { - Load(new XmlTextReader(reader)); - } - - /// - /// Loads the schema from the stream. Closing the stream is responsibility of the caller. - /// - public void Load(Stream input) - { - Load(new XmlTextReader(input)); - } - - /// - /// Loads the schema from the reader. Closing the reader is responsibility of the caller. - /// - public void Load(XmlReader schema) - { - XmlDocument doc = new XmlDocument(schema.NameTable); - doc.Load(schema); - Load(doc.CreateNavigator()); - } - - /// - public void Load(XPathNavigator schema) - { - Loader.LoadSchema(schema); - } - #endregion - - #region Properties - /// - internal protected SchemaLoader Loader - { - get { return _loader; } - set { _loader = value; } - } - - /// - public string DefaultPhase - { - get { return _defaultphase; } - set { _defaultphase = value; } - } - - /// - public string Title - { - get { return _title; } - set { _title = value; } - } - - /// - public PhaseCollection Phases - { - get { return _phases; } - set { _phases = value; } - } - - /// - public PatternCollection Patterns - { - get { return _patterns; } - set { _patterns = value; } - } - - /// - public XmlNamespaceManager NsManager - { - get { return _ns; } - set { _ns = value; } - } - #endregion - } + /// + /// The Schematron namespace. + /// + public const string Namespace = "http://www.ascc.net/xml/schematron"; + + SchemaLoader _loader; + string _title = String.Empty; + + string _defaultphase = String.Empty; + PhaseCollection _phases = new PhaseCollection(); + PatternCollection _patterns = new PatternCollection(); + XmlNamespaceManager _ns = null!; + + /// + public Schema() + { + _loader = CreateLoader(); + } + + /// + public Schema(string title) : this() + { + _title = title; + } + + #region Overridable Factory Methods + /// + internal protected virtual SchemaLoader CreateLoader() + { + return new SchemaLoader(this); + } + + /// + public virtual Phase CreatePhase(string id) + { + return new Phase(id); + } + + /// + public virtual Phase CreatePhase() + { + return new Phase(); + } + #endregion + + #region Overloaded Load methods + /// + /// Loads the schema from the specified URI. + /// + public void Load(string uri) + { + using var fs = new FileStream(uri, FileMode.Open, FileAccess.Read, FileShare.Read); + Load(new XmlTextReader(fs)); + } + + /// + /// Loads the schema from the reader. Closing the reader is responsibility of the caller. + /// + public void Load(TextReader reader) + { + Load(new XmlTextReader(reader)); + } + + /// + /// Loads the schema from the stream. Closing the stream is responsibility of the caller. + /// + public void Load(Stream input) + { + Load(new XmlTextReader(input)); + } + + /// + /// Loads the schema from the reader. Closing the reader is responsibility of the caller. + /// + public void Load(XmlReader schema) + { + var doc = new XmlDocument(schema.NameTable); + doc.Load(schema); + Load(doc.CreateNavigator()); + } + + /// + public void Load(XPathNavigator schema) + { + Loader.LoadSchema(schema); + } + #endregion + + #region Properties + /// + internal protected SchemaLoader Loader + { + get { return _loader; } + set { _loader = value; } + } + + /// + public string DefaultPhase + { + get { return _defaultphase; } + set { _defaultphase = value; } + } + + /// + public string Title + { + get { return _title; } + set { _title = value; } + } + + /// + public PhaseCollection Phases + { + get { return _phases; } + set { _phases = value; } + } + + /// + public PatternCollection Patterns + { + get { return _patterns; } + set { _patterns = value; } + } + + /// + public XmlNamespaceManager NsManager + { + get { return _ns; } + set { _ns = value; } + } + #endregion } + diff --git a/src/Schematron/SchemaCollection.cs b/src/Schematron/SchemaCollection.cs index 0fb0ecb..724b6ea 100644 --- a/src/Schematron/SchemaCollection.cs +++ b/src/Schematron/SchemaCollection.cs @@ -1,77 +1,76 @@ -using System; using System.Collections; -namespace Schematron +namespace Schematron; + +/// A collection of schematron schemas. +/// +/// +public class SchemaCollection : CollectionBase { - /// A collection of schematron schemas. - /// - /// - public class SchemaCollection : CollectionBase - { - /// - public SchemaCollection() - { - } + /// + public SchemaCollection() + { + } - /// Returns the Schema element at the specified position. - public Schema this[int index] - { - get { return (Schema)InnerList[index]; } - set { InnerList[index] = value; } - } + /// Returns the Schema element at the specified position. + public Schema this[int index] + { + get { return (Schema)InnerList[index]; } + set { InnerList[index] = value; } + } - /// - public int Add(Schema value) - { - return InnerList.Add(value); - } + /// + public int Add(Schema value) + { + return InnerList.Add(value); + } - /// - public void AddRange(Schema[] values) - { - foreach (Schema elem in values) - Add(elem); - } + /// + public void AddRange(Schema[] values) + { + foreach (Schema elem in values) + Add(elem); + } - /// - public void AddRange(SchemaCollection values) - { - foreach (Schema elem in values) - Add(elem); - } + /// + public void AddRange(SchemaCollection values) + { + foreach (Schema elem in values) + Add(elem); + } - /// - public bool Contains(Schema value) - { - return InnerList.Contains(value); - } + /// + public bool Contains(Schema value) + { + return InnerList.Contains(value); + } - /// - public void CopyTo(Schema[] array, int index) - { - InnerList.CopyTo(array, index); - } + /// + public void CopyTo(Schema[] array, int index) + { + InnerList.CopyTo(array, index); + } - /// - public int IndexOf(Schema value) - { - return InnerList.IndexOf(value); - } + /// + public int IndexOf(Schema value) + { + return InnerList.IndexOf(value); + } - /// - public void Insert(int index, Schema value) - { - InnerList.Insert(index, value); - } + /// + public void Insert(int index, Schema value) + { + InnerList.Insert(index, value); + } - /// - public void Remove(Schema value) - { - int index = IndexOf(value); - if ( index < 0 ) - throw(new ArgumentException("The specified object is not found in the collection")); + /// + public void Remove(Schema value) + { + int index = IndexOf(value); + if (index < 0) + throw (new ArgumentException("The specified object is not found in the collection")); - RemoveAt(index); - } - } + RemoveAt(index); + } } + diff --git a/src/Schematron/SchemaLoader.cs b/src/Schematron/SchemaLoader.cs index ed0a65e..b715b3b 100644 --- a/src/Schematron/SchemaLoader.cs +++ b/src/Schematron/SchemaLoader.cs @@ -1,240 +1,239 @@ -using System; +using System.Collections; using System.Xml; using System.Xml.XPath; -using System.Collections; -namespace Schematron +namespace Schematron; + +/// +public class SchemaLoader { - /// - public class SchemaLoader - { - Schema _schema; - XPathNavigator _filenav; - Hashtable _abstracts = null; - - /// - public SchemaLoader(Schema schema) - { - _schema = schema; - } - - /// - /// - public virtual void LoadSchema(XPathNavigator source) - { - _schema.NsManager = new XmlNamespaceManager(source.NameTable); - //_schema.NsManager = new GotDotNet.Exslt.ExsltContext(source.NameTable); - - XPathNodeIterator it = source.Select(CompiledExpressions.Schema); - if (it.Count > 1) - throw new BadSchemaException("There can be at most one schema element per Schematron schema."); - - // Always work with the whole document to look for elements. - // Embedded schematron will work as well as stand-alone schemas. - _filenav = source; - - if (it.Count == 1) - { - it.MoveNext(); - LoadSchemaElement(it.Current); - } - else - { - // Load child elements from the appinfo element if it exists. - LoadSchemaElements(source.Select(CompiledExpressions.EmbeddedSchema)); - } - - #region Loading process start - RetrieveAbstractRules(); - LoadPhases(); - LoadPatterns(); - #endregion - } - - private void LoadSchemaElement(XPathNavigator context) - { - string phase = context.GetAttribute("defaultPhase", String.Empty); - if (phase != String.Empty) - _schema.DefaultPhase = phase; - - //TODO: add all schema attributes in the future. - LoadSchemaElements(context.SelectChildren(XPathNodeType.Element)); - } - - private void LoadSchemaElements(XPathNodeIterator children) - { - while (children.MoveNext()) - { - if (children.Current.NamespaceURI == Schema.Namespace) - { - if (children.Current.LocalName == "title") - { - _schema.Title = children.Current.Value; - } - else if (children.Current.LocalName == "ns") - { - _schema.NsManager.AddNamespace( - children.Current.GetAttribute("prefix", String.Empty), - children.Current.GetAttribute("uri", String.Empty)); - } - } - } - } - - private void RetrieveAbstractRules() - { - _filenav.MoveToRoot(); - XPathNodeIterator it = _filenav.Select(CompiledExpressions.AbstractRule); - if (it.Count == 0) return; - - _abstracts = new Hashtable(it.Count); - - // Dummy pattern to use for rule creation purposes. - // TODO: is there a better factory method implementation? - Pattern pt = _schema.CreatePhase(String.Empty).CreatePattern(String.Empty); - - while (it.MoveNext()) - { - Rule rule = pt.CreateRule(); - rule.SetContext(_schema.NsManager); - rule.Id = it.Current.GetAttribute("id", String.Empty); - LoadAsserts(rule, it.Current); - LoadReports(rule, it.Current); - _abstracts.Add(rule.Id, rule); - } - } - - private void LoadPhases() - { - _filenav.MoveToRoot(); - XPathNodeIterator phases = _filenav.Select(CompiledExpressions.Phase); - if (phases.Count == 0) return; - - while (phases.MoveNext()) - { - Phase ph = _schema.CreatePhase(phases.Current.GetAttribute("id", String.Empty)); - _schema.Phases.Add(ph); - } - } - - private void LoadPatterns() - { - _filenav.MoveToRoot(); - XPathNodeIterator patterns = _filenav.Select(CompiledExpressions.Pattern); - - if (patterns.Count == 0) return; - - // A special #ALL phase which contains all the patterns in the schema. - Phase phase = _schema.CreatePhase(Phase.All); - - while (patterns.MoveNext()) - { - Pattern pt = phase.CreatePattern(patterns.Current.GetAttribute("name", String.Empty), - patterns.Current.GetAttribute("id", String.Empty)); - - LoadRules(pt, patterns.Current); - _schema.Patterns.Add(pt); - phase.Patterns.Add(pt); - - if (pt.Id != String.Empty) - { - // Select the phases in which this pattern is active, and add it - // to its collection of patterns. - // This is the only dynamic expression in the process. - // TODO: try to precompile this. Is it possible? - XPathExpression expr = Config.DefaultNavigator.Compile( - "//sch:phase[sch:active/@pattern=\"" + pt.Id + "\"]/@id"); - expr.SetContext(Config.DefaultNsManager); - XPathNodeIterator phases = _filenav.Select(expr); - - while (phases.MoveNext()) - { - _schema.Phases[phases.Current.Value].Patterns.Add(pt); - } - } - } - - _schema.Phases.Add(phase); - } - - private void LoadRules(Pattern pattern, XPathNavigator context) - { - XPathNodeIterator rules = context.Select(CompiledExpressions.ConcreteRule); - if (rules.Count == 0) return; - - while (rules.MoveNext()) - { - Rule rule = pattern.CreateRule(rules.Current.GetAttribute("context", String.Empty)); - rule.Id = rules.Current.GetAttribute("id", String.Empty); - rule.SetContext(_schema.NsManager); - LoadExtends(rule, rules.Current); - LoadAsserts(rule, rules.Current); - LoadReports(rule, rules.Current); - pattern.Rules.Add(rule); - } - } - - private void LoadExtends(Rule rule, XPathNavigator context) - { - XPathNodeIterator extends = context.Select(CompiledExpressions.RuleExtends); - if (extends.Count == 0) return; - - while (extends.MoveNext()) - { - string ruleName = extends.Current.GetAttribute("rule", String.Empty); - if (_abstracts.ContainsKey(ruleName)) - rule.Extend((Rule) _abstracts[ruleName]); - else - throw new BadSchemaException("The abstract rule with id=\""+ruleName+"\" is used but not defined."); + Schema _schema; + XPathNavigator _filenav = null!; + Hashtable? _abstracts = null; + + /// + public SchemaLoader(Schema schema) + { + _schema = schema; + } + + /// + /// + public virtual void LoadSchema(XPathNavigator source) + { + _schema.NsManager = new XmlNamespaceManager(source.NameTable); + //_schema.NsManager = new GotDotNet.Exslt.ExsltContext(source.NameTable); + + XPathNodeIterator it = source.Select(CompiledExpressions.Schema); + if (it.Count > 1) + throw new BadSchemaException("There can be at most one schema element per Schematron schema."); + + // Always work with the whole document to look for elements. + // Embedded schematron will work as well as stand-alone schemas. + _filenav = source; + + if (it.Count == 1) + { + it.MoveNext(); + LoadSchemaElement(it.Current); + } + else + { + // Load child elements from the appinfo element if it exists. + LoadSchemaElements(source.Select(CompiledExpressions.EmbeddedSchema)); + } + + #region Loading process start + RetrieveAbstractRules(); + LoadPhases(); + LoadPatterns(); + #endregion + } + + void LoadSchemaElement(XPathNavigator context) + { + string phase = context.GetAttribute("defaultPhase", String.Empty); + if (phase != String.Empty) + _schema.DefaultPhase = phase; + + //TODO: add all schema attributes in the future. + LoadSchemaElements(context.SelectChildren(XPathNodeType.Element)); + } + + void LoadSchemaElements(XPathNodeIterator children) + { + while (children.MoveNext()) + { + if (children.Current.NamespaceURI == Schema.Namespace) + { + if (children.Current.LocalName == "title") + { + _schema.Title = children.Current.Value; + } + else if (children.Current.LocalName == "ns") + { + _schema.NsManager.AddNamespace( + children.Current.GetAttribute("prefix", String.Empty), + children.Current.GetAttribute("uri", String.Empty)); + } + } + } + } + + void RetrieveAbstractRules() + { + _filenav.MoveToRoot(); + XPathNodeIterator it = _filenav.Select(CompiledExpressions.AbstractRule); + if (it.Count == 0) return; + + _abstracts = new Hashtable(it.Count); + + // Dummy pattern to use for rule creation purposes. + // TODO: is there a better factory method implementation? + Pattern pt = _schema.CreatePhase(String.Empty).CreatePattern(String.Empty); + + while (it.MoveNext()) + { + Rule rule = pt.CreateRule(); + rule.SetContext(_schema.NsManager); + rule.Id = it.Current.GetAttribute("id", String.Empty); + LoadAsserts(rule, it.Current); + LoadReports(rule, it.Current); + _abstracts.Add(rule.Id, rule); + } + } + + void LoadPhases() + { + _filenav.MoveToRoot(); + XPathNodeIterator phases = _filenav.Select(CompiledExpressions.Phase); + if (phases.Count == 0) return; + + while (phases.MoveNext()) + { + Phase ph = _schema.CreatePhase(phases.Current.GetAttribute("id", String.Empty)); + _schema.Phases.Add(ph); + } + } + + void LoadPatterns() + { + _filenav.MoveToRoot(); + XPathNodeIterator patterns = _filenav.Select(CompiledExpressions.Pattern); + + if (patterns.Count == 0) return; + + // A special #ALL phase which contains all the patterns in the schema. + Phase phase = _schema.CreatePhase(Phase.All); + + while (patterns.MoveNext()) + { + Pattern pt = phase.CreatePattern(patterns.Current.GetAttribute("name", String.Empty), + patterns.Current.GetAttribute("id", String.Empty)); + + LoadRules(pt, patterns.Current); + _schema.Patterns.Add(pt); + phase.Patterns.Add(pt); + + if (pt.Id != String.Empty) + { + // Select the phases in which this pattern is active, and add it + // to its collection of patterns. + // This is the only dynamic expression in the process. + // TODO: try to precompile this. Is it possible? + XPathExpression expr = Config.DefaultNavigator.Compile( + "//sch:phase[sch:active/@pattern=\"" + pt.Id + "\"]/@id"); + expr.SetContext(Config.DefaultNsManager); + XPathNodeIterator phases = _filenav.Select(expr); + + while (phases.MoveNext()) + { + _schema.Phases[phases.Current.Value].Patterns.Add(pt); + } + } + } + + _schema.Phases.Add(phase); + } + + void LoadRules(Pattern pattern, XPathNavigator context) + { + XPathNodeIterator rules = context.Select(CompiledExpressions.ConcreteRule); + if (rules.Count == 0) return; + + while (rules.MoveNext()) + { + Rule rule = pattern.CreateRule(rules.Current.GetAttribute("context", String.Empty)); + rule.Id = rules.Current.GetAttribute("id", String.Empty); + rule.SetContext(_schema.NsManager); + LoadExtends(rule, rules.Current); + LoadAsserts(rule, rules.Current); + LoadReports(rule, rules.Current); + pattern.Rules.Add(rule); + } + } + + void LoadExtends(Rule rule, XPathNavigator context) + { + XPathNodeIterator extends = context.Select(CompiledExpressions.RuleExtends); + if (extends.Count == 0) return; + + while (extends.MoveNext()) + { + string ruleName = extends.Current.GetAttribute("rule", String.Empty); + if (_abstracts != null && _abstracts.ContainsKey(ruleName)) + rule.Extend((Rule)_abstracts[ruleName]); + else + throw new BadSchemaException("The abstract rule with id=\"" + ruleName + "\" is used but not defined."); + } + } + + void LoadAsserts(Rule rule, XPathNavigator context) + { + XPathNodeIterator asserts = context.Select(CompiledExpressions.Assert); + if (asserts.Count == 0) return; + + while (asserts.MoveNext()) + { + if (asserts.Current is IHasXmlNode node) + { + Assert asr = rule.CreateAssert(asserts.Current.GetAttribute("test", String.Empty), + node.GetNode().InnerXml); + asr.SetContext(_schema.NsManager); + rule.Asserts.Add(asr); } - } - - private void LoadAsserts(Rule rule, XPathNavigator context) - { - XPathNodeIterator asserts = context.Select(CompiledExpressions.Assert); - if (asserts.Count == 0) return; - - while (asserts.MoveNext()) - { - if (asserts.Current is IHasXmlNode) - { - Assert asr = rule.CreateAssert(asserts.Current.GetAttribute("test", String.Empty), - ((IHasXmlNode) asserts.Current).GetNode().InnerXml); - asr.SetContext(_schema.NsManager); - rule.Asserts.Add(asr); - } - else - { - Assert asr = rule.CreateAssert(asserts.Current.GetAttribute("test", String.Empty), - asserts.Current.Value); - asr.SetContext(_schema.NsManager); - rule.Asserts.Add(asr); - } - } - } - - private void LoadReports(Rule rule, XPathNavigator context) - { - XPathNodeIterator reports = context.Select(CompiledExpressions.Report); - if (reports.Count == 0) return; - - while (reports.MoveNext()) - { - if (reports.Current is IHasXmlNode) - { - Report rpt = rule.CreateReport(reports.Current.GetAttribute("test", String.Empty), - ((IHasXmlNode) reports.Current).GetNode().InnerXml); - rpt.SetContext(_schema.NsManager); - rule.Reports.Add(rpt); - } - else - { - Report rpt = rule.CreateReport(reports.Current.GetAttribute("test", String.Empty), - reports.Current.Value); - rpt.SetContext(_schema.NsManager); - rule.Reports.Add(rpt); - } - } - } - } + else + { + Assert asr = rule.CreateAssert(asserts.Current.GetAttribute("test", String.Empty), + asserts.Current.Value); + asr.SetContext(_schema.NsManager); + rule.Asserts.Add(asr); + } + } + } + + void LoadReports(Rule rule, XPathNavigator context) + { + XPathNodeIterator reports = context.Select(CompiledExpressions.Report); + if (reports.Count == 0) return; + + while (reports.MoveNext()) + { + if (reports.Current is IHasXmlNode node) + { + Report rpt = rule.CreateReport(reports.Current.GetAttribute("test", String.Empty), + node.GetNode().InnerXml); + rpt.SetContext(_schema.NsManager); + rule.Reports.Add(rpt); + } + else + { + Report rpt = rule.CreateReport(reports.Current.GetAttribute("test", String.Empty), + reports.Current.Value); + rpt.SetContext(_schema.NsManager); + rule.Reports.Add(rpt); + } + } + } } + diff --git a/src/Schematron/Schematron.csproj b/src/Schematron/Schematron.csproj index bbf5508..3bf411f 100644 --- a/src/Schematron/Schematron.csproj +++ b/src/Schematron/Schematron.csproj @@ -1,166 +1,15 @@ -ο»Ώ - - +ο»Ώ + - Debug - AnyCPU - {DB7369F1-69CF-4821-98E3-9D6422A7CC36} - Library - Properties - Schematron Schematron - v4.0 - ..\bin\$(Configuration)\ - $(OutputPath)Schematron.xml - true - full - prompt - VERSION - - - DEBUG;TRACE - - - TRACE - true + Schematron + netstandard2.0 + true + Schematron.NET + - - - - - - - Properties\GlobalAssemblyInfo.cs - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - XmlFormatter.xsd - - + - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - \ No newline at end of file + + diff --git a/src/Schematron/Serialization/SchematronValidationResultTempObjectModel.cs b/src/Schematron/Serialization/SchematronValidationResultTempObjectModel.cs index 09ccce1..e12e651 100644 --- a/src/Schematron/Serialization/SchematronValidationResultTempObjectModel.cs +++ b/src/Schematron/Serialization/SchematronValidationResultTempObjectModel.cs @@ -1,526 +1,157 @@ -ο»Ώusing System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +#nullable disable +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member -namespace Schematron.Serialization -{ - /// - /// - /// This code was generated by VS Paste Special Xml to Class. It therefore may feel like overkill and a poor model to support deserialization operations - /// - /// - public class SchematronValidationResultTempObjectModel - { - /// - [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://sourceforge.net/projects/dotnetopensrc/schematron")] - [System.Xml.Serialization.XmlRootAttribute(Namespace = "http://sourceforge.net/projects/dotnetopensrc/schematron", IsNullable = false)] - public partial class output - { - - private outputXml xmlField; - - private outputSchematron schematronField; - - /// - public outputXml xml - { - get - { - return this.xmlField; - } - set - { - this.xmlField = value; - } - } - - /// - public outputSchematron schematron - { - get - { - return this.schematronField; - } - set - { - this.schematronField = value; - } - } - } - - /// - [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://sourceforge.net/projects/dotnetopensrc/schematron")] - public partial class outputXml - { - - private outputXmlXmlSchema xmlSchemaField; - - private outputXmlMessage messageField; - - /// - public outputXmlXmlSchema xmlSchema - { - get - { - return this.xmlSchemaField; - } - set - { - this.xmlSchemaField = value; - } - } - - /// - public outputXmlMessage message - { - get - { - return this.messageField; - } - set - { - this.messageField = value; - } - } - } - - /// - [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://sourceforge.net/projects/dotnetopensrc/schematron")] - public partial class outputXmlXmlSchema - { - - private string idField; - - private string versionField; - - private string targetNamespaceField; - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string id - { - get - { - return this.idField; - } - set - { - this.idField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string version - { - get - { - return this.versionField; - } - set - { - this.versionField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string targetNamespace - { - get - { - return this.targetNamespaceField; - } - set - { - this.targetNamespaceField = value; - } - } - } - - /// - [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://sourceforge.net/projects/dotnetopensrc/schematron")] - public partial class outputXmlMessage - { - - private string textField; - - private outputXmlMessagePosition positionField; - - /// - public string text - { - get - { - return this.textField; - } - set - { - this.textField = value; - } - } - - /// - public outputXmlMessagePosition position - { - get - { - return this.positionField; - } - set - { - this.positionField = value; - } - } - } - - /// - [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://sourceforge.net/projects/dotnetopensrc/schematron")] - public partial class outputXmlMessagePosition - { - - private byte lineField; - - private byte columnField; - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public byte line - { - get - { - return this.lineField; - } - set - { - this.lineField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public byte column - { - get - { - return this.columnField; - } - set - { - this.columnField = value; - } - } - } - - /// - [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://sourceforge.net/projects/dotnetopensrc/schematron")] - public partial class outputSchematron - { - - private schema schemaField; - - /// - [System.Xml.Serialization.XmlElementAttribute(Namespace = "")] - public schema schema - { - get - { - return this.schemaField; - } - set - { - this.schemaField = value; - } - } - } - - /// - [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)] - [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)] - public partial class schema - { - - private schemaPhase phaseField; - - private string titleField; - - /// - public schemaPhase phase - { - get - { - return this.phaseField; - } - set - { - this.phaseField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string title - { - get - { - return this.titleField; - } - set - { - this.titleField = value; - } - } - } +using System.ComponentModel; +using System.Xml.Serialization; - /// - [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)] - public partial class schemaPhase - { +namespace Schematron.Serialization; - private schemaPhasePattern[] patternField; - - private string idField; - - /// - [System.Xml.Serialization.XmlElementAttribute("pattern")] - public schemaPhasePattern[] pattern - { - get - { - return this.patternField; - } - set - { - this.patternField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string id - { - get - { - return this.idField; - } - set - { - this.idField = value; - } - } - } - - /// - [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)] - public partial class schemaPhasePattern - { - - private schemaPhasePatternRule ruleField; - - private string nameField; - - private string idField; - - /// - public schemaPhasePatternRule rule - { - get - { - return this.ruleField; - } - set - { - this.ruleField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string name - { - get - { - return this.nameField; - } - set - { - this.nameField = value; - } - } - - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string id - { - get - { - return this.idField; - } - set - { - this.idField = value; - } - } - } - - /// - [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)] - public partial class schemaPhasePatternRule - { +/// +/// +/// This code was generated by VS Paste Special Xml to Class. It therefore may feel like overkill and a poor model to support deserialization operations +/// +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public class SchematronValidationResultTempObjectModel +{ + [XmlType(AnonymousType = true, Namespace = "http://sourceforge.net/projects/dotnetopensrc/schematron")] + [XmlRoot(ElementName = "output", Namespace = "http://sourceforge.net/projects/dotnetopensrc/schematron", IsNullable = false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public record Output + { + [XmlElement("xml")] + public OutputXml Xml { get; set; } + [XmlElement("schematron")] + public OutputSchematron Schematron { get; set; } + } - private schemaPhasePatternRuleMessage[] messageField; + [XmlType(AnonymousType = true, Namespace = "http://sourceforge.net/projects/dotnetopensrc/schematron")] + [EditorBrowsable(EditorBrowsableState.Never)] + public record OutputXml + { + [XmlElement("xmlSchema")] + public OutputXmlXmlSchema XmlSchema { get; set; } + [XmlElement("message")] + public OutputXmlMessage Message { get; set; } + } - private string contextField; + [XmlType(AnonymousType = true, Namespace = "http://sourceforge.net/projects/dotnetopensrc/schematron")] + [EditorBrowsable(EditorBrowsableState.Never)] + public record OutputXmlXmlSchema + { + [XmlAttribute("id")] + public string Id { get; set; } - private string idField; + [XmlAttribute("version")] + public string Version { get; set; } - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string id - { - get - { - return this.idField; - } - set - { - this.idField = value; - } - } + [XmlAttribute("targetNamespace")] + public string TargetNamespace { get; set; } + } - /// - [System.Xml.Serialization.XmlElementAttribute("message")] - public schemaPhasePatternRuleMessage[] message - { - get - { - return this.messageField; - } - set - { - this.messageField = value; - } - } + [XmlType(AnonymousType = true, Namespace = "http://sourceforge.net/projects/dotnetopensrc/schematron")] + [EditorBrowsable(EditorBrowsableState.Never)] + public record OutputXmlMessage + { + [XmlElement("text")] + public string Text { get; set; } + [XmlElement("position")] + public OutputXmlMessagePosition Position { get; set; } + } - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public string context - { - get - { - return this.contextField; - } - set - { - this.contextField = value; - } - } - } + [XmlType(AnonymousType = true, Namespace = "http://sourceforge.net/projects/dotnetopensrc/schematron")] + [EditorBrowsable(EditorBrowsableState.Never)] + public record OutputXmlMessagePosition + { + [XmlAttribute("line")] + public byte Line { get; set; } - /// - [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)] - public partial class schemaPhasePatternRuleMessage - { + [XmlAttribute("column")] + public byte Column { get; set; } + } - private string textField; + [XmlType(AnonymousType = true, Namespace = "http://sourceforge.net/projects/dotnetopensrc/schematron")] + [EditorBrowsable(EditorBrowsableState.Never)] + public record OutputSchematron + { + [XmlElement("schema", Namespace = "")] + public Schema Schema { get; set; } + } - private string pathField; + [XmlType(AnonymousType = true)] + [XmlRoot(Namespace = "", IsNullable = false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public record Schema + { + [XmlElement("phase")] + public SchemaPhase Phase { get; set; } - private object summaryField; + [XmlAttribute("title")] + public string Title { get; set; } + } - private schemaPhasePatternRuleMessagePosition positionField; + [XmlType(AnonymousType = true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public record SchemaPhase + { + [XmlElement("pattern")] + public SchemaPhasePattern[] Pattern { get; set; } - /// - public string text - { - get - { - return this.textField; - } - set - { - this.textField = value; - } - } + [XmlAttribute("id")] + public string Id { get; set; } + } - /// - public string path - { - get - { - return this.pathField; - } - set - { - this.pathField = value; - } - } + [XmlType(AnonymousType = true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public record SchemaPhasePattern + { + [XmlElement("rule")] + public SchemaPhasePatternRule Rule { get; set; } - /// - public object summary - { - get - { - return this.summaryField; - } - set - { - this.summaryField = value; - } - } + [XmlAttribute("name")] + public string Name { get; set; } - /// - public schemaPhasePatternRuleMessagePosition position - { - get - { - return this.positionField; - } - set - { - this.positionField = value; - } - } - } + [XmlAttribute("id")] + public string Id { get; set; } + } - /// - [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)] - public partial class schemaPhasePatternRuleMessagePosition - { + [XmlType(AnonymousType = true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public record SchemaPhasePatternRule + { + [XmlAttribute("id")] + public string Id { get; set; } - private byte lineField; + [XmlElement("message")] + public SchemaPhasePatternRuleMessage[] Message { get; set; } - private byte columnField; + [XmlAttribute("context")] + public string Context { get; set; } + } - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public byte line - { - get - { - return this.lineField; - } - set - { - this.lineField = value; - } - } + [XmlType(AnonymousType = true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public record SchemaPhasePatternRuleMessage + { + [XmlElement("text")] + public string Text { get; set; } + [XmlElement("path")] + public string Path { get; set; } + [XmlElement("summary")] + public object Summary { get; set; } + [XmlElement("position")] + public SchemaPhasePatternRuleMessagePosition Position { get; set; } + } - /// - [System.Xml.Serialization.XmlAttributeAttribute()] - public byte column - { - get - { - return this.columnField; - } - set - { - this.columnField = value; - } - } - } + [XmlType(AnonymousType = true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public record SchemaPhasePatternRuleMessagePosition + { + [XmlAttribute("line")] + public byte Line { get; set; } + [XmlAttribute("column")] + public byte Column { get; set; } } } + diff --git a/src/Schematron/StartDelegate.cs b/src/Schematron/StartDelegate.cs deleted file mode 100644 index 56e4474..0000000 --- a/src/Schematron/StartDelegate.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System; - -namespace Schematron -{ - /// - public delegate void StartDelegate(); -} diff --git a/src/Schematron/SyncEvaluationContext.cs b/src/Schematron/SyncEvaluationContext.cs index d2d140f..44e47a9 100644 --- a/src/Schematron/SyncEvaluationContext.cs +++ b/src/Schematron/SyncEvaluationContext.cs @@ -1,278 +1,268 @@ -using System; -using System.IO; +using System.Collections; using System.Text; -using System.Xml; -using System.Xml.Schema; using System.Xml.XPath; -using System.Collections; -using Schematron.Formatters; -namespace Schematron +namespace Schematron; + +/// +/// Evaluates all the schema elements synchronously. +/// +/// +/// +/// +/// See for a description of the purpose and +/// of evaluation contexts, and where are they used. +/// +class SyncEvaluationContext : EvaluationContextBase { - /// - /// Evaluates all the schema elements synchronously. - /// - /// - /// - /// - /// See for a description of the purpose and - /// of evaluation contexts, and where are they used. - /// - internal class SyncEvaluationContext : EvaluationContextBase - { - /// Creates the evaluation context - public SyncEvaluationContext() - { - } + /// Creates the evaluation context + public SyncEvaluationContext() + { + } - /// - /// Starts the evaluation process. - /// - /// - /// When the process is finished, the results are placed - /// in the property. - /// - public override void Start() - { - this.Reset(); + /// + /// Starts the evaluation process. + /// + /// + /// When the process is finished, the results are placed + /// in the property. + /// + public override void Start() + { + this.Reset(); - // Is there something to evaluate at all? - if (Schema.Patterns.Count == 0) return; - - // If no phase was received, try the default phase defined for the schema. - // If no default phase is defined, all patterns will be tested. - if (Phase == String.Empty) - { - if (Schema.DefaultPhase == String.Empty) - { - Phase = Schematron.Phase.All; - } - else - { - Phase = Schema.DefaultPhase; - } - } + // Is there something to evaluate at all? + if (Schema.Patterns.Count == 0) return; - if (Schema.Phases[Phase] == null) - throw new ArgumentException("The specified Phase isn't defined for the current schema."); + // If no phase was received, try the default phase defined for the schema. + // If no default phase is defined, all patterns will be tested. + if (Phase == string.Empty) + { + Phase = !string.IsNullOrEmpty(Schema.DefaultPhase) + ? Schema.DefaultPhase + : Schematron.Phase.All; + } - if (Evaluate(Schema.Phases[Phase], Messages)) - { - Formatter.Format(Schema, Source, Messages); - HasErrors = true; - } - } + if (Schema.Phases[Phase] == null) + throw new ArgumentException("The specified Phase isn't defined for the current schema."); - /// - /// Evaluates the selected . - /// - /// - /// As most of the other evaluation methods, it repositions the - /// navigator on the root node. - /// - /// The to evaluate. - /// Contains the builder to accumulate messages in. - /// A boolean indicating the presence of errors (true). - private bool Evaluate(Phase phase, StringBuilder output) - { - bool failed = false; - Source.MoveToRoot(); - StringBuilder sb = new StringBuilder(); + if (Evaluate(Schema.Phases[Phase], Messages)) + { + Formatter.Format(Schema, Source, Messages); + HasErrors = true; + } + } - foreach (Pattern pt in phase.Patterns) - { - if (Evaluate(pt, sb)) failed = true; - } + /// + /// Evaluates the selected . + /// + /// + /// As most of the other evaluation methods, it repositions the + /// navigator on the root node. + /// + /// The to evaluate. + /// Contains the builder to accumulate messages in. + /// A boolean indicating the presence of errors (true). + bool Evaluate(Phase phase, StringBuilder output) + { + bool failed = false; + Source.MoveToRoot(); + var sb = new StringBuilder(); - if (failed) - { - Formatter.Format(phase, Source, sb); - output.Append(sb.ToString()); - } + foreach (Pattern pt in phase.Patterns) + { + if (Evaluate(pt, sb)) failed = true; + } - return failed; - } + if (failed) + { + Formatter.Format(phase, Source, sb); + output.Append(sb.ToString()); + } - /// - /// Evaluates the selected . - /// - /// - /// As most of the other evaluation methods, it repositions the - /// navigator on the root node. - /// - /// Clears the object before - /// proceeding, as the restriction about node mathing (see ) - /// applies only inside a single pattern. - /// - /// - /// The to evaluate. - /// Contains the builder to accumulate messages in. - /// A boolean indicating if a new message was added. - private bool Evaluate(Pattern pattern, StringBuilder output) - { - bool failed = false; - Source.MoveToRoot(); - StringBuilder sb = new StringBuilder(); + return failed; + } - // Reset matched nodes, as across patters, nodes can be - // evaluated more than once. - Matched.Clear(); + /// + /// Evaluates the selected . + /// + /// + /// As most of the other evaluation methods, it repositions the + /// navigator on the root node. + /// + /// Clears the object before + /// proceeding, as the restriction about node mathing (see ) + /// applies only inside a single pattern. + /// + /// + /// The to evaluate. + /// Contains the builder to accumulate messages in. + /// A boolean indicating if a new message was added. + bool Evaluate(Pattern pattern, StringBuilder output) + { + bool failed = false; + Source.MoveToRoot(); + var sb = new StringBuilder(); - foreach (Rule rule in pattern.Rules) - { - if (Evaluate(rule, sb)) failed = true; - } + // Reset matched nodes, as across patters, nodes can be + // evaluated more than once. + Matched.Clear(); - if (failed) - { - Formatter.Format(pattern, Source, sb); - output.Append(sb.ToString()); - } + foreach (Rule rule in pattern.Rules) + { + if (Evaluate(rule, sb)) failed = true; + } - return failed; - } + if (failed) + { + Formatter.Format(pattern, Source, sb); + output.Append(sb.ToString()); + } - /// - /// Evaluates the selected . - /// - /// - /// and are iterated - /// and each and is executed against - /// the context selected by the . - /// - /// Nodes matched are added to the list of - /// nodes to skip in the next rule, using the method. - /// This object is a strategy object which implements different algorithms for matching and - /// saving node references, as the actual implementation provides - /// different methods for accessing the underlying source. - /// - /// This makes the implementation both performant and compliant with - /// the restriction about node matching (see ) in the spec. - /// - /// - /// - /// - /// - /// - /// As most of the other evaluation methods, it repositions the - /// navigator on the root node. - /// - /// - /// The to evaluate. - /// Contains the builder to accumulate messages in. - /// A boolean indicating if a new message was added. - /// - /// The rule to evaluate is abstract (see ). - /// - private bool Evaluate(Rule rule, StringBuilder output) - { - if (rule.IsAbstract) - throw new InvalidOperationException("The Rule is abstract, so it can't be evaluated."); + return failed; + } - bool failed = false; - StringBuilder sb = new StringBuilder(); - Source.MoveToRoot(); - XPathNodeIterator nodes = Source.Select(rule.CompiledExpression); - ArrayList evaluables = new ArrayList(nodes.Count); + /// + /// Evaluates the selected . + /// + /// + /// and are iterated + /// and each and is executed against + /// the context selected by the . + /// + /// Nodes matched are added to the list of + /// nodes to skip in the next rule, using the method. + /// This object is a strategy object which implements different algorithms for matching and + /// saving node references, as the actual implementation provides + /// different methods for accessing the underlying source. + /// + /// This makes the implementation both performant and compliant with + /// the restriction about node matching (see ) in the spec. + /// + /// + /// + /// + /// + /// + /// As most of the other evaluation methods, it repositions the + /// navigator on the root node. + /// + /// + /// The to evaluate. + /// Contains the builder to accumulate messages in. + /// A boolean indicating if a new message was added. + /// + /// The rule to evaluate is abstract (see ). + /// + bool Evaluate(Rule rule, StringBuilder output) + { + if (rule.IsAbstract) + throw new InvalidOperationException("The Rule is abstract, so it can't be evaluated."); - // The navigator doesn't contain line info - while (nodes.MoveNext()) - { - if (!Matched.IsMatched(nodes.Current)) - { - // Add the navigator to the list to be evaluated and to - // the list of pattern-level nodes matched so far. - XPathNavigator curr = nodes.Current.Clone(); - evaluables.Add(curr); - Matched.AddMatched(curr); - } - } + bool failed = false; + var sb = new StringBuilder(); + Source.MoveToRoot(); + XPathNodeIterator nodes = Source.Select(rule.CompiledExpression); + var evaluables = new ArrayList(nodes.Count); - foreach (Assert asr in rule.Asserts) - { - foreach (XPathNavigator node in evaluables) - { - if (EvaluateAssert(asr, node.Clone(), sb)) failed = true; - } - } + // The navigator doesn't contain line info + while (nodes.MoveNext()) + { + if (!Matched.IsMatched(nodes.Current)) + { + // Add the navigator to the list to be evaluated and to + // the list of pattern-level nodes matched so far. + XPathNavigator curr = nodes.Current.Clone(); + evaluables.Add(curr); + Matched.AddMatched(curr); + } + } - foreach (Report rpt in rule.Reports) - { - foreach (XPathNavigator node in evaluables) - { - if (EvaluateReport(rpt, node.Clone(), sb)) failed = true; - } - } + foreach (Assert asr in rule.Asserts) + { + foreach (XPathNavigator node in evaluables) + { + if (EvaluateAssert(asr, node.Clone(), sb)) failed = true; + } + } - if (failed) - { - Formatter.Format(rule, Source, sb); - output.Append(sb.ToString()); - } + foreach (Report rpt in rule.Reports) + { + foreach (XPathNavigator node in evaluables) + { + if (EvaluateReport(rpt, node.Clone(), sb)) failed = true; + } + } - return failed; - } + if (failed) + { + Formatter.Format(rule, Source, sb); + output.Append(sb.ToString()); + } - /// - /// Performs the evaluation of the . - /// - /// - /// This is where the actual assert expression is evaluated. If - /// the returns false, - /// a formated message is generated from the property. - /// - /// The to evaluate. - /// The context node for the execution. - /// Contains the builder to accumulate messages in. - /// A boolean indicating if a new message was added. - private bool EvaluateAssert(Assert assert, XPathNavigator context, StringBuilder output) - { - object eval = context.Evaluate(assert.CompiledExpression); - bool result = true; + return failed; + } - if (assert.ReturnType == XPathResultType.Boolean) - { - result = (bool)eval; - } - else if (assert.ReturnType == XPathResultType.NodeSet && - ((XPathNodeIterator)eval).Count == 0) - { - result = false; - } - - if (!result) Formatter.Format(assert, context, output); - return !result; - } + /// + /// Performs the evaluation of the . + /// + /// + /// This is where the actual assert expression is evaluated. If + /// the returns false, + /// a formated message is generated from the property. + /// + /// The to evaluate. + /// The context node for the execution. + /// Contains the builder to accumulate messages in. + /// A boolean indicating if a new message was added. + bool EvaluateAssert(Assert assert, XPathNavigator context, StringBuilder output) + { + object eval = context.Evaluate(assert.CompiledExpression); + bool result = true; - /// - /// Performs the evaluation of the . - /// - /// - /// This is where the actual report expression is evaluated. If - /// the returns true, - /// a formated message is generated from the property. - /// - /// The to evaluate. - /// The context node for the execution. - /// Contains the builder to accumulate messages in. - /// A boolean indicating if a new message was added. - private bool EvaluateReport(Report report, XPathNavigator context, StringBuilder output) - { - object eval = context.Evaluate(report.CompiledExpression); - bool result = false; + if (assert.ReturnType == XPathResultType.Boolean) + { + result = (bool)eval; + } + else if (assert.ReturnType == XPathResultType.NodeSet && + ((XPathNodeIterator)eval).Count == 0) + { + result = false; + } - if (report.ReturnType == XPathResultType.Boolean) - { - result = (bool)eval; - } - else if (report.ReturnType == XPathResultType.NodeSet && - ((XPathNodeIterator)eval).Count != 0) - { - result = true; - } + if (!result) Formatter.Format(assert, context, output); + return !result; + } - if (result) Formatter.Format(report, context, output); - return result; - } - } + /// + /// Performs the evaluation of the . + /// + /// + /// This is where the actual report expression is evaluated. If + /// the returns true, + /// a formated message is generated from the property. + /// + /// The to evaluate. + /// The context node for the execution. + /// Contains the builder to accumulate messages in. + /// A boolean indicating if a new message was added. + bool EvaluateReport(Report report, XPathNavigator context, StringBuilder output) + { + object eval = context.Evaluate(report.CompiledExpression); + bool result = false; + + if (report.ReturnType == XPathResultType.Boolean) + { + result = (bool)eval; + } + else if (report.ReturnType == XPathResultType.NodeSet && + ((XPathNodeIterator)eval).Count != 0) + { + result = true; + } + + if (result) Formatter.Format(report, context, output); + return result; + } } + diff --git a/src/Schematron/TagExpressions.cs b/src/Schematron/TagExpressions.cs index 6757ef5..1e7cc6e 100644 --- a/src/Schematron/TagExpressions.cs +++ b/src/Schematron/TagExpressions.cs @@ -1,43 +1,42 @@ -using System; using System.Text.RegularExpressions; -namespace Schematron +namespace Schematron; + +/// +class TagExpressions { - /// - internal class TagExpressions - { - /// - /// The compiled regular expression to replace the special name and value tags inside a message. - /// - /// - /// Replaces each instance of name and valuetags with the value in the current context element. - /// - public static Regex NameValueOf; + /// + /// The compiled regular expression to replace the special name and value tags inside a message. + /// + /// + /// Replaces each instance of name and valuetags with the value in the current context element. + /// + public static Regex NameValueOf; - public static Regex Emph; - public static Regex Dir; - public static Regex Span; - public static Regex Para; - public static Regex Any; - public static Regex AllSchematron; + public static Regex Emph; + public static Regex Dir; + public static Regex Span; + public static Regex Para; + public static Regex Any; + public static Regex AllSchematron; - /// - static TagExpressions() - { - // The element declarations can contain the namespace if expanded in a loaded document. - NameValueOf = new Regex(@"<[^\s>]*\b(name|value-of)\b[^>]*/>", RegexOptions.Compiled); - Emph = new Regex(@"<[^\s>]*\bemph\b[^>]*>", RegexOptions.Compiled); - Dir = new Regex(@"<[^\s]*\bdir\b[^>]*>", RegexOptions.Compiled); - Span = new Regex(@"<[^\s]*\bspan\b[^>]*>", RegexOptions.Compiled); - Para = new Regex(@"<[^\s]*\bp\b[^>]*>", RegexOptions.Compiled); - Any = new Regex(@"<[^\s]*[^>]*>", RegexOptions.Compiled); - // Closing elements don't have an expanded xmlns so they will be matched too. - // TODO: improve this to avoid removing non-schematron closing elements. - AllSchematron = new Regex(@"<.*\bxmlns\b[^\s]*" + Schema.Namespace + "[^>]*>|]*>", RegexOptions.Compiled); - } + /// + static TagExpressions() + { + // The element declarations can contain the namespace if expanded in a loaded document. + NameValueOf = new Regex(@"<[^\s>]*\b(name|value-of)\b[^>]*/>", RegexOptions.Compiled); + Emph = new Regex(@"<[^\s>]*\bemph\b[^>]*>", RegexOptions.Compiled); + Dir = new Regex(@"<[^\s]*\bdir\b[^>]*>", RegexOptions.Compiled); + Span = new Regex(@"<[^\s]*\bspan\b[^>]*>", RegexOptions.Compiled); + Para = new Regex(@"<[^\s]*\bp\b[^>]*>", RegexOptions.Compiled); + Any = new Regex(@"<[^\s]*[^>]*>", RegexOptions.Compiled); + // Closing elements don't have an expanded xmlns so they will be matched too. + // TODO: improve this to avoid removing non-schematron closing elements. + AllSchematron = new Regex(@"<.*\bxmlns\b[^\s]*" + Schema.Namespace + "[^>]*>|]*>", RegexOptions.Compiled); + } - private TagExpressions() - { - } - } + TagExpressions() + { + } } + diff --git a/src/Schematron/Test.cs b/src/Schematron/Test.cs index 36c38e4..a833323 100644 --- a/src/Schematron/Test.cs +++ b/src/Schematron/Test.cs @@ -1,109 +1,106 @@ -using System; -using System.Xml; -using System.Xml.XPath; using System.Text.RegularExpressions; -using System.Collections; +using System.Xml.XPath; -namespace Schematron +namespace Schematron; + +/// +/// Base class for testing units of Schematron, such as Assert or Report elements. +/// +public abstract class Test : EvaluableExpression { - /// - /// Base class for testing units of Schematron, such as Assert or Report elements. - /// - public abstract class Test : EvaluableExpression - { - /// - protected string _msg; + /// + protected string _msg = string.Empty; - /// - protected MatchCollection _name_valueofs; + /// + protected MatchCollection _name_valueofs; - /// - protected XPathExpression[] _paths; + /// + protected XPathExpression?[] _paths; - /// - protected XPathExpression[] _selects; + /// + protected XPathExpression?[] _selects; - /// - /// - /// - public Test(string test, string message) : base(test) - { - if (ReturnType != XPathResultType.Boolean && - ReturnType != XPathResultType.NodeSet) - throw new InvalidExpressionException("Test expression doesn't evaluate to a boolean or nodeset result: " + test); + /// + /// + /// + public Test(string test, string message) : base(test) + { + if (ReturnType != XPathResultType.Boolean && + ReturnType != XPathResultType.NodeSet) + throw new InvalidExpressionException("Test expression doesn't evaluate to a boolean or nodeset result: " + test); - Message = Formatters.FormattingUtils.NormalizeString(message); + Message = Formatters.FormattingUtils.NormalizeString(message); - // Save and tags in the message and their paths / selects in their compiled form. - // TODO: see if we can work with the XML in the message, instead of using RE. - // TODO: Check the correct usahe of path and select attributes. - _name_valueofs = TagExpressions.NameValueOf.Matches(Message); - int nc = _name_valueofs.Count; - _paths = new XPathExpression[nc]; - _selects = new XPathExpression[nc]; + // Save and tags in the message and their paths / selects in their compiled form. + // TODO: see if we can work with the XML in the message, instead of using RE. + // TODO: Check the correct usahe of path and select attributes. + _name_valueofs = TagExpressions.NameValueOf.Matches(Message); + int nc = _name_valueofs.Count; + _paths = new XPathExpression[nc]; + _selects = new XPathExpression[nc]; - for (int i = 0; i < nc; i++) + for (int i = 0; i < nc; i++) + { + // Locate the path attribute and compile it with the DefaultNavigator. + Match name_valueof = _name_valueofs[i]; + int start = name_valueof.Value.IndexOf("path="); + // Does it have a path attribute? + if (start > 0) { - // Locate the path attribute and compile it with the DefaultNavigator. - Match name_valueof = _name_valueofs[i]; - int start = name_valueof.Value.IndexOf("path="); - // Does it have a path attribute? - if (start > 0) - { - // Skip the path=" string. - start += 6; - // If the namespace element is present, end the expression there. - int end = name_valueof.Value.LastIndexOf("xmlns") - 2; - if (end < 0) - end = name_valueof.Value.LastIndexOf('"'); - string xpath = name_valueof.Value.Substring(start, end - start); - _paths[i] = Config.DefaultNavigator.Compile(xpath); - _selects[i] = null; + // Skip the path=" string. + start += 6; + // If the namespace element is present, end the expression there. + int end = name_valueof.Value.LastIndexOf("xmlns") - 2; + if (end < 0) + end = name_valueof.Value.LastIndexOf('"'); + string xpath = name_valueof.Value.Substring(start, end - start); + _paths[i] = Config.DefaultNavigator.Compile(xpath); + _selects[i] = null; - } - else if ((start = name_valueof.Value.IndexOf("select=")) > 0) - { - // Skip the select=" string. - start += 8; - // If the namespace element is present, end the expression there. - int end = name_valueof.Value.LastIndexOf("xmlns") - 2; - if (end < 0) - end = name_valueof.Value.LastIndexOf('"'); - string xpath = name_valueof.Value.Substring(start, end - start); - _selects[i] = Config.DefaultNavigator.Compile(xpath); - _paths[i] = null; - } - else - { - _paths[i] = null; - _selects[i] = null; - } + } + else if ((start = name_valueof.Value.IndexOf("select=")) > 0) + { + // Skip the select=" string. + start += 8; + // If the namespace element is present, end the expression there. + int end = name_valueof.Value.LastIndexOf("xmlns") - 2; + if (end < 0) + end = name_valueof.Value.LastIndexOf('"'); + string xpath = name_valueof.Value.Substring(start, end - start); + _selects[i] = Config.DefaultNavigator.Compile(xpath); + _paths[i] = null; + } + else + { + _paths[i] = null; + _selects[i] = null; } } + } - /// - public string Message - { - get { return _msg; } - set { _msg = value; } - } + /// + public string Message + { + get { return _msg; } + set { _msg = value; } + } - /// - public MatchCollection NameValueOfExpressions - { - get { return _name_valueofs; } - } + /// + public MatchCollection NameValueOfExpressions + { + get { return _name_valueofs; } + } - /// - public XPathExpression[] NamePaths - { - get { return _paths; } - } + /// + public XPathExpression?[] NamePaths + { + get { return _paths; } + } - /// - public XPathExpression[] ValueOfSelects - { - get { return _selects; } - } + /// + public XPathExpression?[] ValueOfSelects + { + get { return _selects; } } } + diff --git a/src/Schematron/TestCollection.cs b/src/Schematron/TestCollection.cs index 6fdf97b..83e9fe5 100644 --- a/src/Schematron/TestCollection.cs +++ b/src/Schematron/TestCollection.cs @@ -1,77 +1,76 @@ -using System; using System.Collections; -namespace Schematron +namespace Schematron; + +/// A collection of Test elements. +/// +/// +public class TestCollection : CollectionBase { - /// A collection of Test elements. - /// - /// - public class TestCollection : CollectionBase - { - /// - public TestCollection() - { - } + /// + public TestCollection() + { + } - /// Returns the Test element at the specified position. - public Test this[int index] - { - get { return (Test)InnerList[index]; } - set { InnerList[index] = value; } - } + /// Returns the Test element at the specified position. + public Test this[int index] + { + get { return (Test)InnerList[index]; } + set { InnerList[index] = value; } + } - /// - public int Add(Test value) - { - return InnerList.Add(value); - } + /// + public int Add(Test value) + { + return InnerList.Add(value); + } - /// - public void AddRange(Test[] values) - { - foreach (Test elem in values) - Add(elem); - } + /// + public void AddRange(Test[] values) + { + foreach (Test elem in values) + Add(elem); + } - /// - public void AddRange(TestCollection values) - { - foreach (Test elem in values) - Add(elem); - } + /// + public void AddRange(TestCollection values) + { + foreach (Test elem in values) + Add(elem); + } - /// - public bool Contains(Test value) - { - return InnerList.Contains(value); - } + /// + public bool Contains(Test value) + { + return InnerList.Contains(value); + } - /// - public void CopyTo(Test[] array, int index) - { - InnerList.CopyTo(array, index); - } + /// + public void CopyTo(Test[] array, int index) + { + InnerList.CopyTo(array, index); + } - /// - public int IndexOf(Test value) - { - return InnerList.IndexOf(value); - } + /// + public int IndexOf(Test value) + { + return InnerList.IndexOf(value); + } - /// - public void Insert(int index, Test value) - { - InnerList.Insert(index, value); - } + /// + public void Insert(int index, Test value) + { + InnerList.Insert(index, value); + } - /// - public void Remove(Test value) - { - int index = IndexOf(value); - if ( index < 0 ) - throw(new ArgumentException("The specified object is not found in the collection")); + /// + public void Remove(Test value) + { + int index = IndexOf(value); + if (index < 0) + throw (new ArgumentException("The specified object is not found in the collection")); - RemoveAt(index); - } - } + RemoveAt(index); + } } + diff --git a/src/Schematron/ValidationException.cs b/src/Schematron/ValidationException.cs index f322301..6a38bdb 100644 --- a/src/Schematron/ValidationException.cs +++ b/src/Schematron/ValidationException.cs @@ -1,49 +1,48 @@ -using System; using System.Runtime.Serialization; -namespace Schematron +namespace Schematron; + +/// +/// Represents the an error in the Schematron schema. +/// +/// +/// +[Serializable()] +public class ValidationException : ApplicationException { - /// - /// Represents the an error in the Schematron schema. - /// - /// - /// - [Serializable()] - public class ValidationException : ApplicationException - { - /// - /// Initializes a new instance of the class. - /// - public ValidationException() - { - } + /// + /// Initializes a new instance of the class. + /// + public ValidationException() + { + } - /// - /// Initializes a new instance of the class. - /// - /// The error message that explains the reason for the exception. - public ValidationException(string message) : base(message) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The error message that explains the reason for the exception. + public ValidationException(string message) : base(message) + { + } - /// - /// Initializes a new instance of the class. - /// - /// Info - /// Context - protected ValidationException(SerializationInfo info, StreamingContext context) : - base(info, context) - { - } + /// + /// Initializes a new instance of the class. + /// + /// Info + /// Context + protected ValidationException(SerializationInfo info, StreamingContext context) : + base(info, context) + { + } - /// - /// Initializes a new instance of the class. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception. - public ValidationException(string message, Exception innerException) : - base(message, innerException) - { - } - } + /// + /// Initializes a new instance of the class. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. + public ValidationException(string message, Exception innerException) : + base(message, innerException) + { + } } + diff --git a/src/Schematron/Validator.cs b/src/Schematron/Validator.cs index a376bdd..801899e 100644 --- a/src/Schematron/Validator.cs +++ b/src/Schematron/Validator.cs @@ -1,622 +1,563 @@ -using System; -using System.IO; using System.Text; using System.Xml; using System.Xml.Schema; using System.Xml.XPath; -using System.Collections; -using System.Threading; -using System.Runtime.Remoting.Messaging; using Schematron.Formatters; -namespace Schematron +namespace Schematron; + +/// +/// Performs validation of Schematron elements and schemas. +/// +/// +/// Can handle either standalone or embedded schematron schemas. If the schematron +/// is embedded in an XML Schema, the input document is validated against both at +/// the same time. +/// +public class Validator { - /// - /// Performs validation of Schematron elements and schemas. - /// - /// - /// Can handle either standalone or embedded schematron schemas. If the schematron - /// is embedded in an XML Schema, the input document is validated against both at - /// the same time. - /// - public class Validator - { - #region Fields & Ctors - - XmlSchemaSet _xmlschemas = new XmlSchemaSet(); - SchemaCollection _schematrons = new SchemaCollection(); - EvaluationContextBase _evaluationctx; - NavigableType _navtype = NavigableType.XPathDocument; - - StringBuilder _errors; - bool _haserrors; - - /// - /// Initializes a new instance of the class. - /// - public Validator() - { - _evaluationctx = CreateContext(); - _evaluationctx.Formatter = Config.DefaultFormatter; - } - - /// - /// Initializes a new instance of the class, using the specified output format for error messages. - /// - /// Output format of error messages. - public Validator(OutputFormatting format) : this() - { - InitValidator(format, NavigableType.Default); - } - - /// - /// Initializes a new instance of the class, using the specified return type. - /// - /// The type to use for validation and return type. - public Validator(NavigableType type) : this() - { - InitValidator(OutputFormatting.Default, type); - } - - /// - /// Initializes a new instance of the class, using the specified options. - /// - /// Output format of error messages. - /// The type to use for validation and return type. - public Validator(OutputFormatting format, NavigableType type) : this() - { - InitValidator(format, type); - } - - #endregion Fields & Ctors - - /// - /// Initializes the validator with the options received from the constructor overloads. - /// - /// Output format of error messages. - /// The type to use for validation and return type. - private void InitValidator(OutputFormatting format, NavigableType type) - { - if (!Enum.IsDefined(typeof(OutputFormatting), format)) - throw new ArgumentException("Invalid type.", "type"); - - switch (format) - { - case OutputFormatting.Boolean: - _evaluationctx.Formatter = new BooleanFormatter(); - break; - case OutputFormatting.Log: - case OutputFormatting.Default: - _evaluationctx.Formatter = new LogFormatter(); - break; - case OutputFormatting.Simple: - _evaluationctx.Formatter = new SimpleFormatter(); - break; - case OutputFormatting.XML: - _evaluationctx.Formatter = new XmlFormatter(); - break; - } - - if (!Enum.IsDefined(typeof(NavigableType), type)) - throw new ArgumentException("Invalid type.", "type"); - - // If type is Default, set it to XPathDocument. - _navtype = (type != NavigableType.Default) ? type : NavigableType.XPathDocument; - } - - #region Overridable Factory Methods - /// Creates the evaluation context to use. - /// - /// Inheritors can override this method should they want to - /// use a different strategy for node traversal and evaluation - /// against the source file. - /// - protected virtual EvaluationContextBase CreateContext() - { - return new SyncEvaluationContext(); - } - #endregion - - #region Properties - /// - public EvaluationContextBase Context - { - get { return _evaluationctx; } - set { _evaluationctx = value; } - } - - /// - public IFormatter Formatter - { - get { return _evaluationctx.Formatter; } - set { _evaluationctx.Formatter = value; } - } - - /// - public NavigableType ReturnType - { - get { return _navtype; } - set - { - if (!Enum.IsDefined(typeof(NavigableType), value)) - throw new ArgumentException("NavigableType value is not defined."); - _navtype = value; - } - } - - /// - public string Phase - { - get { return _evaluationctx.Phase; } - set { _evaluationctx.Phase = value; } - } - - /// - /// Exposes the schematron schemas to use for validation. - /// - public SchemaCollection Schemas - { - get { return _schematrons; } - } - - /// - /// Exposes the XML schemas to use for validation. - /// - public XmlSchemaSet XmlSchemas - { - get { return _xmlschemas; } - } - - #endregion - - #region AddSchema overloads - /// - /// Adds an XML Schema to the collection to use for validation. - /// - public void AddSchema(XmlSchema schema) - { - _xmlschemas.Add(schema); - } - - /// - /// Adds a Schematron schema to the collection to use for validation. - /// - public void AddSchema(Schema schema) - { - _schematrons.Add(schema); - } - - /// - /// Adds a set of XML Schemas to the collection to use for validation. - /// - public void AddSchemas(XmlSchemaSet schemas) - { - _xmlschemas.Add(schemas); - } - - /// - /// Adds a set of Schematron schemas to the collection to use for validation. - /// - public void AddSchemas(SchemaCollection schemas) - { - _schematrons.AddRange(schemas); - } - - /// - /// Adds a schema to the collection to use for validation from the specified URL. - /// - public void AddSchema(string uri) - { - using (FileStream fs = new FileStream(uri, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - AddSchema(new XmlTextReader(fs)); - } - } - - /// - /// Adds a schema to the collection to use for validation. - /// - public void AddSchema(TextReader reader) - { - AddSchema(new XmlTextReader(reader)); - } - - /// - /// Adds a schema to the collection to use for validation. - /// - public void AddSchema(Stream input) - { - AddSchema(new XmlTextReader(input)); - } - - /// - /// Adds a schema to the collection to use for validation. - /// - /// Processing takes place here. - public void AddSchema(XmlReader reader) - { - if (reader.MoveToContent() == XmlNodeType.None) throw new BadSchemaException("No information found to read"); - - // Determine type of schema received. - bool standalone = (reader.NamespaceURI == Schema.Namespace); - bool wxs = (reader.NamespaceURI == XmlSchema.Namespace); - - // The whole schema must be read first to preserve the state for later. - string state = reader.ReadOuterXml(); - StringReader r = new StringReader(state); - - if (wxs) - { - _haserrors = false; - _errors = new StringBuilder(); - - var xs = XmlSchema.Read(new XmlTextReader(r, reader.NameTable), new ValidationEventHandler(OnValidation)); - - var set = new XmlSchemaSet(); - set.Add(xs); - - if(!set.IsCompiled) - { - set.Compile(); - } - - if (_haserrors) throw new BadSchemaException(_errors.ToString()); - - _xmlschemas.Add(xs); - } - - //Schemas wouldn't be too big, so they are loaded in an XmlDocument for Schematron validation, so that - //inner XML elements in messages, etc. are available. So we commented the following lines. - //r = new StringReader(state); - //XPathNavigator nav = new XPathDocument(new XmlTextReader(r, reader.NameTable)).CreateNavigator(); - XmlDocument doc = new XmlDocument(reader.NameTable); - doc.LoadXml(state); - XPathNavigator nav = doc.CreateNavigator(); - _evaluationctx.Source = nav; - - if (standalone) - PerformValidation(Config.FullSchematron); - else - PerformValidation(Config.EmbeddedSchematron); - - if (_evaluationctx.HasErrors) - throw new BadSchemaException(_evaluationctx.Messages.ToString()); - - Schema sch = new Schema(); - sch.Load(nav); - _schematrons.Add(sch); - _errors = null; - } - - - #region WORK IN PROGRESS :: The need the for the signature AddSchema(string targetNamespace, string schemaUri) comes from resolving imported (schemaLocation hinted) partial schemas - - private bool TryAddXmlSchema( - string targetNamespace, - string schemaUri, - XmlSchemaSet schemaSet, - Action validationHandler, - out string xmlContent, - out XmlNameTable nameTable, - out string namespaceUri) + #region Fields & Ctors + + XmlSchemaSet _xmlschemas = new XmlSchemaSet(); + SchemaCollection _schematrons = new SchemaCollection(); + EvaluationContextBase _evaluationctx; + NavigableType _navtype = NavigableType.XPathDocument; + + StringBuilder? _errors; + bool _haserrors; + + /// + /// Initializes a new instance of the class. + /// + public Validator() + { + _evaluationctx = CreateContext(); + _evaluationctx.Formatter = Config.DefaultFormatter; + } + + /// + /// Initializes a new instance of the class, using the specified output format for error messages. + /// + /// Output format of error messages. + public Validator(OutputFormatting format) : this() + { + InitValidator(format, NavigableType.Default); + } + + /// + /// Initializes a new instance of the class, using the specified return type. + /// + /// The type to use for validation and return type. + public Validator(NavigableType type) : this() + { + InitValidator(OutputFormatting.Default, type); + } + + /// + /// Initializes a new instance of the class, using the specified options. + /// + /// Output format of error messages. + /// The type to use for validation and return type. + public Validator(OutputFormatting format, NavigableType type) : this() + { + InitValidator(format, type); + } + + #endregion Fields & Ctors + + /// + /// Initializes the validator with the options received from the constructor overloads. + /// + /// Output format of error messages. + /// The type to use for validation and return type. + void InitValidator(OutputFormatting format, NavigableType type) + { + if (!Enum.IsDefined(typeof(OutputFormatting), format)) + throw new ArgumentException("Invalid type.", "type"); + + switch (format) { - xmlContent = default(string); - nameTable = default(XmlNameTable); - namespaceUri = default(string); + case OutputFormatting.Boolean: + _evaluationctx.Formatter = new BooleanFormatter(); + break; + case OutputFormatting.Log: + case OutputFormatting.Default: + _evaluationctx.Formatter = new LogFormatter(); + break; + case OutputFormatting.Simple: + _evaluationctx.Formatter = new SimpleFormatter(); + break; + case OutputFormatting.XML: + _evaluationctx.Formatter = new XmlFormatter(); + break; + } + + if (!Enum.IsDefined(typeof(NavigableType), type)) + throw new ArgumentException("Invalid type.", "type"); + + // If type is Default, set it to XPathDocument. + _navtype = (type != NavigableType.Default) ? type : NavigableType.XPathDocument; + } + + #region Overridable Factory Methods + /// Creates the evaluation context to use. + /// + /// Inheritors can override this method should they want to + /// use a different strategy for node traversal and evaluation + /// against the source file. + /// + protected virtual EvaluationContextBase CreateContext() + { + return new SyncEvaluationContext(); + } + #endregion + + #region Properties + /// + public EvaluationContextBase Context + { + get { return _evaluationctx; } + set { _evaluationctx = value; } + } + + /// + public IFormatter Formatter + { + get { return _evaluationctx.Formatter; } + set { _evaluationctx.Formatter = value; } + } + + /// + public NavigableType ReturnType + { + get { return _navtype; } + set + { + if (!Enum.IsDefined(typeof(NavigableType), value)) + throw new ArgumentException("NavigableType value is not defined."); + _navtype = value; + } + } + + /// + public string Phase + { + get { return _evaluationctx.Phase; } + set { _evaluationctx.Phase = value; } + } + + /// + /// Exposes the schematron schemas to use for validation. + /// + public SchemaCollection Schemas + { + get { return _schematrons; } + } + + /// + /// Exposes the XML schemas to use for validation. + /// + public XmlSchemaSet XmlSchemas + { + get { return _xmlschemas; } + } + + #endregion + + #region AddSchema overloads + /// + /// Adds an XML Schema to the collection to use for validation. + /// + public void AddSchema(XmlSchema schema) + { + _xmlschemas.Add(schema); + } + + /// + /// Adds a Schematron schema to the collection to use for validation. + /// + public void AddSchema(Schema schema) + { + _schematrons.Add(schema); + } + + /// + /// Adds a set of XML Schemas to the collection to use for validation. + /// + public void AddSchemas(XmlSchemaSet schemas) + { + _xmlschemas.Add(schemas); + } + + /// + /// Adds a set of Schematron schemas to the collection to use for validation. + /// + public void AddSchemas(SchemaCollection schemas) + { + _schematrons.AddRange(schemas); + } + + /// + /// Adds a schema to the collection to use for validation from the specified URL. + /// + public void AddSchema(string uri) + { + using (var fs = new FileStream(uri, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + AddSchema(new XmlTextReader(fs)); + } + } + + /// + /// Adds a schema to the collection to use for validation. + /// + public void AddSchema(TextReader reader) + { + AddSchema(new XmlTextReader(reader)); + } + + /// + /// Adds a schema to the collection to use for validation. + /// + public void AddSchema(Stream input) + { + AddSchema(new XmlTextReader(input)); + } + + /// + /// Adds a schema to the collection to use for validation. + /// + /// Processing takes place here. + public void AddSchema(XmlReader reader) + { + if (reader.MoveToContent() == XmlNodeType.None) throw new BadSchemaException("No information found to read"); + + // Determine type of schema received. + bool standalone = (reader.NamespaceURI == Schema.Namespace); + bool wxs = (reader.NamespaceURI == XmlSchema.Namespace); + + // The whole schema must be read first to preserve the state for later. + string state = reader.ReadOuterXml(); + var r = new StringReader(state); + + if (wxs) + { + _haserrors = false; + _errors = new StringBuilder(); + + var xs = XmlSchema.Read(new XmlTextReader(r, reader.NameTable), new ValidationEventHandler(OnValidation)); + + var set = new XmlSchemaSet(); + set.Add(xs); - using (var reader = XmlReader.Create(schemaUri)) + if (!set.IsCompiled) { - if (reader.MoveToContent() == XmlNodeType.None) - { - throw new BadSchemaException("No information found to read"); - } + set.Compile(); + } - nameTable = reader.NameTable; - namespaceUri = reader.NamespaceURI; - xmlContent = reader.ReadOuterXml(); + if (_haserrors) throw new BadSchemaException(_errors.ToString()); - if (!IsStandardSchema(namespaceUri)) - return false; + _xmlschemas.Add(xs); + } - using (var stringReader = new StringReader(xmlContent)) - { - if (_errors == null) - { - _errors = new StringBuilder(); - } + //Schemas wouldn't be too big, so they are loaded in an XmlDocument for Schematron validation, so that + //inner XML elements in messages, etc. are available. So we commented the following lines. + //r = new StringReader(state); + //XPathNavigator nav = new XPathDocument(new XmlTextReader(r, reader.NameTable)).CreateNavigator(); + var doc = new XmlDocument(reader.NameTable); + doc.LoadXml(state); + XPathNavigator nav = doc.CreateNavigator(); + _evaluationctx.Source = nav; + + if (standalone) + PerformValidation(Config.FullSchematron); + else + PerformValidation(Config.EmbeddedSchematron); + + if (_evaluationctx.HasErrors) + throw new BadSchemaException(_evaluationctx.Messages.ToString()); + + var sch = new Schema(); + sch.Load(nav); + _schematrons.Add(sch); + _errors = null; + } + + + #region WORK IN PROGRESS :: The need the for the signature AddSchema(string targetNamespace, string schemaUri) comes from resolving imported (schemaLocation hinted) partial schemas + + bool TryAddXmlSchema( + string targetNamespace, + string schemaUri, + XmlSchemaSet schemaSet, + Action validationHandler, + out string? xmlContent, + out XmlNameTable? nameTable, + out string? namespaceUri) + { + xmlContent = null; + nameTable = null; + namespaceUri = null; + + using (var reader = XmlReader.Create(schemaUri)) + { + if (reader.MoveToContent() == XmlNodeType.None) + { + throw new BadSchemaException("No information found to read"); + } - var xs = XmlSchema.Read(new XmlTextReader(stringReader, nameTable), new ValidationEventHandler(validationHandler)); + nameTable = reader.NameTable; + namespaceUri = reader.NamespaceURI; + xmlContent = reader.ReadOuterXml(); - var set = new XmlSchemaSet(); - set.Add(targetNamespace, schemaUri); + if (!IsStandardSchema(namespaceUri)) + return false; - if (!set.IsCompiled) - { - set.Compile(); - } + if (_errors == null) + _errors = new StringBuilder(); - if (_haserrors) throw new BadSchemaException(_errors.ToString()); + var set = new XmlSchemaSet(); + set.XmlResolver = new XmlUrlResolver(); + set.Add(targetNamespace, new Uri(Path.GetFullPath(schemaUri)).AbsoluteUri); - schemaSet.Add(xs); - } - } + if (!set.IsCompiled) + set.Compile(); - return true; + if (_haserrors) throw new BadSchemaException(_errors.ToString()); + + foreach (XmlSchema s in set.Schemas()) + schemaSet.Add(s); } - private bool IsStandaloneSchematron(string namespaceUri) + return true; + } + + bool IsStandaloneSchematron(string? namespaceUri) => namespaceUri == Schema.Namespace; + + bool IsStandardSchema(string? namespaceUri) => namespaceUri == XmlSchema.Namespace; + + /// + /// Adds a schema to the collection to use for validation. + /// + /// Validation takes place here. + public void AddSchema(string targetNamespace, string schemaUri) + { + TryAddXmlSchema(targetNamespace, schemaUri, _xmlschemas, OnValidation, out var xmlContent, out var nameTable, out var namespaceUri); + + var doc = new XmlDocument(nameTable!); + doc.LoadXml(xmlContent!); + + XPathNavigator nav = doc.CreateNavigator(); + _evaluationctx.Source = nav; + + if (IsStandaloneSchematron(namespaceUri)) + PerformValidation(Config.FullSchematron); + else + PerformValidation(Config.EmbeddedSchematron); + + if (_evaluationctx.HasErrors) + throw new BadSchemaException(_evaluationctx.Messages.ToString()); + + var sch = new Schema(); + sch.Load(nav); + + _schematrons.Add(sch); + _errors = null; + } + + #endregion + + + #endregion + + #region Validation Methods + /// + /// Performs Schematron-only validation. + /// + /// + /// Even when implements IXPathNavigable, WXS + /// validation can't be performed once it has been loaded becasue a + /// validating reader has to be used. + /// + /// + /// The document is invalid with respect to the loaded schemas. + /// + public void ValidateSchematron(IXPathNavigable source) + { + ValidateSchematron(source.CreateNavigator()); + } + + /// + /// Performs Schematron-only validation. + /// + /// + /// The document is invalid with respect to the loaded schemas. + /// + public void ValidateSchematron(XPathNavigator file) + { + _errors = new StringBuilder(); + _evaluationctx.Source = file; + + foreach (Schema sch in _schematrons) { - return namespaceUri == Schema.Namespace; + PerformValidation(sch); + if (_evaluationctx.HasErrors) + { + _haserrors = true; + _errors.Append(_evaluationctx.Messages.ToString()); + } } - private bool IsStandardSchema(string namespaceUri) + if (_haserrors) { - return namespaceUri == XmlSchema.Namespace; + _evaluationctx.Formatter.Format(_errors); + throw new ValidationException(_errors.ToString()); } - /// - /// Adds a schema to the collection to use for validation. - /// - /// Validation takes place here. - public void AddSchema(string targetNamespace, string schemaUri) - { - var xmlContent = default(string); - var nameTable = default(XmlNameTable); - var namespaceUri = default(string); - _xmlschemas = _xmlschemas ?? new XmlSchemaSet(); + if (_haserrors) throw new ValidationException(_errors.ToString()); + } + + /// Performs validation of the document at the specified URI. + /// The document location. + /// + /// The document is invalid with respect to the loaded schemas. + /// + /// The loaded instance. + public IXPathNavigable Validate(string uri) + { + using (var fs = new FileStream(uri, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + return Validate(new XmlTextReader(fs)); + } + } + + /// Performs validation of the document using the specified reader. + /// The reader pointing to the document to validate. + /// The loaded instance. + /// + /// The document is invalid with respect to the loaded schemas. + /// + /// The loaded instance. + public IXPathNavigable Validate(TextReader reader) + { + return Validate(new XmlTextReader(reader)); + } + + /// Performs validation of the document using the specified stream. + /// The stream with the document to validate. + /// + /// The document is invalid with respect to the loaded schemas. + /// + /// The loaded instance. + public IXPathNavigable Validate(Stream input) + { + return Validate(new XmlTextReader(input)); + } + + /// Performs validation of the document using the received reader. + /// Where the actual work takes place + /// The reader pointing to the document to validate. + /// + /// The document is invalid with respect to the loaded schemas. + /// + /// The loaded instance. + public IXPathNavigable Validate(XmlReader reader) + { + _errors = new StringBuilder(); + + bool hasxml = false; + StringBuilder? xmlerrors = null; + bool hassch = false; + StringBuilder? scherrors = null; + + var settings = new XmlReaderSettings + { + ValidationType = ValidationType.Schema, + }; + settings.ValidationEventHandler += OnValidation; - TryAddXmlSchema(targetNamespace, schemaUri, _xmlschemas, OnValidation, out xmlContent, out nameTable, out namespaceUri); + foreach (XmlSchema xsd in _xmlschemas.Schemas()) + settings.Schemas.Add(xsd); - XmlDocument doc = new XmlDocument(nameTable); - doc.LoadXml(xmlContent); + var r = XmlReader.Create(reader, settings); - XPathNavigator nav = doc.CreateNavigator(); - _evaluationctx.Source = nav; + IXPathNavigable navdoc; + XPathNavigator nav; - if (IsStandaloneSchematron(namespaceUri)) - PerformValidation(Config.FullSchematron); + try + { + if (_navtype == NavigableType.XmlDocument) + { + navdoc = new XmlDocument(r.NameTable); + ((XmlDocument)navdoc).Load(r); + } else - PerformValidation(Config.EmbeddedSchematron); + { + navdoc = new XPathDocument(r); + } + } + finally + { + reader.Close(); + } + + nav = navdoc.CreateNavigator(); + + if (_haserrors) + { + _evaluationctx.Formatter.Format(r.Settings.Schemas, _errors); + _evaluationctx.Formatter.Format(r, _errors); + hasxml = true; + xmlerrors = _errors; + } + _evaluationctx.Source = nav; + + // Reset shared variables + _haserrors = false; + _errors = new StringBuilder(); + + foreach (Schema sch in _schematrons) + { + PerformValidation(sch); if (_evaluationctx.HasErrors) - throw new BadSchemaException(_evaluationctx.Messages.ToString()); + { + _haserrors = true; + _errors.Append(_evaluationctx.Messages.ToString()); + } + } - Schema sch = new Schema(); - sch.Load(nav); + if (_haserrors) + { + _evaluationctx.Formatter.Format(_schematrons, _errors); + hassch = true; + scherrors = _errors; + } + + _errors = new StringBuilder(); + if (hasxml) _errors.Append(xmlerrors!.ToString()); + if (hassch) _errors.Append(scherrors!.ToString()); - _schematrons.Add(sch); - _errors = null; + if (hasxml || hassch) + { + _evaluationctx.Formatter.Format(_errors); + throw new ValidationException(_errors.ToString()); } - #endregion - - - #endregion - - #region Validation Methods - /// - /// Performs Schematron-only validation. - /// - /// - /// Even when implements IXPathNavigable, WXS - /// validation can't be performed once it has been loaded becasue a - /// validating reader has to be used. - /// - /// - /// The document is invalid with respect to the loaded schemas. - /// - public void ValidateSchematron(IXPathNavigable source) - { - ValidateSchematron(source.CreateNavigator()); - } - - /// - /// Performs Schematron-only validation. - /// - /// - /// The document is invalid with respect to the loaded schemas. - /// - public void ValidateSchematron(XPathNavigator file) - { - _errors = new StringBuilder(); - _evaluationctx.Source = file; - - foreach (Schema sch in _schematrons) - { - PerformValidation(sch); - if (_evaluationctx.HasErrors) - { - _haserrors = true; - _errors.Append(_evaluationctx.Messages.ToString()); - } - } - - if (_haserrors) - { - _evaluationctx.Formatter.Format(_errors); - throw new ValidationException(_errors.ToString()); - } - - if (_haserrors) throw new ValidationException(_errors.ToString()); - } - - /// Performs validation of the document at the specified URI. - /// The document location. - /// - /// The document is invalid with respect to the loaded schemas. - /// - /// The loaded instance. - public IXPathNavigable Validate(string uri) - { - using (FileStream fs = new FileStream(uri, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - return Validate(new XmlTextReader(fs)); - } - } - - /// Performs validation of the document using the specified reader. - /// The reader pointing to the document to validate. - /// The loaded instance. - /// - /// The document is invalid with respect to the loaded schemas. - /// - /// The loaded instance. - public IXPathNavigable Validate(TextReader reader) - { - return Validate(new XmlTextReader(reader)); - } - - /// Performs validation of the document using the specified stream. - /// The stream with the document to validate. - /// - /// The document is invalid with respect to the loaded schemas. - /// - /// The loaded instance. - public IXPathNavigable Validate(Stream input) - { - return Validate(new XmlTextReader(input)); - } - - /// Performs validation of the document using the received reader. - /// Where the actual work takes place - /// The reader pointing to the document to validate. - /// - /// The document is invalid with respect to the loaded schemas. - /// - /// The loaded instance. - public IXPathNavigable Validate(XmlReader reader) - { - try - { - _errors = new StringBuilder(); - - // Temporary variables to hold error flags and messages. - bool hasxml = false; - StringBuilder xmlerrors = null; - bool hassch = false; - StringBuilder scherrors = null; - - var settings = new XmlReaderSettings() - { - ValidationType = ValidationType.Schema, - }; - settings.ValidationEventHandler += new ValidationEventHandler(OnValidation); - - foreach (XmlSchema xsd in _xmlschemas.Schemas()) - { - settings.Schemas.Add(xsd); - } - - var r = XmlReader.Create(reader, settings); - - - IXPathNavigable navdoc; - XPathNavigator nav; - - if (_navtype == NavigableType.XmlDocument) - { - navdoc = new XmlDocument(r.NameTable); - ((XmlDocument)navdoc).Load(r); - } - else - { - navdoc = new XPathDocument(r); - } - - nav = navdoc.CreateNavigator(); - - if (_haserrors) - { - _evaluationctx.Formatter.Format(r.Settings.Schemas, _errors); - _evaluationctx.Formatter.Format(r, _errors); - hasxml = true; - xmlerrors = _errors; - } - - _evaluationctx.Source = nav; - - // Reset shared variables - _haserrors = false; - _errors = new StringBuilder(); - - foreach (Schema sch in _schematrons) - { - PerformValidation(sch); - if (_evaluationctx.HasErrors) - { - _haserrors = true; - _errors.Append(_evaluationctx.Messages.ToString()); - } - } - - if (_haserrors) - { - _evaluationctx.Formatter.Format(_schematrons, _errors); - hassch = true; - scherrors = _errors; - } - - _errors = new StringBuilder(); - if (hasxml) _errors.Append(xmlerrors.ToString()); - if (hassch) _errors.Append(scherrors.ToString()); - - if (hasxml || hassch) - { - _evaluationctx.Formatter.Format(_errors); - throw new ValidationException(_errors.ToString()); - } - - return navdoc; - } - catch - { - // Rethrow. - throw; - } - finally - { - reader.Close(); - } - } - #endregion - - private void PerformValidation(Schema schema) - { - try - { - _evaluationctx.Schema = schema; - //_evaluationctx.Start(); - StartDelegate st = new StartDelegate(_evaluationctx.Start); - WaitHandle wh = st.BeginInvoke(new AsyncCallback(EndValidation), - _evaluationctx).AsyncWaitHandle; - wh.WaitOne(); - } - catch (Exception ex) - { - System.Diagnostics.Debug.Fail(ex.Message, ex.ToString()); - throw ex; - } - } - - private void EndValidation(IAsyncResult result) - { - try - { - AsyncResult ar = (AsyncResult) result; - StartDelegate st = (StartDelegate) ar.AsyncDelegate; - st.EndInvoke(ar); - } - catch (Exception ex) - { - System.Diagnostics.Debug.Fail(ex.Message, ex.ToString()); - throw ex; - } - } - - private void OnValidation(object sender, ValidationEventArgs e) - { - if (!_haserrors) _haserrors = true; - _evaluationctx.Formatter.Format(e, _errors); - } - } + return navdoc; + } + #endregion + + void PerformValidation(Schema schema) + { + _evaluationctx.Schema = schema; + _evaluationctx.Start(); + } + + void OnValidation(object sender, ValidationEventArgs e) + { + _haserrors = true; + _evaluationctx.Formatter.Format(e, _errors!); + } } + diff --git a/src/Schematron/XPathMatchedNodes.cs b/src/Schematron/XPathMatchedNodes.cs index 468296b..5996a3d 100644 --- a/src/Schematron/XPathMatchedNodes.cs +++ b/src/Schematron/XPathMatchedNodes.cs @@ -1,73 +1,49 @@ -using System; using System.Xml; using System.Xml.XPath; -using System.Collections; -namespace Schematron +namespace Schematron; + +/// +/// Strategy class for matching and keeping references to nodes in an . +/// +/// +/// +/// +/// When an is created from an , +/// it implements the interface, which is used to gain +/// access to the underlying node position. +/// +class XPathMatchedNodes : IMatchedNodes { - /// - /// Strategy class for matching and keeping references to nodes in an . - /// - /// - /// - /// - /// When an is created from an , - /// it implements the interface, which is used to gain - /// access to the underlying node position. - /// - class XPathMatchedNodes : IMatchedNodes - { - /// - /// The table contains an item for each line, and the item value - /// is an instance of our class for - /// optimized value types storage. - /// - Hashtable _matched = new Hashtable(); + // The dictionary maps each line number to a list of column positions. + Dictionary> _matched = new Dictionary>(); - /// Initializes an instance of the class. - public XPathMatchedNodes() - { - } + /// See . + public bool IsMatched(XPathNavigator node) + { + var info = (IXmlLineInfo)node; - /// See . - public bool IsMatched(System.Xml.XPath.XPathNavigator node) - { - IXmlLineInfo info = (IXmlLineInfo)node; + if (!_matched.TryGetValue(info.LineNumber, out List pos)) + return false; - if (!_matched.ContainsKey(info.LineNumber)) - return false; + return pos.Contains(info.LinePosition); + } - Int32Collection pos = (Int32Collection) _matched[info.LineNumber]; - - if (!pos.Contains(info.LinePosition)) - return false; + /// See . + public void AddMatched(XPathNavigator node) + { + var info = (IXmlLineInfo)node; - return true; - } + if (!_matched.TryGetValue(info.LineNumber, out var pos)) + { + pos = []; + _matched.Add(info.LineNumber, pos); + } - /// See . - public void AddMatched(System.Xml.XPath.XPathNavigator node) - { - IXmlLineInfo info = (IXmlLineInfo)node; - Int32Collection pos; + pos.Add(info.LinePosition); + } - if (!_matched.ContainsKey(info.LineNumber)) - { - pos = new Int32Collection(); - _matched.Add(info.LineNumber, pos); - } - else - { - pos = (Int32Collection)_matched[info.LineNumber]; - } - - pos.Add(info.LinePosition); - } - - /// See . - public void Clear() - { - _matched.Clear(); - } - } + /// See . + public void Clear() => _matched.Clear(); } + diff --git a/src/Schematron/packages.config b/src/Schematron/packages.config deleted file mode 100644 index bd6f14c..0000000 --- a/src/Schematron/packages.config +++ /dev/null @@ -1,4 +0,0 @@ -ο»Ώ - - - \ No newline at end of file diff --git a/src/nuget.config b/src/nuget.config new file mode 100644 index 0000000..1c18c96 --- /dev/null +++ b/src/nuget.config @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + +