|
9 | 9 | import java.net.URISyntaxException; |
10 | 10 | import java.nio.charset.Charset; |
11 | 11 | import java.util.*; |
| 12 | +import java.util.function.UnaryOperator; |
12 | 13 | import java.util.regex.Pattern; |
13 | 14 | import java.util.stream.Collectors; |
14 | 15 | import java.util.stream.Stream; |
@@ -137,22 +138,22 @@ private BitSet elementBitSet(byte[] doc) throws XMLStreamException { |
137 | 138 | * A Slight variation on writeAsUnicode from stax which writes as a regex string so we could |
138 | 139 | * rewrite its output |
139 | 140 | */ |
140 | | - private String writeAsRegex(StartElement element) { |
| 141 | + private String writeAsRegex(final StartElement element, final List<Attribute> orderedAttributes) { |
141 | 142 | StringWriter writer = new StringWriter(); |
142 | 143 |
|
143 | 144 | writer.write("<"); |
144 | 145 | writer.write(Pattern.quote(element.getName().getLocalPart())); |
145 | 146 |
|
146 | | - Iterator<?> attrIter = element.getAttributes(); |
147 | | - while (attrIter.hasNext()) { |
148 | | - Attribute attr = (Attribute) attrIter.next(); |
149 | | - |
| 147 | + for (var attr : orderedAttributes) { |
150 | 148 | writer.write("\\s+"); |
151 | 149 |
|
152 | 150 | writer.write(Pattern.quote(attr.getName().getLocalPart())); |
153 | | - writer.write("=[\\\"\']"); |
| 151 | + writer.write("\\s*"); |
| 152 | + writer.write("="); |
| 153 | + writer.write("\\s*"); |
| 154 | + writer.write("[\\\"']"); |
154 | 155 | writer.write(Pattern.quote(attr.getValue())); |
155 | | - writer.write("[\\\"\']"); |
| 156 | + writer.write("[\\\"']"); |
156 | 157 | } |
157 | 158 | writer.write("\\s*\\/>"); |
158 | 159 |
|
@@ -335,7 +336,25 @@ private void parseXmlAndCharset(POMDocument pomFile) throws XMLStreamException, |
335 | 336 | new IntRange( |
336 | 337 | realElementStart, realElementStart + 1 + trimmedOriginalContent.length()); |
337 | 338 |
|
338 | | - String contentRe = writeAsRegex(getLastStartElement(prevEvents)); |
| 339 | + var element = getLastStartElement(prevEvents); |
| 340 | + |
| 341 | + // order the attributes by the original ordering |
| 342 | + // attributes names are unique, we can just order them by the index of the name |
| 343 | + |
| 344 | + // Remove attribute contents, just in case some they contain the name of an attribute |
| 345 | + // TODO should we trim the element name beforehand? |
| 346 | + String contentRemoved = untrimmedOriginalContent.replaceAll("[\\\"'].*[\\\"']", ""); |
| 347 | + |
| 348 | + var it = element.getAttributes(); |
| 349 | + var orderedAttributes = |
| 350 | + Stream.iterate(it, Iterator::hasNext, UnaryOperator.identity()) |
| 351 | + .map(Iterator::next) |
| 352 | + .map(a -> new Pair<>(a, contentRemoved.indexOf(a.getName().getLocalPart()))) |
| 353 | + .sorted(Comparator.comparing(p -> p.getSecond())) |
| 354 | + .map(p -> p.getFirst()) |
| 355 | + .collect(Collectors.toList()); |
| 356 | + |
| 357 | + String contentRe = writeAsRegex(element, orderedAttributes); |
339 | 358 |
|
340 | 359 | Regex modifiedContentRE = new Regex(contentRe); |
341 | 360 |
|
@@ -397,7 +416,7 @@ private StartElement getLastStartElement(List<XMLEvent> prevEvents) { |
397 | 416 | * <p>this is important so we can mix and match offsets and apply formatting accordingly |
398 | 417 | * |
399 | 418 | * @param xmlDocumentString Rendered POM Document Contents (string-formatted) |
400 | | - * @return map of (index, matchData object) reverse ordered |
| 419 | + * @return map of (index, matchData object) reve/rse ordered |
401 | 420 | */ |
402 | 421 | private LinkedHashMap<Integer, MatchData> findSingleElementMatchesFrom(String xmlDocumentString) { |
403 | 422 | Sequence<MatchResult> allFoundMatchesSequence = |
@@ -494,6 +513,8 @@ private byte[] serializePomFile(POMDocument pom) throws XMLStreamException { |
494 | 513 | // Let's find out the original empty elements from the original pom and store into a stack |
495 | 514 | List<MatchData> elementsToReplace = getElementsToReplace(originalElementMap, pom); |
496 | 515 |
|
| 516 | + // DOM parsers don't guarantee attribute ordering, extract the original ordering for the regex |
| 517 | + |
497 | 518 | // Lets to the replacements backwards on the existing, current pom |
498 | 519 | Map<Integer, MatchData> emptyElements = getEmptyElements(targetElementMap, xmlRepresentation); |
499 | 520 |
|
|
0 commit comments