jOOX icon indicating copy to clipboard operation
jOOX copied to clipboard

Add method to make a deep copy of all matches

Open Nordiii opened this issue 2 years ago • 8 comments

My Usecase

I want to modify .fodt Files and replace the placeholders inside the document with values from a Database. Now I gather multiple matches via the nextUntil(Filter) Method. Currently I need to do something like this to deep copy all elements:

        List<ProjectDTO> projects;
        Match projectMatches = aMatch.nextUntil(filter);
        projects.stream().forEach(project -> {
            List<Element> deepCopies = projectMatches.each()
                    .stream().map(val -> (Element) val.get(0).cloneNode(true))
                    .toList();
            Match copy = $();
            copy = copy.add((deepCopies.toArray(new Element[deepCopies.size()])));
            
            //Do Stuff with the copy
        });

There is Probably a cleaner way, but it would be nice if I could just call projectMatches.deepCopy()

Versions:

  • jOOX:2.0.0
  • Java:17

Nordiii avatar Apr 04 '23 09:04 Nordiii

Another workaround is to just toString() the matches and import them again. But I'm not sure if I understand the connection between your use-case and your suggested solution. Can you describe your use-case instead?

lukaseder avatar Apr 04 '23 10:04 lukaseder

Sorry for me beeing unclear! I have an office document in .fodt format (XML) which works as a template, now I need to fill placeholder values with data from the database. Some placeholders should be duplicated, in case there are multiple values stored in the database. Lets say there are 3 Projects (name, time and more data stored in the database). Now the office document has a section for a Project with placeholders which should be replaced.

This bit could look like this in the fodt File:

....
<text:p>$PROJECT_START<\text:p>
<text:p><text:span>$PROJECT_NAME</text:span><\text:p>
<table:table>
  <table:row>
  ...
 </table:row>
</table:table>
...
</text:p>$PROJECT_END</text:p>
...

Now I gather all matches via nextUntil(matches("$PROJECT_END")). All values between the placeholder "$PROJECT_START" and "$PROJECT_END" are collected by this. All matches gathered I want to duplicate those 3 times (for each Project) and fill the placeholders with the project specific data.

Nordiii avatar Apr 04 '23 11:04 Nordiii

Some placeholders should be duplicated, in case there are multiple values stored in the database.

Ah, I see. I was thinking of replacing placeholders, not duplicating them.

Anyway, this should work?

Match d = JOOX.$("""
    <a>
      <b/>
    </a>
    """);
d.append(d.find("b").toString());
System.out.println(d);

I can see the utility of making this available through API, but in most cases, the string workaround is good enough.

lukaseder avatar Apr 04 '23 11:04 lukaseder

Copying multiple matches (without inserting them in the existing DOM?) sadly does throw an error:

        projects.stream().forEach(project -> {
            Match copy = $(projectMatches.toString());
            //Do Stuff with the copy
        });

Error: org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: An attempt was made to insert a node where it is not permitted.

EDIT: Calling

        projects.stream().forEach(project -> {
            Match copy = $().append(projectMatches.toString());
            //Do Stuff with the copy
        });

does not throw an error but is an empty copy.

Nordiii avatar Apr 04 '23 12:04 Nordiii

But you're not doing what I suggested, you're doing entirely other things. $(String) creates a new document, and that has to be valid, which it isn't in the first case. In the second case, there is no root element, so nothing can be added.

lukaseder avatar Apr 04 '23 12:04 lukaseder

My Problem with your example is that you grab a single match and duplicate it. In my example I want too:

  1. Grab a batch of matches (nextUntil()) (I can’t go by an single XML Tag to get a root node for the project because the next parent is the whole text document, which contains not only the project data) Then for each project
  2. Make a deep copy of all matches
  3. Replace Placeholders
  4. Insert the batch of matches
  5. remove the original matches with the placeholders from the DOM

Sorry for bothering you, the help is much appreciated!

Nordiii avatar Apr 04 '23 12:04 Nordiii

If you have an "xmlforest", just add a new dummy root node:

Match:

<a/>
<a/>
<a/>

String workaround:

<dummy>
  <a/>
  <a/>
  <a/>
</dummy>

I mean, it's a workaround. There are other tricks, too. You could also loop over each element in the match using each() and do element-by-element processing.

I hope this helps?

lukaseder avatar Apr 04 '23 12:04 lukaseder

Sorry for the late reply, thank you a lot for the help and time you provided! Thanks to you I was able to archive my goal.

Nordiii avatar Apr 20 '23 09:04 Nordiii