Skip to content

Commit

Permalink
Merge pull request #65 from millij/boolean-support
Browse files Browse the repository at this point in the history
Added support for Bean property types :: Boolean and Enums
  • Loading branch information
millij authored Jul 22, 2024
2 parents f6d3ee6 + 3c165cb commit ef7ed01
Show file tree
Hide file tree
Showing 10 changed files with 402 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
import io.github.millij.poi.util.Spreadsheet;


/**
* SheetContentsHandler impl for reading row as {@link Map}
*
* @since 3.1.0
*/
public class RowContentsAsMapHandler extends AbstractSheetContentsHandler {

private static final Logger LOGGER = LoggerFactory.getLogger(RowContentsAsMapHandler.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import io.github.millij.poi.ss.model.Column;
import io.github.millij.poi.util.Spreadsheet;
import io.github.millij.poi.util.Strings;


public class RowContentsHandler<T> extends AbstractSheetContentsHandler {
Expand Down Expand Up @@ -114,7 +115,7 @@ private Map<String, String> asHeaderNameToCellRefMap(final Map<String, Object> h
final Object header = headerRowData.get(colRef);

final String headerName = Objects.isNull(header) ? "" : String.valueOf(header);
final String normalHeaderName = Spreadsheet.normalize(headerName);
final String normalHeaderName = Strings.normalize(headerName);
headerCellRefs.put(normalHeaderName, colRef);
}

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/io/github/millij/poi/ss/reader/XlsReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.github.millij.poi.ss.handler.RowListener;
import io.github.millij.poi.ss.model.Column;
import io.github.millij.poi.util.Spreadsheet;
import io.github.millij.poi.util.Strings;


/**
Expand Down Expand Up @@ -240,7 +241,7 @@ private Map<String, String> asHeaderNameToCellRefMap(final HSSFRow headerRow, fi
final Object header = this.getCellValue(cell);

final String rawHeaderName = Objects.isNull(header) ? "" : String.valueOf(header);
final String headerName = normalizeHeaderName ? Spreadsheet.normalize(rawHeaderName) : rawHeaderName;
final String headerName = normalizeHeaderName ? Strings.normalize(rawHeaderName) : rawHeaderName;
headerCellRefs.put(headerName, cellColRef);
}

Expand Down
168 changes: 105 additions & 63 deletions src/main/java/io/github/millij/poi/util/Beans.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,21 @@
*/
public final class Beans {

private static final Logger LOGGER = LoggerFactory.getLogger(Spreadsheet.class);
private static final Logger LOGGER = LoggerFactory.getLogger(Beans.class);

private Beans() {
super();
// Utility Class
}


//
// Constants

private static final PropertyUtilsBean PROP_UTILS_BEAN = new PropertyUtilsBean();
private static final ConvertUtilsBean CONVERT_UTILS_BEAN = new ConvertUtilsBean();


// Static Utilities
// ------------------------------------------------------------------------

Expand Down Expand Up @@ -68,7 +75,7 @@ public static String getFieldName(final Method method) {
*/
public static String getFieldValueAsString(final Object beanObj, final String fieldName) throws Exception {
// Property Descriptor
final PropertyDescriptor pd = new PropertyDescriptor(fieldName, beanObj.getClass());
final PropertyDescriptor pd = PROP_UTILS_BEAN.getPropertyDescriptor(beanObj, fieldName);
final Method getterMtd = pd.getReadMethod();

final Object value = getterMtd.invoke(beanObj);
Expand Down Expand Up @@ -102,101 +109,136 @@ public static boolean isInstantiableType(final Class<?> clz) {
}


// Set Property
// Bean Property :: Get
// ------------------------------------------------------------------------

private static final PropertyUtilsBean PROP_UTILS_BEAN = new PropertyUtilsBean();
private static final ConvertUtilsBean CONVERT_UTILS_BEAN = new ConvertUtilsBean();
public static Object getProperty(final Object bean, final String propName) throws Exception {
final Object value = PROP_UTILS_BEAN.getSimpleProperty(bean, propName);
return value;
}


// Bean Property :: Set
// ------------------------------------------------------------------------

/**
*/
public static void setProperty(final Object target, final String propName, final Class<?> propType,
final Object propValue) throws Exception {
// Sanity checks
if (Objects.isNull(propValue)) {
return; // Skip Setter if property value is NULL
}

try {
// Convert the specified value to the required type
final Object newValue;
if (propValue instanceof String) {
newValue = CONVERT_UTILS_BEAN.convert((String) propValue, propType);
} else {
final Converter converter = CONVERT_UTILS_BEAN.lookup(propType);
if (converter != null) {
newValue = converter.convert(propType, propValue);
} else {
newValue = propValue;
}
}

// Invoke the setter method
PROP_UTILS_BEAN.setProperty(target, propName, newValue);

} catch (Exception ex) {
//
}
}


/**
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static void setProperty(final Object target, final String propName, final Object propValue,
final String format, final DateTimeType dateTimeType) throws Exception {
// Sanity checks
if (Objects.isNull(propValue)) {
return; // Skip Setter if property value is NULL
}

// Calculate the property type
final PropertyDescriptor descriptor = PROP_UTILS_BEAN.getPropertyDescriptor(target, propName);
if (descriptor == null || descriptor.getWriteMethod() == null) {
if (Objects.isNull(descriptor) || Objects.isNull(descriptor.getWriteMethod())) {
return; // Skip this property setter
}

// Property Type
final Class<?> type = descriptor.getPropertyType();
final Class<?> propType = descriptor.getPropertyType();

// Check PropValue (expected non-null and string)
if (Objects.isNull(propValue) || !(propValue instanceof String)) {
setProperty(target, propName, type, propValue);
//
// Handle Date/Time/Duration Cases
if (!DateTimeType.NONE.equals(dateTimeType) || propType.equals(Date.class)) {
setDateTimeProperty(target, propName, propType, propValue, format, dateTimeType);
return;
}

//
// Handle Date/Time/Duration Cases
// Handle ENUM
if (propType.isEnum() && (propValue instanceof String)) {
final String cleanEnumStr = Strings.normalize((String) propValue).toUpperCase();
final Enum<?> enumValue = Enum.valueOf((Class<? extends Enum>) propType, cleanEnumStr);
setProperty(target, propName, propType, enumValue);
return;
}

// Check if its a Date time property
if (!type.equals(Date.class) && DateTimeType.NONE.equals(dateTimeType)) {
setProperty(target, propName, type, propValue);
//
// Handle Boolean
if (propType.equals(Boolean.class) && (propValue instanceof String)) {
// Cleanup Boolean String
final String cleanBoolStr = Strings.normalize((String) propValue); // for cases like "FALSE()", "TRUE()"
setProperty(target, propName, propType, cleanBoolStr);
return;
}

//
// Default Handling (for all other Types)
setProperty(target, propName, propType, propValue);

}


/**
* Set the Date/Time property of the Target Bean.
*/
private static void setDateTimeProperty(final Object target, final String propName, final Class<?> propType,
final Object propValue, final String format, final DateTimeType dateTimeType) throws Exception {
// Input value Format
final String dateFormatStr = Objects.isNull(format) || format.isBlank() ? "dd/MM/yyyy" : format;

// Parse
final SimpleDateFormat dateFmt = new SimpleDateFormat(dateFormatStr);
final Date dateValue = dateFmt.parse((String) propValue);

// check if the PropType is Date
if (type.equals(Date.class)) {
setProperty(target, propName, type, dateValue);
return;
}

// Convert to Long
final Long longValue;
if (DateTimeType.DURATION.equals(dateTimeType)) {
final Calendar calendar = Calendar.getInstance();
calendar.setTime(dateValue);
final long durationMillis = calendar.get(HOUR_OF_DAY) * 3600_000 + //
calendar.get(MINUTE) * 60_000 + //
calendar.get(SECOND) * 1_000;
// Check if the PropType is Date
if (propType.equals(Date.class)) {
setProperty(target, propName, propType, dateValue);

longValue = durationMillis;
} else {
longValue = dateValue.getTime();
}

setProperty(target, propName, type, longValue);
return;
}

/**
*/
public static void setProperty(final Object target, final String propName, final Class<?> type,
final Object propValue) throws Exception {
try {
// Convert the specified value to the required type
final Object newValue;
if (propValue instanceof String) {
newValue = CONVERT_UTILS_BEAN.convert((String) propValue, type);
// Convert to Long
final Long longValue;
if (DateTimeType.DURATION.equals(dateTimeType)) {
final Calendar calendar = Calendar.getInstance();
calendar.setTime(dateValue);
final long durationMillis = calendar.get(HOUR_OF_DAY) * 3600_000 + //
calendar.get(MINUTE) * 60_000 + //
calendar.get(SECOND) * 1_000;

longValue = durationMillis;
} else {
final Converter converter = CONVERT_UTILS_BEAN.lookup(type);
if (converter != null) {
newValue = converter.convert(type, propValue);
} else {
newValue = propValue;
}
longValue = dateValue.getTime();
}

// Invoke the setter method
PROP_UTILS_BEAN.setProperty(target, propName, newValue);

} catch (Exception ex) {
//
setProperty(target, propName, propType, longValue);
}
}

/**
*/
public static Object getProperty(final Object bean, final String propName) throws Exception {
final Object value = PROP_UTILS_BEAN.getSimpleProperty(bean, propName);
return value;
//
}


}
23 changes: 9 additions & 14 deletions src/main/java/io/github/millij/poi/util/Spreadsheet.java
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ public static <T> T rowAsBean(Class<T> beanClz, Map<String, Column> propColumnMa
final String propColName = propColDef.getName();

// Get the Header Cell Ref
final String normalizedColName = Spreadsheet.normalize(propColName);
final String normalizedColName = Strings.normalize(propColName);
final String propCellRef = headerCellRefsMap.get(normalizedColName);
if (Objects.isNull(propCellRef) || propCellRef.isBlank()) {
LOGGER.debug("{} :: No Cell Ref found [Prop - Col] : [{} - {}]", beanClz, propName, propColName);
Expand All @@ -278,8 +278,9 @@ public static <T> T rowAsBean(Class<T> beanClz, Map<String, Column> propColumnMa
// Set the property value in the current row object bean
Beans.setProperty(bean, propName, propValue, dataFormat, datetimeType);
} catch (Exception ex) {
String errMsg = String.format("Failed to set bean property - %s, value - %s", propName, propValue);
LOGGER.error(errMsg, ex);
String exMsg = ex.getMessage();
String errMsg = String.format("Error setting prop - %s, val - %s : %s", propName, propValue, exMsg);
LOGGER.error(errMsg);
}

}
Expand All @@ -303,7 +304,7 @@ private static boolean validateRowData(final Map<String, Object> rowDataMap,
// Prop Column Definition
final Column propColDef = propColumnMap.get(propName);
final String propColName = propColDef.getName();
final String normalizedColName = Spreadsheet.normalize(propColName);
final String normalizedColName = Strings.normalize(propColName);

// Get the Header Cell Ref
final String propCellRef = headerCellRefsMap.containsKey(propColName) //
Expand Down Expand Up @@ -334,18 +335,12 @@ private static boolean validateRowData(final Map<String, Object> rowDataMap,

/**
* Normalize the string. typically used for case-insensitive comparison.
*
* @deprecated in favor of {@link Strings#normalize(String)}
*/
@Deprecated
public static String normalize(final String inStr) {
// Sanity checks
if (Objects.isNull(inStr)) {
return "";
}

// Special characters
final String cleanStr = inStr.replaceAll("–", " ").replaceAll("[-\\[\\]/{}:.,;#%=()*+?\\^$|<>&\"\'\\\\]", " ");
final String normalizedStr = cleanStr.toLowerCase().trim().replaceAll("\\s+", "_");

return normalizedStr;
return Strings.normalize(inStr);
}


Expand Down
55 changes: 55 additions & 0 deletions src/main/java/io/github/millij/poi/util/Strings.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.github.millij.poi.util;

import java.util.Objects;


/**
* Odd ball String Utilities
*
* @since 3.1.0
*/
public final class Strings {

private Strings() {
super();
// Utility Class
}


// Util Methods
// ------------------------------------------------------------------------

/**
* Normalize the string by removing unwanted characters from the String.
*
* @param inStr Input String
* @param replacement Replacement character for the unwanted characters
*
* @return Clean / Normalized String
*/
public static String normalize(final String inStr, final String replacement) {
// Sanity checks
if (Objects.isNull(inStr)) {
return "";
}

// Special characters
final String cleanStr = inStr.replaceAll("–", " ").replaceAll("[-\\[\\]/{}:.,;#%=()*+?\\^$|<>&\"\'\\\\]", " ");
final String normalizedStr = cleanStr.toLowerCase().trim().replaceAll("\\s+", replacement);

return normalizedStr;
}

/**
* Normalize the string by replacing unwanted characters from the String with "_".
*
* @param inStr Input String
*
* @return Clean / Normalized String
*/
public static String normalize(final String inStr) {
return normalize(inStr, "_");
}


}
Loading

0 comments on commit ef7ed01

Please sign in to comment.