content: package org.aksw.rdfunit.model.impl.shacl;
  
  import com.google.common.collect.ImmutableSet;
  import java.util.Collection;
  import java.util.List;
  import java.util.Set;
  import java.util.stream.Collectors;
  import lombok.EqualsAndHashCode;
  import lombok.NonNull;
  import org.aksw.rdfunit.enums.RLOGLevel;
  import org.aksw.rdfunit.enums.TestAppliesTo;
  import org.aksw.rdfunit.enums.TestGenerationType;
  import org.aksw.rdfunit.model.impl.results.ShaclTestCaseGroupResult;
  import org.aksw.rdfunit.model.interfaces.GenericTestCase;
  import org.aksw.rdfunit.model.interfaces.TestCaseAnnotation;
  import org.aksw.rdfunit.model.interfaces.TestCaseGroup;
  import org.aksw.rdfunit.model.interfaces.results.TestCaseResult;
  import org.aksw.rdfunit.model.interfaces.shacl.PrefixDeclaration;
  import org.aksw.rdfunit.model.interfaces.shacl.ShapeTarget;
  import org.aksw.rdfunit.model.interfaces.shacl.TargetBasedTestCase;
  import org.aksw.rdfunit.utils.JenaUtils;
  import org.aksw.rdfunit.vocabulary.SHACL;
  import org.apache.jena.rdf.model.RDFNode;
  import org.apache.jena.rdf.model.Resource;
  import org.apache.jena.rdf.model.ResourceFactory;
  
  /**
   * Implements the logical constraint sh:and
   */
  @EqualsAndHashCode(exclude = {"resource"})
  public class TestCaseGroupAnd implements TestCaseGroup {
  
    private final ShapeTarget target;
    private final Resource resource;
    private final ImmutableSet<TargetBasedTestCase> testCases;
    private final TestCaseAnnotation testCaseAnnotation;
  
    public TestCaseGroupAnd(@NonNull Set<? extends TargetBasedTestCase> testCases) {
      assert (!testCases.isEmpty());
      target = testCases.iterator().next().getTarget();
      assert (testCases.stream().map(TargetBasedTestCase::getTarget).noneMatch(x -> x != target));
  
      this.resource = ResourceFactory.createProperty(JenaUtils.getUniqueIri());
      this.testCases = ImmutableSet.copyOf(testCases);
  
      List<TestCaseAnnotation> innerAnnotations = testCases.stream()
          .map(GenericTestCase::getTestCaseAnnotation).collect(Collectors.toList());
      String description = "A logical AND constraint component containing the following rules:";
      if (innerAnnotations.size() == 1) {
        description = "";
      }
      description += innerAnnotations.stream().map(TestCaseAnnotation::getDescription)
          .collect(Collectors.joining(", "));
      this.testCaseAnnotation = new TestCaseAnnotation(
          this.resource,
          innerAnnotations.stream().map(TestCaseAnnotation::getGenerated).findFirst()
              .orElse(TestGenerationType.AutoGenerated),
          null,
          innerAnnotations.stream().map(TestCaseAnnotation::getAppliesTo).findFirst()
              .orElse(TestAppliesTo.Dataset),
          innerAnnotations.stream().map(TestCaseAnnotation::getSourceUri).findFirst()
              .orElse(SHACL.namespace),
          innerAnnotations.stream().flatMap(t -> t.getReferences().stream())
              .collect(Collectors.toSet()),
          description,
          RLOGLevel.ERROR, // TODO probably needs to provided from shape
          ImmutableSet.of()   //TODO do I have to add annotations by default?
      );
    }
  
    public boolean isAtomic() {
      return testCases.size() == 1;
    }
  
    @Override
    public Set<TargetBasedTestCase> getTestCases() {
      return this.testCases;
    }
  
    @Override
    public SHACL.LogicalConstraint getLogicalOperator() {
      return SHACL.LogicalConstraint.and;
    }
  
    @Override
    public Collection<TestCaseResult> evaluateInternalResults(
        Collection<TestCaseResult> internalResults) {
      ImmutableSet.Builder<TestCaseResult> res = ImmutableSet.builder();
      TestCaseGroup.groupInternalResults(internalResults).forEach((focusNode, valueMap) -> {
        valueMap.forEach((value, results) -> {
          res.addAll(results);
          addSummaryResult(res, focusNode, results);
          //else we ignore all internal errors, since at least one was successful
        });
      });
      return res.build();
    }
  
    void addSummaryResult(ImmutableSet.Builder<TestCaseResult> builder, RDFNode focusNode,
        List<TestCaseResult> results) {
      builder.add(new ShaclTestCaseGroupResult(
          this.resource,
          this.getLogLevel(),
          "At least one test case failed inside a sh:and constraint.",
          focusNode,
          results
      ));
    }
  
    @Override
    public TestCaseAnnotation getTestCaseAnnotation() {
      return testCaseAnnotation;
    }
  
    @Override
    public Collection<PrefixDeclaration> getPrefixDeclarations() {
      return testCases.stream().flatMap(t -> t.getPrefixDeclarations().stream())
          .collect(Collectors.toSet());
    }
  
    @Override
    public Resource getElement() {
      return this.resource;
    }
  
    @Override
    public ShapeTarget getTarget() {
      return target;
    }
  }
  