Skip to content

Commit

Permalink
ExcelRow.autoFill: caching of reflection fields implemented for faste…
Browse files Browse the repository at this point in the history
…r exports.
  • Loading branch information
kreinhard committed May 9, 2021
1 parent 6d17f1f commit 9b614b3
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 25 deletions.
28 changes: 14 additions & 14 deletions merlin-core/README.adoc
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
Merlin technical documentation
==============================
= Merlin technical documentation
Micromata GmbH, Kai Reinhard, Version {version}
:toc:
:toclevels: 4
Expand All @@ -12,11 +11,11 @@ link:index{outfilesuffix}[Top]
:sectnums:


= Merlin Core library
== Merlin Core library

This is Merlin's core library used by all other modules of Merlin.

== Functionality
=== Functionality
Merlin core contains the base library supporting:

- Reading Excel files with validation of the data (xls and xlsx).
Expand All @@ -29,8 +28,8 @@ Merlin core contains the base library supporting:
substitution is as well supported as conditionals (equals, greater-less etc.)
for having dynamic content in the result word file. Support of serial letters.

== Excel validation
=== Reading and validating Excel files
=== Excel validation
==== Reading and validating Excel files

Refer e. g. TemplateDefinitionExcelReader as a full example of
reading and validating an excel file:
Expand Down Expand Up @@ -61,7 +60,7 @@ reading and validating an excel file:
}
----

=== Mark validation errors
==== Mark validation errors
Refer e. g. ExcelWorkbookTest for returning Excel files with marked validation
errors:

Expand Down Expand Up @@ -89,8 +88,8 @@ errors:
sheet.markErrors(ctx);
----

== Templating
=== Glossar
=== Templating
==== Glossar

- Templates (or template files) are Word files containing variables and conditionals such as
====
Expand Down Expand Up @@ -137,8 +136,8 @@ Here is my text.
- Please note: Comments containing hyperlinks aren't supported.


=== Running a template
==== Single run
==== Running a template
===== Single run
You may run a template with the following settings:

- Customized variables (gender, name of receiver, etc.)
Expand All @@ -155,7 +154,7 @@ You may run a template with the following settings:
* Checking the correct type of the user's input: texts, selections, boolean, numbers (including optional min an max values), etc.
* Flags such as required variables or unique flag (useful for the serial letter functionality).

==== Serial letter run
===== Serial letter run
You may run a template with the following settings:
- Serial definition file containg variables. The template and template definition file may be specified
inside this definition file (Excel), each set of variables per row.
Expand All @@ -166,7 +165,7 @@ You may run a template with the following settings:
* in the template file itself.
- Please notice: A template definition file is optional (see above).

=== Template files
==== Template files
Template files (docx) may contain following expressions supported by Merlin:

[%autowidth, frame="topbot",options="header"]
Expand Down Expand Up @@ -222,7 +221,8 @@ http://pool.sks-keyservers.net:11371 and http://keys.gnupg.net:11371/

=== Building

Simply run `gradle clean publish`.
* Simply run `gradle clean publish` for publishing to Sonatype (uncomment block in `gradle.build` before).
* For local publishing use `gradle publishToMavenLocal`.

=== Staging a release

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ object BeanUtils {
fun getValue(src: Any, property: String): Any? {
val srcClazz = src.javaClass
val srcField = getDeclaredField(srcClazz, property) ?: return null
srcField.isAccessible = true
return srcField.get(src)
return getValue(src, srcField)
}

fun getValue(src: Any, field: Field): Any? {
field.isAccessible = true
return field.get(src)
}

/**
Expand All @@ -35,7 +39,7 @@ object BeanUtils {
* @param fieldnames
* @return the field or null
*/
private fun getDeclaredField(clazz: Class<*>, fieldname: String): Field? {
fun getDeclaredField(clazz: Class<*>, fieldname: String): Field? {
val fields: Array<Field> = getAllDeclaredFields(clazz)
for (f in fields) {
if (f.name == fieldname) {
Expand Down
41 changes: 33 additions & 8 deletions merlin-core/src/main/kotlin/de/micromata/merlin/excel/ExcelRow.kt
Original file line number Diff line number Diff line change
Expand Up @@ -181,18 +181,30 @@ class ExcelRow(val sheet: ExcelSheet, val row: Row) {
) {
obj ?: return
for (colDef in sheet.columnDefinitions) {
var value = getPropertyValue(obj, colDef.columnHeadname, ignoreProperties)
if (value != null) {
processPropertyValue(obj, value, colDef, process)
if (sheet.cache.autoFillCache.notFoundFieldsSet.contains(colDef)) {
// Don't search multiple times, if no method was found.
continue
}
for (alias in colDef.columnAliases) {
value = getPropertyValue(obj, alias.decapitalize(), ignoreProperties)
if (value != null) {
var pair = getPropertyValue(obj, colDef, colDef.columnHeadname, ignoreProperties)
if (pair != null) {
pair.second?.let { value ->
processPropertyValue(obj, value, colDef, process)
}
continue
}
for (alias in colDef.columnAliases) {
pair = getPropertyValue(obj, colDef, alias.decapitalize(), ignoreProperties)
if (pair != null) {
pair.second?.let { value ->
processPropertyValue(obj, value, colDef, process)
}
break
}
}
if (pair == null) {
// No bean property found for this column definition.
sheet.cache.autoFillCache.notFoundFieldsSet.add(colDef)
}
}
}

Expand All @@ -208,12 +220,25 @@ class ExcelRow(val sheet: ExcelSheet, val row: Row) {
}
}

private fun getPropertyValue(obj: Any, identifier: String?, ignoreProperties: Array<out String>): Any? {
private fun getPropertyValue(
obj: Any,
colDef: ExcelColumnDef,
identifier: String?,
ignoreProperties: Array<out String>
): Pair<Boolean, Any?>? {
identifier ?: return null
if (ignoreProperties.any { identifier.compareTo(it, ignoreCase = true) == 0 }) {
return null
}
return BeanUtils.getValue(obj, identifier.decapitalize())
sheet.cache.autoFillCache.foundFieldsMap[colDef]?.let {
return Pair(true, BeanUtils.getValue(obj, it))
}
val field = BeanUtils.getDeclaredField(obj::class.java, identifier.decapitalize())
if (field == null) {
return null
}
sheet.cache.autoFillCache.foundFieldsMap[colDef] = field
return Pair(true, BeanUtils.getValue(obj, field))
}

var heightInPoints: Float
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class ExcelSheet internal constructor(val excelWorkbook: ExcelWorkbook, val poiS
val columnDefinitions
get() = columnDefList.toList()

internal val cache = WorkingCache()

init {
log.debug("Reading sheet '" + poiSheet.sheetName + "'")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package de.micromata.merlin.excel

import java.lang.reflect.Field

/**
* Used for caching some stuff for better performance especially for large sheets to create.
*/
internal class WorkingCache {
internal class AutoFillCache{
val foundFieldsMap = mutableMapOf<ExcelColumnDef, Field>()
val notFoundFieldsSet = mutableSetOf<ExcelColumnDef>()
}
internal val autoFillCache = AutoFillCache()
}

0 comments on commit 9b614b3

Please sign in to comment.