Skip to content

Commit

Permalink
WstxEventFactory: Ensure events' immutable non-null location (#205)
Browse files Browse the repository at this point in the history
  • Loading branch information
stanio authored Apr 30, 2024
1 parent 5419349 commit dbef4a9
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 0 deletions.
5 changes: 5 additions & 0 deletions release-notes/CREDITS
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,8 @@ Alexey Gavrilov (@agavrilov76)
Jeremy Norris (@norrisjeremy)
* Reported #200: Fix shading of `isorelax` dependency (and other msv-core components)
(6.6.2)

Stanimir Stamenkov (@stanio)

* Reported, provided fix for #204: Non-conformant `XMLEventFactory.setLocation(null)`
(7.0.0)
2 changes: 2 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Project: woodstox
#194: Remove `QNameCreator` compatibility class
#196: WstxSAXParser error handling when used with JAXB validation
(reported by @winfriedgerlach)
#204: Non-conformant `XMLEventFactory.setLocation(null)`
(fix provided by Stanimir S)

6.6.2 (26-Mar-2024)

Expand Down
25 changes: 25 additions & 0 deletions src/main/java/com/ctc/wstx/stax/WstxEventFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
import javax.xml.stream.events.*;

import aQute.bnd.annotation.spi.ServiceProvider;
import org.codehaus.stax2.XMLStreamLocation2;
import org.codehaus.stax2.ri.Stax2EventFactoryImpl;

import com.ctc.wstx.evt.*;
import com.ctc.wstx.io.WstxInputLocation;

/**
* Implementation of {@link XMLEventFactory} to be used with
Expand All @@ -37,6 +39,7 @@ public final class WstxEventFactory
{
public WstxEventFactory() {
super();
super.setLocation(WstxInputLocation.getEmptyLocation());
}

/*
Expand All @@ -45,6 +48,28 @@ public WstxEventFactory() {
/////////////////////////////////////////////////////////////
*/

@Override
public void setLocation(Location location) {
super.setLocation(location == null ? WstxInputLocation.getEmptyLocation()
: immutableLocation(location));
}

private static WstxInputLocation immutableLocation(Location location) {
if (location == null) {
return null;
}
if (location.getClass() == WstxInputLocation.class) {
return (WstxInputLocation) location;
}

WstxInputLocation context = (location instanceof XMLStreamLocation2)
? immutableLocation(((XMLStreamLocation2) location).getContext())
: null;
return new WstxInputLocation(context, location.getPublicId(),
location.getSystemId(), location.getCharacterOffset(),
location.getLineNumber(), location.getColumnNumber());
}

//public Attribute createAttribute(QName name, String value)
//public Attribute createAttribute(String localName, String value)
//public Attribute createAttribute(String prefix, String nsURI, String localName, String value)
Expand Down
102 changes: 102 additions & 0 deletions src/test/java/wstxtest/evt/TestEventFactoryLocation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package wstxtest.evt;

import static org.hamcrest.MatcherAssert.assertThat;

import java.util.Objects;

import javax.xml.stream.Location;
import javax.xml.stream.events.XMLEvent;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Before;
import org.junit.Test;

import com.ctc.wstx.io.WstxInputLocation;
import com.ctc.wstx.stax.WstxEventFactory;

public class TestEventFactoryLocation {

private WstxEventFactory eventFactory;

@Before
public void setUp() {
eventFactory = new WstxEventFactory();
}

@Test
public void testDefaultLocation() {
XMLEvent event = eventFactory.createStartDocument();

assertThat("event.location", event.getLocation(), unknownLocation());
}

@Test
public void testResetLocation() {
eventFactory.setLocation(new WstxInputLocation(null, null, "about:blank", 3L, 2, 1));
eventFactory.setLocation(null);

XMLEvent event = eventFactory.createStartElement("foo", "bar", "baz");

assertThat("event.location", event.getLocation(), unknownLocation());
}

@Test
public void testNonVolatileLocation() {
VolatileLocation volatileLocation = new VolatileLocation(2, 3);
eventFactory.setLocation(volatileLocation);

XMLEvent event = eventFactory.createEndElement("foo", "bar", "baz");
volatileLocation.line = 4;
volatileLocation.col = 5;

assertThat("event.location", event.getLocation(),
locationWithProperties(null, null, -1, 2, 3));
}

private static Matcher<Location> unknownLocation() {
// XXX: Not sure if the empty-string publicId/systemId are conformant
//return locationWithProperties(null, null, -1, -1, -1);
return locationWithProperties("", "", -1, -1, -1);
}

private static Matcher<Location> locationWithProperties(String pubId,
String sysId, int charOffset, int row, int col) {
return new TypeSafeMatcher<Location>() {
@Override public void describeTo(Description description) {
description.appendText("Location(publicId: ").appendValue(pubId)
.appendText(", systemId: ").appendValue(sysId)
.appendText(", characterOffset: ").appendValue(charOffset)
.appendText(", lineNumber: ").appendValue(row)
.appendText(", columnNumber: ").appendValue(col);
}

@Override protected boolean matchesSafely(Location item) {
return Objects.equals(item.getPublicId(), pubId)
&& Objects.equals(item.getSystemId(), sysId)
&& Objects.equals(item.getCharacterOffset(), charOffset)
&& Objects.equals(item.getLineNumber(), row)
&& Objects.equals(item.getColumnNumber(), col);
}
};
}


static class VolatileLocation implements Location {
int line;
int col;
VolatileLocation(int line, int col) {
this.line = line;
this.col = col;
}
@Override public String getPublicId() { return null; }
@Override public String getSystemId() { return null; }
@Override public int getLineNumber() { return line; }
@Override public int getColumnNumber() { return col; }
@Override public int getCharacterOffset() { return -1; }
}


}

0 comments on commit dbef4a9

Please sign in to comment.