Repository: dita-ot/dita-ot
Issue: Branch filtering processes nested-topic topic files multiple times

## Expected Behavior
When branch filtering is applied to a topic with nested subtopics:

```
<map>
  <title>Test Map</title>

  <ditavalref href="filter-A.ditaval"/>

  <topicref href="topic.dita">
    <topicref href="topic.dita#id_1">
      <topicref href="topic.dita#id_1_1"/>
      <topicref href="topic.dita#id_1_2"/>
    </topicref>
    <topicref href="topic.dita#id_2">
      <topicref href="topic.dita#id_2_1"/>
      <topicref href="topic.dita#id_2_2"/>
    </topicref>
  </topicref>

</map>
```

the `branch-filter` step should process each topic file a single time:

```
[branch-filter] Filtering file:/tmp/temp20220319114445274/topic.dita
```

## Actual Behavior
The `branch-filter` step processes each topic file multiple times:

```
[branch-filter] Filtering file:/tmp/temp20220319114445274/topic.dita
[branch-filter] Filtering file:/tmp/temp20220319114445274/topic.dita#id_1
[branch-filter] Filtering file:/tmp/temp20220319114445274/topic.dita#id_1_1
[branch-filter] Filtering file:/tmp/temp20220319114445274/topic.dita#id_1_2
[branch-filter] Filtering file:/tmp/temp20220319114445274/topic.dita#id_2
[branch-filter] Filtering file:/tmp/temp20220319114445274/topic.dita#id_2_1
[branch-filter] Filtering file:/tmp/temp20220319114445274/topic.dita#id_2_2
```

I confirmed that the first branch filtering operation processes the entire file; thus, the subsequent operations perform unnecessary read-modify-write operations.

On small testcases, the effect on runtime is negligible.

But on a production online help map with many nested topics, branch filtering takes 66% of the `html5` transformation runtime (1050 seconds for branch filtering, out of 1590 seconds total).

#3875 is likely related - see the bottom of [this comment](https://github.com/dita-ot/dita-ot/issues/3875#issuecomment-1044208535).

## Possible Solution
`BranchFilterModule.java` should be updated to process each file a single time. The solution should handle both omitted root topic `@id` values:

```
  <topicref href="topic.dita"> <!-- root topic @id omitted -->
    <topicref href="topic.dita#id_1"/>
    <topicref href="topic.dita#id_2"/>
  </topicref>
```

as well as explicit root topic `@id` values:

```
  <topicref href="topic.dita#root_topic_id"> <!-- @id explicitly included -->
    <topicref href="topic.dita#id_1"/>
    <topicref href="topic.dita#id_2"/>
  </topicref>
```

## Steps to Reproduce
You must use a DITA-OT that includes the fix for #3875 (3.7.1 when released, or a development branch before then).

1. Download and extract the following testcase:
   [3903.zip](https://github.com/dita-ot/dita-ot/files/11067818/3903.zip)
2. Run the following command:
   `dita -i map.ditamap -f html5 -verbose`

A Perl script is included to generate a nested map/topic structure. For example, to try various combinations of 1000 nested topics,

```
make_nested_topic_testcase.pl 1000
dita -i map.ditamap -f html5 -verbose

make_nested_topic_testcase.pl 20 50
dita -i map.ditamap -f html5 -verbose

make_nested_topic_testcase.pl 10 10 10
dita -i map.ditamap -f html5 -verbose
```

The included testcase was generated with `make_nested_topic_testcase.pl 2 2 2`.

## Environment
* DITA-OT version: 3.7.1
* Operating system and version: 
  Linux (Ubuntu 20.04)
* How did you run DITA-OT?
  `dita` command
* Transformation type:
  `html5`


Changed files:
src/main/java/org/dita/dost/module/BranchFilterModule.java

diff --git a/src/main/java/org/dita/dost/module/BranchFilterModule.java b/src/main/java/org/dita/dost/module/BranchFilterModule.java
index 5c45a5289..f21f7bda2 100644
--- a/src/main/java/org/dita/dost/module/BranchFilterModule.java
+++ b/src/main/java/org/dita/dost/module/BranchFilterModule.java
@@ -11,11 +11,9 @@ import static java.util.Collections.singletonList;
 import static org.dita.dost.util.Constants.*;
 import static org.dita.dost.util.StringUtils.getExtProps;
 import static org.dita.dost.util.StringUtils.getExtPropsFromSpecializations;
-import static org.dita.dost.util.URLUtils.stripFragment;
-import static org.dita.dost.util.URLUtils.toURI;
+import static org.dita.dost.util.URLUtils.*;
 import static org.dita.dost.util.XMLUtils.*;
 
-import java.io.File;
 import java.io.IOException;
 import java.net.URI;
 import java.util.*;
@@ -419,7 +417,7 @@ public class BranchFilterModule extends AbstractPipelineModuleImpl {
 
     final String href = topicref.getAttribute(ATTRIBUTE_NAME_HREF);
     final Attr skipFilter = topicref.getAttributeNode(SKIP_FILTER);
-    final URI srcAbsUri = job.tempDirURI.resolve(map.resolve(href));
+    final URI srcAbsUri = setFragment(job.tempDirURI.resolve(map.resolve(href)), null);
     if (
       !fs.isEmpty() &&
       skipFilter == null &&