title: Why SHACL API's SHShape.hasTargetNode Returns False For Targeted Class?
body: <p>In the course of trying to selectively invoke shapes/rules when classes are asserted, I'm working with the following example shapes definition (in TTL):</p>
  
  <pre><code># baseURI: http://example.org/familyShapes
  # imports: http://datashapes.org/dash
  # prefix: familyShapes
  
  @prefix dash: &lt;http://datashapes.org/dash#&gt; .
  @prefix familyShapes: &lt;http://example.org/familyShapes#&gt; .
  @prefix owl: &lt;http://www.w3.org/2002/07/owl#&gt; .
  @prefix rdf: &lt;http://www.w3.org/1999/02/22-rdf-syntax-ns#&gt; .
  @prefix rdfs: &lt;http://www.w3.org/2000/01/rdf-schema#&gt; .
  @prefix sh: &lt;http://www.w3.org/ns/shacl#&gt; .
  @prefix xsd: &lt;http://www.w3.org/2001/XMLSchema#&gt; .
  
  &lt;http://example.org/familyShapes&gt;
    rdf:type owl:Ontology ;
    owl:imports &lt;http://datashapes.org/dash&gt; ;
  .
  familyShapes:FemaleShape
    rdf:type sh:NodeShape ;
    rdfs:label "Female shape" ;
    sh:property [
        sh:path familyShapes:gender ;
        sh:hasValue familyShapes:female ;
      ] ;
  .
  familyShapes:Gender
    rdf:type rdfs:Class ;
    rdfs:label "Gender" ;
    rdfs:subClassOf rdfs:Resource ;
  .
  familyShapes:GrandpaRuleShape
    rdf:type sh:NodeShape ;
    rdfs:label "Grandpa rule shape" ;
    sh:rule [
        rdf:type sh:SPARQLRule ;
        rdfs:comment "Shape to infer grandpa/grandchild relationship" ;
        rdfs:label "Infer grandpas and grandchildren" ;
        sh:construct """
          PREFIX familyShapes: &lt;http://example.org/familyShapes#&gt;
  
          CONSTRUCT {
            ?child familyShapes:grandPa $this .
            $this familyShapes:grandChild ?child .
          }
          WHERE {
          {
             ?child familyShapes:mother ?mom .
             ?mom familyShapes:father $this .
          }
          UNION 
          {
             ?child familyShapes:father ?dad .
             ?dad familyShapes:father $this .
          }
        }
     """ ;
        sh:order 10 ;
      ] ;
    sh:targetClass familyShapes:Person ;
  .
  familyShapes:MaleShape
    rdf:type sh:NodeShape ;
    rdfs:label "Male shape" ;
    sh:property [
        sh:path familyShapes:gender ;
        sh:hasValue familyShapes:male ;
      ] ;
  .
  familyShapes:Person
    rdf:type rdfs:Class ;
    rdf:type sh:NodeShape ;
    rdfs:label "Person" ;
    rdfs:subClassOf rdfs:Resource ;
    sh:property [
        rdf:type sh:PropertyShape ;
        sh:path familyShapes:father ;
        sh:class familyShapes:Person ;
        sh:description "A Person's father." ;
        sh:maxCount 1 ;
        sh:name "father" ;
        sh:node familyShapes:MaleShape ;
        sh:nodeKind sh:IRI ;
        sh:sparql [
            sh:message "A person cannot be a father to that same person." ;
            sh:select """PREFIX familyShapes: &lt;http://example.org/familyShapes#&gt;
  
  SELECT $this
  WHERE {
      $this familyShapes:father $this .
  }""" ;
          ] ;
      ] ;
    sh:property [
        rdf:type sh:PropertyShape ;
        sh:path familyShapes:firstName ;
        sh:datatype xsd:string ;
        sh:description "A Person's first name (aka given name)." ;
        sh:minCount 1 ;
        sh:name "first name" ;
      ] ;
    sh:property [
        rdf:type sh:PropertyShape ;
        sh:path familyShapes:gender ;
        sh:class familyShapes:Gender ;
        sh:description "A Person's gender." ;
        sh:maxCount 1 ;
        sh:minCount 1 ;
        sh:name "gender" ;
      ] ;
    sh:property [
        rdf:type sh:PropertyShape ;
        sh:path familyShapes:lastName ;
        sh:datatype xsd:string ;
        sh:description "A Person's last name (aka family name)." ;
        sh:maxCount 1 ;
        sh:minCount 1 ;
        sh:name "last name" ;
      ] ;
    sh:property [
        rdf:type sh:PropertyShape ;
        sh:path familyShapes:mother ;
        sh:class familyShapes:Person ;
        sh:description "A Person's mother." ;
        sh:maxCount 1 ;
        sh:name "mother" ;
        sh:node familyShapes:FemaleShape ;
        sh:nodeKind sh:IRI ;
        sh:sparql [
            rdfs:comment "A person cannot be that same person's mother." ;
            sh:message "A person cannot be that same person's mother." ;
            sh:select """PREFIX familyShapes: &lt;http://example.org/familyShapes#&gt;
  
  SELECT $this 
  WHERE {
      $this familyShapes:mother $this .
  }""" ;
          ] ;
      ] ;
    sh:rule [
        rdf:type sh:SPARQLRule ;
        rdfs:label "Infer grandmas and grandchildren" ;
        sh:construct """PREFIX familyShapes: &lt;http://example.org/familyShapes#&gt;
  
  CONSTRUCT {
        ?child familyShapes:grandMa $this .
        $this familyShapes:grandChild ?child .
  }
  WHERE {
        {
          ?child familyShapes:mother ?mom .
          ?mom familyShapes:mother $this .
        }
        UNION 
        {
          ?child familyShapes:father ?dad .
          ?dad familyShapes:mother $this .
        }
  }
          """ ;
      ] ;
  .
  familyShapes:female
    rdf:type familyShapes:Gender ;
    rdfs:label "female" ;
  .
  familyShapes:firstName
    rdf:type rdf:Property ;
    rdfs:comment "A Person's first name (aka given name)." ;
    rdfs:label "first name" ;
  .
  familyShapes:grandChild
    rdf:type owl:ObjectProperty ;
    rdfs:domain familyShapes:Person ;
    rdfs:label "grand child" ;
    rdfs:range familyShapes:Person ;
  .
  familyShapes:grandMa
    rdf:type owl:ObjectProperty ;
    rdfs:domain familyShapes:Person ;
    rdfs:label "grand ma" ;
    rdfs:range familyShapes:Person ;
  .
  familyShapes:grandPa
    rdf:type owl:ObjectProperty ;
    rdfs:domain familyShapes:Person ;
    rdfs:label "grand pa" ;
    rdfs:range familyShapes:Person ;
  .
  familyShapes:male
    rdf:type familyShapes:Gender ;
    rdfs:label "male" ;
  .
  familyShapes:mother
    rdf:type rdf:Property ;
    rdfs:comment "A Person's mother." ;
    rdfs:label "mother" ;
  .
  </code></pre>
  
  <p>I'm focused at this time on the <code>familyShapes:GrandpaRuleShape</code> shape (starting on line 30) which, I believe, on line 58 targets the <code>familyShapes:Person</code> class.</p>
  
  <p>The SHACL API's <code>RuleUtil.getShapesWithTargetNode</code> method returns an empty list, which is not the result I was expecting, so I've created a temporary local copy of the <code>RuleUtil.getShapesWithTargetNode</code> method as shown below to help me debug my own code.</p>
  
  <pre><code>private static List&lt;Shape&gt; getShapesWithTargetNode(RDFNode focusNode, ShapesGraph shapesGraph) {
      // TODO: Not a particularly smart algorithm - walks all shapes that have rules
      List&lt;Shape&gt; shapes = new ArrayList&lt;&gt;();
      for(Shape shape : shapesGraph.getRootShapes()) {
          SHShape sr = shape.getShapeResource();
          boolean shapeHasRule = sr.hasProperty(SH.rule);
          boolean shapeFocused = sr.hasTargetNode(focusNode);
          if(shapeHasRule &amp;&amp; shapeFocused) {
              shapes.add(shape);
          }
      }
      return shapes;
  }
  </code></pre>
  
  <p>I've stopped execution in the debugger in this method with <code>focusNode</code>=<code>http://example.org/familyShapes#Person</code> and the shapesGraph representing the shapes file above.  The breakpoint is at the conditional in the for loop, after the two booleans have been assigned.  The first value of <code>shape</code> is <code>familyShapes:GrandpaRuleShape</code>.  However, the boolean <code>shapeFocused</code> is <code>false</code>.  The boolean <code>shapeHasRule</code> is <code>true</code> as expected.</p>
  
  <p>I was expecting that <code>shapeFocused</code> would be <code>true</code> at this point of execution.  At a higher level, I was expecting that this method would return a list at least containing the grandpa shape, but it returns empty.  I think I must be setting up the call to this method incorrectly, but I'm not sure what I'm doing wrong.  Any suggestions?</p>