| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.uima.fit.validation; |
| |
| import static java.util.Arrays.stream; |
| import static java.util.ServiceLoader.load; |
| import static java.util.stream.StreamSupport.stream; |
| |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.LinkedHashSet; |
| import java.util.Set; |
| import java.util.regex.Pattern; |
| |
| import org.apache.uima.cas.CAS; |
| import org.apache.uima.cas.CASException; |
| import org.apache.uima.jcas.JCas; |
| |
| /** |
| * Validate a (J)CAS. |
| */ |
| public class Validator { |
| private Collection<ValidationCheck> checks; |
| |
| public Validator(Collection<ValidationCheck> checks) { |
| this.checks = checks; |
| } |
| |
| public ValidationSummary check(JCas aJCas) throws ValidationException { |
| ValidationSummary summary = new ValidationSummary(); |
| |
| for (ValidationCheck check : checks) { |
| try { |
| if (check instanceof CasValidationCheck) { |
| summary.addAll(((CasValidationCheck) check).validate(aJCas.getCas())); |
| } else if (check instanceof JCasValidationCheck) { |
| summary.addAll(((JCasValidationCheck) check).validate(aJCas)); |
| } else { |
| throw new IllegalArgumentException( |
| "Unknown ValidationCheck type: [" + check.getClass().getName() + "]"); |
| } |
| } catch (ValidationCheckSkippedException e) { |
| summary.add(ValidationResult.info(check, "Skipped: %s", e.getMessage())); |
| } catch (ValidationCheckException e) { |
| summary.add(ValidationResult.error(check, "%s", e.getMessage())); |
| } |
| } |
| |
| return summary; |
| } |
| |
| public ValidationSummary check(CAS cas) throws ValidationException { |
| ValidationSummary summary = new ValidationSummary(); |
| |
| for (ValidationCheck check : checks) { |
| try { |
| if (check instanceof CasValidationCheck) { |
| summary.addAll(((CasValidationCheck) check).validate(cas)); |
| } else if (check instanceof JCasValidationCheck) { |
| try { |
| summary.addAll(((JCasValidationCheck) check).validate(cas.getJCas())); |
| } catch (CASException e) { |
| throw new ValidationException(e); |
| } |
| } else { |
| throw new IllegalArgumentException( |
| "Unknown ValidationCheck type: [" + check.getClass().getName() + "]"); |
| } |
| } catch (ValidationCheckSkippedException e) { |
| summary.add(ValidationResult.info(check, "Skipped: %s", e.getMessage())); |
| } catch (ValidationCheckException e) { |
| summary.add(ValidationResult.error(check, "%s", e.getMessage())); |
| } |
| } |
| |
| return summary; |
| } |
| |
| public Collection<ValidationCheck> getChecks() { |
| return checks; |
| } |
| |
| public static class Builder { |
| |
| private final Set<ValidationCheck> checks = new LinkedHashSet<>(); |
| private final Set<Pattern> includePatterns = new HashSet<>(); |
| private final Set<Class<?>> includeTypes = new HashSet<>(); |
| private final Set<Pattern> excludePatterns = new HashSet<>(); |
| private final Set<Class<?>> excludeTypes = new HashSet<>(); |
| private boolean skipAutoDetection = false; |
| |
| /** |
| * Add the given check instance to the validator. This allows even adding checks which are not |
| * available via the Java Service Locator, which take parameters or which are otherwise stateful |
| * (assuming that the resulting validator is not shared between threads). |
| * <p> |
| * <b>Note:</b> Includes/excludes do also apply do checks added via this method. |
| * |
| * @param check |
| * a check instance to use. |
| */ |
| public Validator.Builder withCheck(CasValidationCheck check) { |
| checks.add(check); |
| return this; |
| } |
| |
| /** |
| * Disable auto-detection of checks. |
| */ |
| public Validator.Builder withoutAutoDetectedChecks() { |
| skipAutoDetection = true; |
| return this; |
| } |
| |
| /** |
| * Enable auto-detection of checks (the default behavior). |
| */ |
| public Validator.Builder withAutoDetectedChecks() { |
| skipAutoDetection = false; |
| return this; |
| } |
| |
| /** |
| * Skip any checks with the given names. Subtypes of the given classes are not skipped. |
| * <p> |
| * <b>Note:</b> Excludes are applied after includes. |
| * |
| * @param className |
| * names of check classes to be excluded. |
| */ |
| public Validator.Builder excludingByName(String... className) { |
| stream(className) |
| .map(Pattern::quote) |
| .map(Pattern::compile) |
| .forEach(excludePatterns::add); |
| return this; |
| } |
| |
| /** |
| * Skip any checks with names matching the given regular expressions. |
| * <p> |
| * <b>Note:</b> Excludes are applied after includes. |
| * |
| * @param patterns |
| * regular expressions matching check class names to be excluded. |
| */ |
| public Validator.Builder excludingByPattern(String... patterns) { |
| stream(patterns) |
| .map(Pattern::compile) |
| .forEach(excludePatterns::add); |
| return this; |
| } |
| |
| /** |
| * Skips any checks of the given types (includes checks that are subclasses or implementations |
| * of the given types). |
| * <p> |
| * <b>Note:</b> Excludes are applied after includes. |
| * |
| * @param types |
| * check type names to be excluded. |
| */ |
| public Validator.Builder excludingByType(Class<?>... types) { |
| stream(types).forEach(excludeTypes::add); |
| return this; |
| } |
| |
| /** |
| * Retain only checks with the given names. Subtypes of the given classes are not retained. |
| * <p> |
| * <b>Note:</b> Excludes are applied after includes. |
| * |
| * @param className |
| * names of check classes to be included. |
| */ |
| public Validator.Builder includingByName(String... className) { |
| stream(className) |
| .map(Pattern::quote) |
| .map(Pattern::compile) |
| .forEach(includePatterns::add); |
| return this; |
| } |
| |
| /** |
| * Retain any checks with names matching the given regular expressions. |
| * <p> |
| * <b>Note:</b> Excludes are applied after includes. |
| * |
| * @param patterns |
| * regular expressions matching check class names to be included. |
| */ |
| public Validator.Builder includingByPattern(String... patterns) { |
| stream(patterns) |
| .map(Pattern::compile) |
| .forEach(includePatterns::add); |
| return this; |
| } |
| |
| /** |
| * Retain any checks of the given types (includes checks that are subclasses or implementations |
| * of the given types). |
| * <p> |
| * <b>Note:</b> Excludes are applied after includes. |
| * |
| * @param types |
| * check type names to be included. |
| */ |
| public Validator.Builder includingByType(Class<?>... types) { |
| stream(types).forEach(includeTypes::add); |
| return this; |
| } |
| |
| private Validator.Builder autoDetectChecks() { |
| stream(load(ValidationCheck.class).spliterator(), false) |
| .forEachOrdered(checks::add); |
| return this; |
| } |
| |
| public Validator build() { |
| if (!skipAutoDetection) { |
| autoDetectChecks(); |
| } |
| |
| if (!includePatterns.isEmpty()) { |
| checks.removeIf(check -> includePatterns.stream() |
| .noneMatch(p -> p.matcher(check.getClass().getName()).matches())); |
| } |
| |
| if (!includeTypes.isEmpty()) { |
| checks.removeIf(check -> includeTypes.stream() |
| .noneMatch(t -> t.isAssignableFrom(check.getClass()))); |
| } |
| |
| if (!excludePatterns.isEmpty()) { |
| checks.removeIf(check -> excludePatterns.stream() |
| .anyMatch(p -> p.matcher(check.getClass().getName()).matches())); |
| } |
| |
| if (!excludeTypes.isEmpty()) { |
| checks.removeIf(check -> excludeTypes.stream() |
| .anyMatch(t -> t.isAssignableFrom(check.getClass()))); |
| } |
| |
| return new Validator(checks); |
| } |
| } |
| } |