Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
import org.apache.fesod.sheet.converters.string.StringNumberConverter;
import org.apache.fesod.sheet.converters.string.StringStringConverter;
import org.apache.fesod.sheet.converters.url.UrlImageConverter;
import org.apache.fesod.sheet.converters.uuid.UUIDStringConverter;
import org.apache.fesod.sheet.converters.year.YearStringConverter;

/**
Expand Down Expand Up @@ -162,6 +163,8 @@ private static void initAllConverter() {
putAllConverter(new StringStringConverter());
putAllConverter(new StringErrorConverter());

putAllConverter(new UUIDStringConverter());

putAllConverter(new YearStringConverter());
allConverter = Collections.unmodifiableMap(allConverter);
}
Expand Down Expand Up @@ -191,6 +194,7 @@ private static void initDefaultWriteConverter() {
putWriteConverter(new ByteArrayImageConverter());
putWriteConverter(new BoxingByteArrayImageConverter());
putWriteConverter(new UrlImageConverter());
putWriteConverter(new UUIDStringConverter());

// In some cases, it must be converted to string
putWriteStringConverter(new BigDecimalStringConverter());
Expand All @@ -210,6 +214,7 @@ private static void initDefaultWriteConverter() {
putWriteStringConverter(new LongStringConverter());
putWriteStringConverter(new ShortStringConverter());
putWriteStringConverter(new StringStringConverter());
putWriteStringConverter(new UUIDStringConverter());
putWriteStringConverter(new YearStringConverter());
defaultWriteConverter = Collections.unmodifiableMap(defaultWriteConverter);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.fesod.sheet.converters.uuid;

import java.util.UUID;
import org.apache.fesod.common.util.StringUtils;
import org.apache.fesod.sheet.converters.Converter;
import org.apache.fesod.sheet.enums.CellDataTypeEnum;
import org.apache.fesod.sheet.metadata.GlobalConfiguration;
import org.apache.fesod.sheet.metadata.data.ReadCellData;
import org.apache.fesod.sheet.metadata.data.WriteCellData;
import org.apache.fesod.sheet.metadata.property.ExcelContentProperty;

/**
* Converts UUID values to canonical lowercase strings and reads strings using {@link UUID#fromString(String)}.
* Blank strings are treated as missing values, like blank cells. Leading and trailing whitespace is trimmed before parsing.
*/
public class UUIDStringConverter implements Converter<UUID> {

@Override
public Class<UUID> supportJavaTypeKey() {
return UUID.class;
}

@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}

@Override
public UUID convertToJavaData(
ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
String value = cellData.getStringValue();
return StringUtils.isBlank(value) ? null : UUID.fromString(value.trim());
}

