Keep open tachograph intervals without synthetic tails

This commit is contained in:
trifonovt 2026-05-12 18:43:40 +02:00
parent 7209a73d30
commit 9e6f8efb26
8 changed files with 172 additions and 44 deletions

View File

@ -31,7 +31,7 @@ public record ResolvedVehicleUsageInterval(
intervalId, intervalId,
from, from,
to, to,
Duration.between(from, to).getSeconds(), to == null ? 0L : Duration.between(from, to).getSeconds(),
odometerBeginKm, odometerBeginKm,
odometerEndKm, odometerEndKm,
registrationKey, registrationKey,

View File

@ -171,6 +171,14 @@ public class DriverCardXmlExtractionService {
String path = "/DriverCard/VehiclesUsed/cardVehiclesUsed/cardVehicleRecords[" + (i + 1) + "]"; String path = "/DriverCard/VehiclesUsed/cardVehiclesUsed/cardVehicleRecords[" + (i + 1) + "]";
OffsetDateTime from = offsetDateTime(childText(record, "vehicleFirstUse")); OffsetDateTime from = offsetDateTime(childText(record, "vehicleFirstUse"));
OffsetDateTime to = offsetDateTime(childText(record, "vehicleLastUse")); OffsetDateTime to = offsetDateTime(childText(record, "vehicleLastUse"));
if (from == null) {
warnings.add(new ExtractionWarning("MISSING_VEHICLE_FIRST_USE", "Driver-card vehicle record is missing vehicleFirstUse.", path));
continue;
}
if (to != null && to.isBefore(from)) {
warnings.add(new ExtractionWarning("INVALID_VEHICLE_USE_INTERVAL", "Driver-card vehicle record has an invalid first/last use range.", path));
continue;
}
Element vehicleRegistration = child(record, "vehicleRegistration"); Element vehicleRegistration = child(record, "vehicleRegistration");
String registrationNation = childText(vehicleRegistration, "vehicleRegistrationNation"); String registrationNation = childText(vehicleRegistration, "vehicleRegistrationNation");
String registrationNumber = childText(child(vehicleRegistration, "vehicleRegistrationNumber"), "vehicleRegNumber"); String registrationNumber = childText(child(vehicleRegistration, "vehicleRegistrationNumber"), "vehicleRegNumber");
@ -192,9 +200,8 @@ public class DriverCardXmlExtractionService {
new ExtractedVehicle(vehicleKey, vehicleKeyFactory.createSourceVehicleId(vehicleKey), vin) new ExtractedVehicle(vehicleKey, vehicleKeyFactory.createSourceVehicleId(vehicleKey), vin)
); );
} }
if (from == null || to == null) { if (to == null) {
warnings.add(new ExtractionWarning("INCOMPLETE_VEHICLE_USAGE", "Vehicle usage interval is missing start or end timestamp.", path)); warnings.add(new ExtractionWarning("OPEN_VEHICLE_USAGE", "Driver-card vehicle record has no vehicleLastUse and was kept open-ended.", path));
continue;
} }
intervals.add(new ExtractedCardVehicleUsageInterval( intervals.add(new ExtractedCardVehicleUsageInterval(
"CVU-" + (i + 1), "CVU-" + (i + 1),
@ -244,11 +251,9 @@ public class DriverCardXmlExtractionService {
)); ));
} }
parsedChanges.sort(Comparator.comparing(ActivityChange::from)); parsedChanges.sort(Comparator.comparing(ActivityChange::from));
for (int i = 0; i < parsedChanges.size(); i++) { for (int i = 0; i + 1 < parsedChanges.size(); i++) {
ActivityChange current = parsedChanges.get(i); ActivityChange current = parsedChanges.get(i);
OffsetDateTime to = i + 1 < parsedChanges.size() OffsetDateTime to = parsedChanges.get(i + 1).from();
? parsedChanges.get(i + 1).from()
: date.plusDays(1).atStartOfDay().atOffset(ZoneOffset.UTC);
if (!current.from().isBefore(to)) { if (!current.from().isBefore(to)) {
continue; continue;
} }
@ -279,7 +284,7 @@ public class DriverCardXmlExtractionService {
int usageStartIndex = 0; int usageStartIndex = 0;
for (ExtractedCardActivityInterval interval : activityIntervals) { for (ExtractedCardActivityInterval interval : activityIntervals) {
while (usageStartIndex < vehicleUsageIntervals.size() while (usageStartIndex < vehicleUsageIntervals.size()
&& !endExclusive(vehicleUsageIntervals.get(usageStartIndex).to()).isAfter(interval.from())) { && !usageEndExclusive(vehicleUsageIntervals.get(usageStartIndex), interval.to()).isAfter(interval.from())) {
usageStartIndex++; usageStartIndex++;
} }
@ -309,7 +314,7 @@ public class DriverCardXmlExtractionService {
if (usage.from().isAfter(interval.from()) && usage.from().isBefore(interval.to())) { if (usage.from().isAfter(interval.from()) && usage.from().isBefore(interval.to())) {
cutPoints.add(usage.from()); cutPoints.add(usage.from());
} }
OffsetDateTime usageEndExclusive = endExclusive(usage.to()); OffsetDateTime usageEndExclusive = usageEndExclusive(usage, interval.to());
if (usageEndExclusive.isAfter(interval.from()) && usageEndExclusive.isBefore(interval.to())) { if (usageEndExclusive.isAfter(interval.from()) && usageEndExclusive.isBefore(interval.to())) {
cutPoints.add(usageEndExclusive); cutPoints.add(usageEndExclusive);
} }
@ -326,7 +331,7 @@ public class DriverCardXmlExtractionService {
} }
while (coverageIndex < overlappingUsages.size() while (coverageIndex < overlappingUsages.size()
&& !endExclusive(overlappingUsages.get(coverageIndex).to()).isAfter(segmentFrom)) { && !usageEndExclusive(overlappingUsages.get(coverageIndex), interval.to()).isAfter(segmentFrom)) {
coverageIndex++; coverageIndex++;
} }
ExtractedCardVehicleUsageInterval covering = coverageIndex < overlappingUsages.size() ExtractedCardVehicleUsageInterval covering = coverageIndex < overlappingUsages.size()
@ -351,11 +356,14 @@ public class DriverCardXmlExtractionService {
} }
private boolean covers(ExtractedCardVehicleUsageInterval usage, OffsetDateTime timestamp) { private boolean covers(ExtractedCardVehicleUsageInterval usage, OffsetDateTime timestamp) {
return !usage.from().isAfter(timestamp) && timestamp.isBefore(endExclusive(usage.to())); return !usage.from().isAfter(timestamp) && timestamp.isBefore(usageEndExclusive(usage, null));
} }
private OffsetDateTime endExclusive(OffsetDateTime timestamp) { private OffsetDateTime usageEndExclusive(ExtractedCardVehicleUsageInterval usage, OffsetDateTime fallbackExclusiveEnd) {
return timestamp.plusSeconds(1); if (usage.to() == null) {
return fallbackExclusiveEnd == null ? OffsetDateTime.MAX : fallbackExclusiveEnd;
}
return usage.to().plusSeconds(1);
} }
private Element child(Element parent, String name) { private Element child(Element parent, String name) {

View File

@ -52,7 +52,7 @@ public class DriverTimelineBuilder {
return List.of(); return List.of();
} }
List<ResolvedVehicleUsageInterval> sorted = rawIntervals.stream() List<ResolvedVehicleUsageInterval> sorted = rawIntervals.stream()
.filter(interval -> interval.from() != null && interval.to() != null && interval.to().isAfter(interval.from())) .filter(interval -> interval.from() != null && (interval.to() == null || interval.to().isAfter(interval.from())))
.map(interval -> ResolvedVehicleUsageInterval.resolved( .map(interval -> ResolvedVehicleUsageInterval.resolved(
interval.intervalId(), interval.intervalId(),
interval.from(), interval.from(),
@ -65,7 +65,7 @@ public class DriverTimelineBuilder {
List.of(interval.intervalId()) List.of(interval.intervalId())
)) ))
.sorted(Comparator.comparing(ResolvedVehicleUsageInterval::from) .sorted(Comparator.comparing(ResolvedVehicleUsageInterval::from)
.thenComparing(ResolvedVehicleUsageInterval::to)) .thenComparing(ResolvedVehicleUsageInterval::to, Comparator.nullsLast(Comparator.naturalOrder())))
.toList(); .toList();
if (sorted.isEmpty()) { if (sorted.isEmpty()) {
return List.of(); return List.of();
@ -81,7 +81,7 @@ public class DriverTimelineBuilder {
current = ResolvedVehicleUsageInterval.resolved( current = ResolvedVehicleUsageInterval.resolved(
current.intervalId() + "+" + next.intervalId(), current.intervalId() + "+" + next.intervalId(),
current.from(), current.from(),
max(current.to(), next.to()), mergedTo(current.to(), next.to()),
current.odometerBeginKm(), current.odometerBeginKm(),
next.odometerEndKm() != null ? next.odometerEndKm() : current.odometerEndKm(), next.odometerEndKm() != null ? next.odometerEndKm() : current.odometerEndKm(),
current.registrationKey(), current.registrationKey(),
@ -102,7 +102,7 @@ public class DriverTimelineBuilder {
private boolean canMerge(ResolvedVehicleUsageInterval left, ResolvedVehicleUsageInterval right) { private boolean canMerge(ResolvedVehicleUsageInterval left, ResolvedVehicleUsageInterval right) {
return Objects.equals(left.registrationKey(), right.registrationKey()) return Objects.equals(left.registrationKey(), right.registrationKey())
&& Objects.equals(left.vehicleKey(), right.vehicleKey()) && Objects.equals(left.vehicleKey(), right.vehicleKey())
&& !right.from().isAfter(left.to().plusSeconds(1)); && !right.from().isAfter(mergeBoundary(left.to()));
} }
private List<ResolvedActivityInterval> resolveActivities( private List<ResolvedActivityInterval> resolveActivities(
@ -209,4 +209,15 @@ public class DriverTimelineBuilder {
} }
return left.isAfter(right) ? left : right; return left.isAfter(right) ? left : right;
} }
private OffsetDateTime mergedTo(OffsetDateTime left, OffsetDateTime right) {
if (left == null || right == null) {
return null;
}
return max(left, right);
}
private OffsetDateTime mergeBoundary(OffsetDateTime endInclusive) {
return endInclusive == null ? OffsetDateTime.MAX : endInclusive.plusSeconds(1);
}
} }

View File

@ -103,15 +103,7 @@ public class VehicleUnitXmlExtractionService {
continue; continue;
} }
OffsetDateTime to = offsetDateTime(text(record, "cardWithdrawalTime")); OffsetDateTime to = offsetDateTime(text(record, "cardWithdrawalTime"));
if (to == null) { if (to != null && to.isBefore(from)) {
to = vehicleContext.defaultOpenIntervalEnd();
sessionWarnings.add(new ExtractionWarning(
"OPEN_VU_CARD_INTERVAL",
"Vehicle-unit insertion/withdrawal record has no withdrawal time; interval was closed using the VU downloadable-period end.",
path
));
}
if (to == null || to.isBefore(from)) {
sessionWarnings.add(new ExtractionWarning( sessionWarnings.add(new ExtractionWarning(
"INVALID_VU_CARD_INTERVAL", "INVALID_VU_CARD_INTERVAL",
"Vehicle-unit insertion/withdrawal record has an invalid interval range.", "Vehicle-unit insertion/withdrawal record has an invalid interval range.",
@ -288,11 +280,9 @@ public class VehicleUnitXmlExtractionService {
)); ));
} }
parsedChanges.sort(Comparator.comparing(ActivityChange::from)); parsedChanges.sort(Comparator.comparing(ActivityChange::from));
for (int i = 0; i < parsedChanges.size(); i++) { for (int i = 0; i + 1 < parsedChanges.size(); i++) {
ActivityChange current = parsedChanges.get(i); ActivityChange current = parsedChanges.get(i);
OffsetDateTime to = i + 1 < parsedChanges.size() OffsetDateTime to = parsedChanges.get(i + 1).from();
? parsedChanges.get(i + 1).from()
: date.plusDays(1).atStartOfDay().atOffset(ZoneOffset.UTC);
if (!current.from().isBefore(to)) { if (!current.from().isBefore(to)) {
continue; continue;
} }
@ -898,7 +888,7 @@ public class VehicleUnitXmlExtractionService {
String rawRecordPath String rawRecordPath
) { ) {
private OffsetDateTime endExclusive() { private OffsetDateTime endExclusive() {
return to.plusSeconds(1); return to == null ? OffsetDateTime.MAX : to.plusSeconds(1);
} }
private boolean covers(OffsetDateTime timestamp) { private boolean covers(OffsetDateTime timestamp) {

View File

@ -45,12 +45,10 @@ class DriverCardXmlExtractionServiceTest {
assertThat(driver.vehicleRegistrations()).hasSize(2); assertThat(driver.vehicleRegistrations()).hasSize(2);
assertThat(driver.vehicles()).hasSize(2); assertThat(driver.vehicles()).hasSize(2);
assertThat(driver.cardVehicleUsageIntervals()).hasSize(2); assertThat(driver.cardVehicleUsageIntervals()).hasSize(2);
assertThat(driver.cardActivityIntervals()).hasSize(5); assertThat(driver.cardActivityIntervals()).hasSize(3);
assertThat(driver.cardActivityIntervals().get(0).registrationKey()).isEqualTo("12:W-12345A"); assertThat(driver.cardActivityIntervals().get(0).registrationKey()).isEqualTo("12:W-12345A");
assertThat(driver.cardActivityIntervals().get(1).to()).isEqualTo(driver.cardVehicleUsageIntervals().get(0).to().plusSeconds(1)); assertThat(driver.cardActivityIntervals().get(1).to()).isEqualTo(driver.cardVehicleUsageIntervals().get(0).to().plusSeconds(1));
assertThat(driver.cardActivityIntervals().get(2).registrationKey()).isEqualTo("12:W-54321B"); assertThat(driver.cardActivityIntervals().get(2).registrationKey()).isEqualTo("12:W-54321B");
assertThat(driver.cardActivityIntervals().get(3).registrationKey()).isEqualTo("12:W-54321B");
assertThat(driver.cardActivityIntervals().get(4).registrationKey()).isNull();
} }
@Test @Test
@ -74,9 +72,36 @@ class DriverCardXmlExtractionServiceTest {
); );
DriverExtractionSession driver = session.driversByKey().values().iterator().next(); DriverExtractionSession driver = session.driversByKey().values().iterator().next();
assertThat(driver.cardActivityIntervals()).hasSize(5); assertThat(driver.cardActivityIntervals()).hasSize(3);
assertThat(driver.cardActivityIntervals()) assertThat(driver.cardActivityIntervals())
.extracting(interval -> interval.from().toString()) .extracting(interval -> interval.from().toString())
.contains("2026-04-01T08:00Z", "2026-04-01T12:30Z"); .contains("2026-04-01T08:00Z", "2026-04-01T12:00Z");
}
@Test
void keepsLastOpenVehicleUseRecordWithoutSynthesizingActivityTail() {
TachographFileSession session = service.extract(
parser.parse(DriverCardXmlSamples.driverCardXmlWithOpenVehicleUseRecord()),
new TachographFileSessionMetadata(
"default",
"legalrequirements-drivercard",
"sample",
"sample.ddd",
"abc",
10,
"42",
"def",
true,
null
),
Instant.now(),
Instant.now().plus(4, ChronoUnit.HOURS)
);
DriverExtractionSession driver = session.driversByKey().values().iterator().next();
assertThat(driver.cardVehicleUsageIntervals()).hasSize(2);
assertThat(driver.cardVehicleUsageIntervals().get(1).to()).isNull();
assertThat(driver.cardActivityIntervals()).hasSize(3);
assertThat(driver.cardActivityIntervals().get(2).registrationKey()).isEqualTo("12:W-54321B");
} }
} }

View File

@ -119,4 +119,9 @@ final class DriverCardXmlSamples {
.replace("<timeOfChange>09:00:00Z</timeOfChange>", "<timeOfChange>09:00:00</timeOfChange>") .replace("<timeOfChange>09:00:00Z</timeOfChange>", "<timeOfChange>09:00:00</timeOfChange>")
.replace("<timeOfChange>12:30:00Z</timeOfChange>", "<timeOfChange>12:30:00</timeOfChange>"); .replace("<timeOfChange>12:30:00Z</timeOfChange>", "<timeOfChange>12:30:00</timeOfChange>");
} }
static String driverCardXmlWithOpenVehicleUseRecord() {
return validDriverCardXml()
.replace("<vehicleLastUse>2026-04-01T18:00:00Z</vehicleLastUse>\n", "");
}
} }

View File

@ -108,4 +108,70 @@ class DriverTimelineBuilderTest {
assertThat(timeline.loadedTo()).isEqualTo(OffsetDateTime.parse("2026-05-02T08:00:00Z")); assertThat(timeline.loadedTo()).isEqualTo(OffsetDateTime.parse("2026-05-02T08:00:00Z"));
assertThat(timeline.warnings()).extracting(ExtractionWarning::code).containsExactly("W2", "W1"); assertThat(timeline.warnings()).extracting(ExtractionWarning::code).containsExactly("W2", "W1");
} }
@Test
void mergesTouchingOpenEndedVehicleUsageInterval() {
DriverExtractionSession driver = new DriverExtractionSession(
"12:123",
null,
null,
List.of(),
List.of(),
List.of(
new ExtractedCardVehicleUsageInterval(
"CVU-1",
OffsetDateTime.parse("2026-05-01T00:00:00Z"),
OffsetDateTime.parse("2026-05-01T23:59:59Z"),
100L,
200L,
"12:REG-1",
"VIN-1",
"a"
),
new ExtractedCardVehicleUsageInterval(
"CVU-2",
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
null,
201L,
null,
"12:REG-1",
"VIN-1",
"b"
)
),
List.of(
new ExtractedCardActivityInterval(
"ACT-1",
OffsetDateTime.parse("2026-05-02T08:00:00Z"),
OffsetDateTime.parse("2026-05-02T09:00:00Z"),
"WORK",
"DRIVER",
"INSERTED",
"SINGLE",
"12:REG-1",
"VIN-1",
"c"
)
),
List.of(),
List.of()
);
TachographFileSession session = new TachographFileSession(
UUID.randomUUID(),
new TachographFileSessionMetadata("default", "legalrequirements-drivercard", "sample", "sample.ddd", "a", 3, "42", "b", true, null),
Map.of(driver.driverKey(), driver),
new ExtractionStats(1, 1, 2, 1, 1, 0),
List.of(),
Instant.now(),
Instant.now().plus(4, ChronoUnit.HOURS)
);
ResolvedDriverTimeline timeline = builder.build(session, driver);
assertThat(timeline.vehicleUsageIntervals()).hasSize(1);
assertThat(timeline.vehicleUsageIntervals().get(0).from()).isEqualTo(OffsetDateTime.parse("2026-05-01T00:00:00Z"));
assertThat(timeline.vehicleUsageIntervals().get(0).to()).isNull();
assertThat(timeline.vehicleUsageIntervals().get(0).sourceIntervalIds()).containsExactly("CVU-1", "CVU-2");
assertThat(timeline.loadedTo()).isEqualTo(OffsetDateTime.parse("2026-05-02T09:00:00Z"));
}
} }

View File

@ -52,10 +52,9 @@ class VehicleUnitXmlExtractionServiceTest {
assertThat(firstDriver.cardVehicleUsageIntervals()).hasSize(1); assertThat(firstDriver.cardVehicleUsageIntervals()).hasSize(1);
assertThat(firstDriver.cardVehicleUsageIntervals().get(0).from().toString()).isEqualTo("2026-04-01T08:00Z"); assertThat(firstDriver.cardVehicleUsageIntervals().get(0).from().toString()).isEqualTo("2026-04-01T08:00Z");
assertThat(firstDriver.cardVehicleUsageIntervals().get(0).to().toString()).isEqualTo("2026-04-01T11:00Z"); assertThat(firstDriver.cardVehicleUsageIntervals().get(0).to().toString()).isEqualTo("2026-04-01T11:00Z");
assertThat(firstDriver.cardActivityIntervals()).hasSize(3); assertThat(firstDriver.cardActivityIntervals()).hasSize(2);
assertThat(firstDriver.cardActivityIntervals().get(0).activityType()).isEqualTo("WORK"); assertThat(firstDriver.cardActivityIntervals().get(0).activityType()).isEqualTo("WORK");
assertThat(firstDriver.cardActivityIntervals().get(1).activityType()).isEqualTo("DRIVE"); assertThat(firstDriver.cardActivityIntervals().get(1).activityType()).isEqualTo("DRIVE");
assertThat(firstDriver.cardActivityIntervals().get(2).to().toString()).isEqualTo("2026-04-01T11:00:01Z");
assertThat(firstDriver.supportEvents()).hasSize(1); assertThat(firstDriver.supportEvents()).hasSize(1);
assertThat(firstDriver.supportEvents().get(0).eventDomain()).isEqualTo("PLACE"); assertThat(firstDriver.supportEvents().get(0).eventDomain()).isEqualTo("PLACE");
assertThat(firstDriver.supportEvents().get(0).eventType()).isEqualTo("BEGIN_DAILY_WORK_PERIOD"); assertThat(firstDriver.supportEvents().get(0).eventType()).isEqualTo("BEGIN_DAILY_WORK_PERIOD");
@ -63,17 +62,15 @@ class VehicleUnitXmlExtractionServiceTest {
assertThat(firstDriver.supportEvents().get(0).latitude()).isNotNull(); assertThat(firstDriver.supportEvents().get(0).latitude()).isNotNull();
assertThat(secondDriver.cardVehicleUsageIntervals()).hasSize(1); assertThat(secondDriver.cardVehicleUsageIntervals()).hasSize(1);
assertThat(secondDriver.cardVehicleUsageIntervals().get(0).to().toString()).isEqualTo("2026-04-02T10:00Z"); assertThat(secondDriver.cardVehicleUsageIntervals().get(0).to()).isNull();
assertThat(secondDriver.cardActivityIntervals()).hasSize(2); assertThat(secondDriver.cardActivityIntervals()).hasSize(1);
assertThat(secondDriver.cardActivityIntervals().get(0).from().toString()).isEqualTo("2026-04-02T07:30Z"); assertThat(secondDriver.cardActivityIntervals().get(0).from().toString()).isEqualTo("2026-04-02T07:30Z");
assertThat(secondDriver.cardActivityIntervals().get(1).to().toString()).isEqualTo("2026-04-02T10:00:01Z");
assertThat(secondDriver.supportEvents()).hasSize(2); assertThat(secondDriver.supportEvents()).hasSize(2);
assertThat(secondDriver.supportEvents()).extracting("eventDomain").containsExactly("POSITION", "SPECIFIC_CONDITION"); assertThat(secondDriver.supportEvents()).extracting("eventDomain").containsExactly("POSITION", "SPECIFIC_CONDITION");
assertThat(secondDriver.supportEvents().get(0).latitude()).isNotNull(); assertThat(secondDriver.supportEvents().get(0).latitude()).isNotNull();
assertThat(secondDriver.supportEvents().get(1).code()).isEqualTo("1"); assertThat(secondDriver.supportEvents().get(1).code()).isEqualTo("1");
assertThat(session.warnings()).extracting("code") assertThat(session.warnings()).isEmpty();
.contains("OPEN_VU_CARD_INTERVAL", "VU_ACTIVITY_UNASSIGNED");
} }
@Test @Test
@ -102,6 +99,32 @@ class VehicleUnitXmlExtractionServiceTest {
assertThat(secondDriver.cardActivityIntervals().get(0).from().toString()).isEqualTo("2026-04-02T07:30Z"); assertThat(secondDriver.cardActivityIntervals().get(0).from().toString()).isEqualTo("2026-04-02T07:30Z");
} }
@Test
void keepsOpenVuCardUsageRecordWithoutClosingItAtDownloadPeriodEnd() throws Exception {
TachographFileSession session = service.extract(
new TachographXmlParser.ParsedTachographXml(document(VehicleUnitXmlSamples.vehicleUnitXml()), "VehicleUnit"),
new TachographFileSessionMetadata(
"default",
"legalrequirements-vehicleunit",
"sample-vu",
"sample-vu.ddd",
"abc",
10,
"42",
"def",
false,
null
),
Instant.now(),
Instant.now().plus(4, ChronoUnit.HOURS)
);
DriverExtractionSession secondDriver = session.driversByKey().get("12:99999999999911");
assertThat(secondDriver.cardVehicleUsageIntervals()).hasSize(1);
assertThat(secondDriver.cardVehicleUsageIntervals().get(0).from().toString()).isEqualTo("2026-04-02T07:30Z");
assertThat(secondDriver.cardVehicleUsageIntervals().get(0).to()).isNull();
}
private Document document(String xml) throws Exception { private Document document(String xml) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(false); factory.setNamespaceAware(false);