@Override
public WriteCellData<?> convertToExcelData(
UUID value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
return new WriteCellData<>(value.toString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.fesod.sheet.converter;

import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import lombok.Data;
import org.apache.fesod.sheet.exception.ExcelDataConvertException;
import org.apache.fesod.sheet.testkit.Tags;
import org.apache.fesod.sheet.testkit.base.AbstractExcelTest;
import org.apache.fesod.sheet.testkit.enums.ExcelFormat;
import org.apache.fesod.sheet.testkit.helpers.RoundTripHelper;
import org.apache.fesod.sheet.testkit.params.ExcelFormatSource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.params.ParameterizedTest;

@Tag(Tags.ROUND_TRIP)
class UUIDConverterRoundTripTest extends AbstractExcelTest {

private static final String TEXT = "123e4567-e89b-12d3-a456-426614174000";

@ParameterizedTest
@ExcelFormatSource
void roundTripsUuidAndNullFieldsWithoutCustomRegistration(ExcelFormat format) throws Exception {
File file = createTempFile(format);
UUIDData first = new UUIDData();
first.setId(UUID.fromString(TEXT));
first.setLabel("populated");
UUIDData second = new UUIDData();
second.setLabel("null UUID");
List<UUIDData> expected = Arrays.asList(first, second);
Assertions.assertEquals(expected, RoundTripHelper.writeAndRead(file, UUIDData.class, expected));
List<StringData> strings = RoundTripHelper.read(file, StringData.class);
Assertions.assertEquals(TEXT, strings.get(0).getId());
}

@ParameterizedTest
@ExcelFormatSource
void readsUppercaseAndBlankCells(ExcelFormat format) throws Exception {
File file = createTempFile(format);
StringData first = new StringData();
first.setId(TEXT.toUpperCase(Locale.ROOT));
first.setLabel("uppercase");
StringData second = new StringData();
second.setId("");
second.setLabel("blank UUID");
RoundTripHelper.write(file, StringData.class, Arrays.asList(first, second));
List<UUIDData> rows = RoundTripHelper.read(file, UUIDData.class);
Assertions.assertEquals(2, rows.size());
Assertions.assertEquals(UUID.fromString(TEXT), rows.get(0).getId());
Assertions.assertNull(rows.get(1).getId());
}

@ParameterizedTest
@ExcelFormatSource
void reportsInvalidCellAsConversionFailure(ExcelFormat format) throws Exception {
File file = createTempFile(format);
StringData row = new StringData();
row.setId("not-a-uuid");
row.setLabel("invalid");
RoundTripHelper.write(file, StringData.class, Collections.singletonList(row));
ExcelDataConvertException error = Assertions.assertThrows(
ExcelDataConvertException.class, () -> RoundTripHelper.read(file, UUIDData.class));
Assertions.assertInstanceOf(IllegalArgumentException.class, error.getCause());
}

@Data
public static class UUIDData {
private UUID id;
private String label;
}

@Data
public static class StringData {
private String id;
private String label;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.fesod.sheet.converter;

import java.util.Locale;
import java.util.UUID;
import org.apache.fesod.sheet.converters.uuid.UUIDStringConverter;
import org.apache.fesod.sheet.enums.CellDataTypeEnum;
import org.apache.fesod.sheet.metadata.GlobalConfiguration;
import org.apache.fesod.sheet.metadata.data.ReadCellData;
import org.apache.fesod.sheet.metadata.data.WriteCellData;
import org.apache.fesod.sheet.testkit.Tags;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource;

@Tag(Tags.UNIT)
class UUIDConverterTest {

private static final String TEXT = "123e4567-e89b-12d3-a456-426614174000";
private final UUIDStringConverter converter = new UUIDStringConverter();
private final GlobalConfiguration configuration = new GlobalConfiguration();

@Test
void supportsUuidStrings() {
Assertions.assertEquals(UUID.class, converter.supportJavaTypeKey());
Assertions.assertEquals(CellDataTypeEnum.STRING, converter.supportExcelTypeKey());
}

@ParameterizedTest
@ValueSource(
strings = {
TEXT,
"123E4567-E89B-12D3-A456-426614174000",
"00000000-0000-0000-0000-000000000000",
"ffffffff-ffff-ffff-ffff-ffffffffffff"
})
void readsAndWritesCanonicalStrings(String input) {
UUID value = converter.convertToJavaData(new ReadCellData<>(input), null, configuration);
WriteCellData<?> cell = converter.convertToExcelData(value, null, configuration);
Assertions.assertEquals(CellDataTypeEnum.STRING, cell.getType());
Assertions.assertEquals(input.toLowerCase(Locale.ROOT), cell.getStringValue());
}

@ParameterizedTest
@ValueSource(
strings = {
"not-a-uuid",
"123e4567-e89b-12d3-a456-42661417400g",
"123e4567e89b12d3a456426614174000",
" not-a-uuid\t",
"123e4567-e89b-12d3-a456-42661417 4000"
})
void rejectsInvalidInput(String input) {
Assertions.assertThrows(
IllegalArgumentException.class,
() -> converter.convertToJavaData(new ReadCellData<>(input), null, configuration));
}

@ParameterizedTest
@NullSource
@ValueSource(strings = {"", " ", "\t\r\n", "\u2003"})
void readsBlankStringAsMissingValue(String input) {
ReadCellData<String> cell = new ReadCellData<>();
cell.setStringValue(input);
Assertions.assertNull(converter.convertToJavaData(cell, null, configuration));
}

@ParameterizedTest
@ValueSource(strings = {" " + TEXT, TEXT + " ", "\t" + TEXT + "\r\n"})
void trimsWhitespaceBeforeParsing(String input) {
Assertions.assertEquals(
UUID.fromString(TEXT), converter.convertToJavaData(new ReadCellData<>(input), null, configuration));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@
import java.sql.Time;
import java.time.LocalTime;
import java.util.Map;
import java.util.UUID;
import org.apache.fesod.sheet.converters.ConverterKeyBuild.ConverterKey;
import org.apache.fesod.sheet.converters.localtime.LocalTimeDateConverter;
import org.apache.fesod.sheet.converters.localtime.LocalTimeNumberConverter;
import org.apache.fesod.sheet.converters.localtime.LocalTimeStringConverter;
import org.apache.fesod.sheet.converters.sqltime.SqlTimeDateConverter;
import org.apache.fesod.sheet.converters.sqltime.SqlTimeNumberConverter;
import org.apache.fesod.sheet.converters.sqltime.SqlTimeStringConverter;
import org.apache.fesod.sheet.converters.uuid.UUIDStringConverter;
import org.apache.fesod.sheet.enums.CellDataTypeEnum;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -71,6 +73,20 @@ void loadConvertersRegistersLocalTimeFamily() {
writeConverter.get(ConverterKeyBuild.buildKey(LocalTime.class, CellDataTypeEnum.STRING)));
}

@Test
void loadConvertersRegistersUUIDFamily() {
Assertions.assertInstanceOf(
UUIDStringConverter.class,
DefaultConverterLoader.loadDefaultReadConverter()
.get(ConverterKeyBuild.buildKey(UUID.class, CellDataTypeEnum.STRING)));
Map<ConverterKey, Converter<?>> writeConverter = DefaultConverterLoader.loadDefaultWriteConverter();
Assertions.assertInstanceOf(
UUIDStringConverter.class, writeConverter.get(ConverterKeyBuild.buildKey(UUID.class)));
Assertions.assertInstanceOf(
UUIDStringConverter.class,
writeConverter.get(ConverterKeyBuild.buildKey(UUID.class, CellDataTypeEnum.STRING)));
}

@Test
void loadConvertersRegistersSqlTimeFamily() {
Map<ConverterKey, Converter<?>> allConverter = DefaultConverterLoader.loadAllConverter();
Expand Down
Loading