diff --git a/build.gradle.kts b/build.gradle.kts index 960213a3e..43f48a032 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,10 +15,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -import org.kopi.galite.gradle._java -import org.kopi.galite.gradle._publishing -import org.kopi.galite.gradle.configureMavenCentralPom -import org.kopi.galite.gradle.signPublication +import org.kopi.galite.gradle.* plugins { id("org.jetbrains.kotlin.jvm") version "1.6.10" apply false @@ -43,6 +40,9 @@ subprojects { maven { url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") } + maven { + url = uri("https://mvnrepository.com/artifact/org.vaadin.addons") + } } dependencies { diff --git a/buildSrc/src/main/kotlin/org/kopi/galite/gradle/Versions.kt b/buildSrc/src/main/kotlin/org/kopi/galite/gradle/Versions.kt index 87c7f8edd..497718256 100644 --- a/buildSrc/src/main/kotlin/org/kopi/galite/gradle/Versions.kt +++ b/buildSrc/src/main/kotlin/org/kopi/galite/gradle/Versions.kt @@ -41,4 +41,5 @@ object Versions { const val JFREE_CHART = "1.0.19" const val WYSIWYG_EJAVA = "2.0.1" const val KOTLINX_DATAFRAME = "0.8.0-rc-7" + const val PIVOT_TABLE = "2.0.0" } diff --git a/galite-core/build.gradle.kts b/galite-core/build.gradle.kts index ff9179c39..4f2ce38f4 100644 --- a/galite-core/build.gradle.kts +++ b/galite-core/build.gradle.kts @@ -82,6 +82,9 @@ dependencies { // Javax Activation dependency implementation("javax.activation", "activation", Versions.JAVAX_ACTIVATION) + + // Pivot Table dependency + implementation("org.vaadin.addons.componentfactory", "pivottable-flow", Versions.PIVOT_TABLE) } dependencyManagement { diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/domain/Domain.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/domain/Domain.kt index 091b8b533..c12200c2e 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/domain/Domain.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/domain/Domain.kt @@ -27,54 +27,21 @@ import java.time.LocalTime import kotlin.reflect.KClass import org.joda.time.DateTime -import org.kopi.galite.visual.chart.VBooleanDimension -import org.kopi.galite.visual.chart.VColumnFormat -import org.kopi.galite.visual.chart.VDateDimension -import org.kopi.galite.visual.chart.VDimension -import org.kopi.galite.visual.chart.VDecimalDimension -import org.kopi.galite.visual.chart.VDecimalMeasure -import org.kopi.galite.visual.chart.VIntegerDimension -import org.kopi.galite.visual.chart.VIntegerMeasure -import org.kopi.galite.visual.chart.VMeasure -import org.kopi.galite.visual.chart.VMonthDimension -import org.kopi.galite.visual.chart.VStringDimension -import org.kopi.galite.visual.chart.VTimeDimension -import org.kopi.galite.visual.chart.VTimestampDimension -import org.kopi.galite.visual.chart.VWeekDimension +import org.kopi.galite.type.Image +import org.kopi.galite.type.Month +import org.kopi.galite.type.Week +import org.kopi.galite.visual.VColor +import org.kopi.galite.visual.chart.* import org.kopi.galite.visual.dsl.chart.ChartDimension import org.kopi.galite.visual.dsl.chart.ChartMeasure import org.kopi.galite.visual.dsl.common.LocalizationWriter import org.kopi.galite.visual.dsl.form.FormField import org.kopi.galite.visual.dsl.report.ReportField -import org.kopi.galite.visual.form.VBooleanField -import org.kopi.galite.visual.form.VConstants -import org.kopi.galite.visual.form.VDateField -import org.kopi.galite.visual.form.VField -import org.kopi.galite.visual.form.VDecimalField -import org.kopi.galite.visual.form.VImageField -import org.kopi.galite.visual.form.VIntegerField -import org.kopi.galite.visual.form.VMonthField -import org.kopi.galite.visual.form.VStringField -import org.kopi.galite.visual.form.VTextField -import org.kopi.galite.visual.form.VTimeField -import org.kopi.galite.visual.form.VTimestampField -import org.kopi.galite.visual.form.VWeekField -import org.kopi.galite.visual.report.VBooleanColumn -import org.kopi.galite.visual.report.VCalculateColumn -import org.kopi.galite.visual.report.VCellFormat -import org.kopi.galite.visual.report.VDateColumn -import org.kopi.galite.visual.report.VDecimalColumn -import org.kopi.galite.visual.report.VIntegerColumn -import org.kopi.galite.visual.report.VMonthColumn -import org.kopi.galite.visual.report.VReportColumn -import org.kopi.galite.visual.report.VStringColumn -import org.kopi.galite.visual.report.VTimeColumn -import org.kopi.galite.visual.report.VTimestampColumn -import org.kopi.galite.visual.report.VWeekColumn -import org.kopi.galite.type.Image -import org.kopi.galite.type.Month -import org.kopi.galite.type.Week -import org.kopi.galite.visual.VColor +import org.kopi.galite.visual.form.* +import org.kopi.galite.visual.report.* +import org.kopi.galite.visual.dsl.pivottable.Dimension +import org.kopi.galite.visual.dsl.pivottable.PivotTableField +import org.kopi.galite.visual.pivottable.VPivotTableColumn /** * A domain is a data type with predefined list of allowed values. @@ -274,6 +241,22 @@ open class Domain(val width: Int? = null, } } + /** + * Builds the pivot table column model + */ + open fun buildPivotTableFieldModel(field: PivotTableField<*>, position: Dimension.Position?): VPivotTableColumn { + return with(field) { + when (kClass) { + Int::class, Long::class, String::class, BigDecimal::class, Boolean::class, org.joda.time.LocalDate::class, + LocalDate::class, java.sql.Date::class, java.util.Date::class, Month::class, Week::class, org.joda.time.LocalTime::class, + LocalTime::class, Instant::class, LocalDateTime::class, DateTime::class -> + VPivotTableColumn(ident, position) + + else -> throw java.lang.RuntimeException("Type ${kClass!!.qualifiedName} is not supported") + } + } + } + /** * Returns the default alignment */ diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/form/DictionaryForm.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/form/DictionaryForm.kt index c26e6f74a..c28a89363 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/form/DictionaryForm.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/form/DictionaryForm.kt @@ -21,6 +21,7 @@ import java.util.Locale import org.kopi.galite.visual.ApplicationContext import org.kopi.galite.visual.VException import org.kopi.galite.visual.cross.VDynamicReport +import org.kopi.galite.visual.dsl.pivottable.PivotTable import org.kopi.galite.visual.dsl.report.Report import org.kopi.galite.visual.form.VDictionaryForm @@ -123,6 +124,16 @@ abstract class DictionaryForm(title: String, locale: Locale? = null) : Form(titl VDynamicReport.createDynamicReport(this.block) } + /** + * create a pivot table for this form + */ + + protected fun Block.createPivotTable(reportbuilder: () -> PivotTable) { + model.createPivotTable(block) { + reportbuilder().model + } + } + // ---------------------------------------------------------------------- // DICTIONARY FORM MODEL // ---------------------------------------------------------------------- diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/form/Form.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/form/Form.kt index 5101cab45..3858cac67 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/form/Form.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/form/Form.kt @@ -370,6 +370,7 @@ abstract class Form(title: String, locale: Locale? = null) : Window(title, local save delete dynamicReport + pivotTable help showHideFilter report @@ -451,6 +452,8 @@ abstract class Form(title: String, locale: Locale? = null) : Window(title, local open val dynamicReport by lazy { actor(CreateDynamicReport()) } + open val pivotTable by lazy { actor(CreatePivotTable()) } + open val showHideFilter by lazy { actor(ShowHideFilter()) } open val help by lazy { actor(Help()) } @@ -713,6 +716,13 @@ abstract class Form(title: String, locale: Locale? = null) : Window(title, local } } + class CreatePivotTable : Actor(menu = ActionMenu(), label = "Pivot Table", help = "Create pivot table.", userActor = false) { + init { + key = Key.F10 + icon = Icon.REPORT + } + } + class ShowHideFilter : Actor(menu = ActionMenu(), label = "Show/Hide filter", help = "Show or hide block filters.", userActor = false) { init { key = Key.SHIFT_F12 diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/Dimension.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/Dimension.kt new file mode 100644 index 000000000..dda64655d --- /dev/null +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/Dimension.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.kopi.galite.visual.dsl.pivottable + +import org.kopi.galite.visual.domain.Domain +import org.kopi.galite.visual.dsl.common.LocalizationWriter +import org.kopi.galite.visual.pivottable.Constants +import org.kopi.galite.visual.pivottable.VPivotTableColumn + +class Dimension(override val domain: Domain, + val init: Dimension.() -> Unit, + ident: String? = null, + val position: Position, + override val source: String?) : PivotTableField(domain, ident) { + + fun initField() { + init() + } + + lateinit var model: VPivotTableColumn + + fun buildPivotTableColumn(): VPivotTableColumn { + model = domain.buildPivotTableFieldModel(this, position).also { column -> + column.label = label ?: "" + } + + return model + } + + // ---------------------------------------------------------------------- + // XML LOCALIZATION GENERATION + // ---------------------------------------------------------------------- + /** + * Generates localization for the field in the xml file + */ + override fun genLocalization(writer: LocalizationWriter) { + (writer as PivotTableLocalizationWriter).genField(ident, label, help) + } + + // ---------------------------------------------------------------------- + // POSITION CLASS + // ---------------------------------------------------------------------- + + enum class Position(val value: Int) { + NONE(Constants.DIMENSION_NO_POSITION), + ROW(Constants.DIMENSION_ROW), + COLUMN(Constants.DIMENSION_COLUMN) + } +} diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/Measure.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/Measure.kt new file mode 100644 index 000000000..049d34e25 --- /dev/null +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/Measure.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.kopi.galite.visual.dsl.pivottable + +import org.kopi.galite.visual.domain.Domain +import org.kopi.galite.visual.dsl.common.LocalizationWriter +import org.kopi.galite.visual.pivottable.VPivotTableColumn + +class Measure(override val domain: Domain, + val init: Measure.() -> Unit, + ident: String? = null, + override val source: String?) : PivotTableField(domain, ident) { + + fun initField() { + init() + } + + lateinit var model: VPivotTableColumn + + fun buildPivotTableColumn(): VPivotTableColumn { + model = domain.buildPivotTableFieldModel(this, Dimension.Position.NONE).also { column -> + column.label = label ?: "" + } + + return model + } + + // ---------------------------------------------------------------------- + // XML LOCALIZATION GENERATION + // ---------------------------------------------------------------------- + /** + * Generates localization for the field in the xml file + */ + override fun genLocalization(writer: LocalizationWriter) { + (writer as PivotTableLocalizationWriter).genField(ident, label, help) + } +} diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/PivotTable.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/PivotTable.kt new file mode 100644 index 000000000..246cd760d --- /dev/null +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/PivotTable.kt @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.kopi.galite.visual.dsl.pivottable + +import java.io.IOException +import java.util.Locale + +import org.kopi.galite.visual.ApplicationContext +import org.kopi.galite.visual.domain.Domain +import org.kopi.galite.visual.dsl.common.* +import org.kopi.galite.visual.form.VConstants +import org.kopi.galite.visual.pivottable.Constants +import org.kopi.galite.visual.pivottable.VPivotTable + +abstract class PivotTable(title: String, val help: String?, locale: Locale? = null) : Window(title, locale) { + + constructor(title: String, locale: Locale? = null) : this(title, null, locale) + + /** Pivot table's fields. */ + val dimensions = mutableListOf>() + val measures = mutableListOf>() + + /** Pivot table's data rows. */ + val rows = mutableListOf() + + + inline fun ?> dimension(domain: Domain, + position: Dimension.Position, + noinline init: Dimension.() -> Unit): Dimension { + domain.kClass = T::class + val dimension = Dimension(domain, init, "DIMENSION_${dimensions.size}", position, domain.source.ifEmpty { `access$sourceFile` }) + dimension.init() + model.model.columns.add(dimension.buildPivotTableColumn()) + dimensions.add(dimension) + + return dimension + } + + inline fun ?> measure(domain: Domain, + noinline init: Measure.() -> Unit): Measure { + domain.kClass = T::class + val measure = Measure(domain, init, "MEASURE_${measures.size}", domain.source.ifEmpty { `access$sourceFile` }) + measure.init() + model.model.columns.add(measure.buildPivotTableColumn()) + measures.add(measure) + + return measure + } + + /** + * Adds a row to the Pivot table. + * + * @param init initializes the row with values. + */ + fun add(init: PivotTableRow.() -> Unit) { + val row = PivotTableRow((dimensions + measures).toMutableList()) + row.init() + + val list = row.addLine() + model.model.addLine(list.toTypedArray()) + + rows.add(row) + } + + private fun PivotTableRow.addLine(): List { + return (dimensions + measures).toMutableList().map { field -> + data[field] + } + } + + /** + * Adds triggers to this pivot table + * + * @param pivotTriggerEvents the trigger events to add + * @param method the method to execute when trigger is called + */ + fun trigger(vararg pivotTriggerEvents: PivotTableTriggerEvent, method: () -> T): Trigger { + val event = formEventList(pivotTriggerEvents) + val pivotTableAction = Action(null, method) + val trigger = FormTrigger(event, pivotTableAction) + + triggers.add(FormTrigger(event, Action(null, method))) + // PIVOT TABLE TRIGGERS + triggers.forEach { trigger -> + for (i in VConstants.TRG_TYPES.indices) { + if (trigger.events shr i and 1 > 0) { + model.PIVOT_TABLE_Triggers[0][i] = trigger + } + } + } + + return trigger + } + + private fun formEventList(PivotTableTriggerEvent: Array>): Long { + var self = 0L + + PivotTableTriggerEvent.forEach { trigger -> + self = self or (1L shl trigger.event) + } + + return self + } + + /** + * Returns the row's data. + * + * @param rowNumber the index of the desired row. + */ + fun getRow(rowNumber: Int): MutableMap, Any?> = rows[rowNumber].data + + /** + * Returns rows of data for a specific [field]. + * + * @param field the field. + */ + fun getRowsForField(field: PivotTableField<*>) = rows.map { it.data[field] } + + fun setMenu() { + model.setMenu() + } + + /////////////////////////////////////////////////////////////////////////// + // PIVOT TABLE TRIGGERS EVENTS + /////////////////////////////////////////////////////////////////////////// + + /** + * Pivot table Triggers + * + * @param event the event of the trigger + */ + open class PivotTableTriggerEvent(val event: Int) + + /** + * Executed at pivot table initialization. + */ + + val INIT = PivotTableTriggerEvent(Constants.TRG_INIT) + + // ---------------------------------------------------------------------- + // XML LOCALIZATION GENERATION + // ---------------------------------------------------------------------- + + override fun genLocalization(destination: String?, locale: Locale?) { + if (locale != null) { + val baseName = this::class.simpleName + requireNotNull(baseName) + val localizationDestination = destination + ?: (this.javaClass.classLoader.getResource("")?.path + + this.javaClass.`package`.name.replace(".", "/")) + try { + val writer = PivotTableLocalizationWriter() + genLocalization(writer) + writer.write(localizationDestination, baseName, locale) + } catch (ioe: IOException) { + ioe.printStackTrace() + System.err.println("cannot write : $baseName") + } + } + } + + fun genLocalization(writer: LocalizationWriter) { + (writer as PivotTableLocalizationWriter).genPivotTable(title, (dimensions + measures).toMutableList(), menus, actors) + } + + var defaultRenderer: String + get() = model.defaultRenderer + set(value) { + model.setDefaultRenderer(value) + } + + var aggregator: Pair + get() = model.aggregator + set(value) { + model.setAggregator(value) + } + + var disabledRerenders: MutableList + get() = model.disabledRerenders + set(value) { + model.setDisabledRerenders(value) + } + + var interactive: Int + get() = model.interactive + set(value) { + model.setInteractive(value) + } + + // ---------------------------------------------------------------------- + // Pivot table MODEL + // ---------------------------------------------------------------------- + override val model: VPivotTable = object : VPivotTable() { + + override fun init() { + dimensions.forEach { + it.initField() + } + measures.forEach { + it.initField() + } + } + + override val locale: Locale get() = this@PivotTable.locale ?: ApplicationContext.getDefaultLocale() + } + + init { + model.setTitle(title) + model.setPageTitle(title) + model.help = help + model.source = sourceFile + } + + @PublishedApi + internal val `access$sourceFile`: String + get() = sourceFile +} diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/PivotTableField.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/PivotTableField.kt new file mode 100644 index 000000000..0e9902f74 --- /dev/null +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/PivotTableField.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.kopi.galite.visual.dsl.pivottable + +import org.kopi.galite.visual.domain.Domain +import org.kopi.galite.visual.dsl.common.LocalizationWriter +import org.kopi.galite.visual.dsl.field.Field + +abstract class PivotTableField(override val domain: Domain, ident: String? = null) : Field(domain, ident) { + + // ---------------------------------------------------------------------- + // XML LOCALIZATION GENERATION + // ---------------------------------------------------------------------- + /** + * Generates localization for the field in the xml file + */ + override fun genLocalization(writer: LocalizationWriter) { + (writer as PivotTableLocalizationWriter).genField(ident, label, help) + } +} diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/PivotTableLocalizationWriter.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/PivotTableLocalizationWriter.kt new file mode 100644 index 000000000..bed81cc27 --- /dev/null +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/PivotTableLocalizationWriter.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.kopi.galite.visual.dsl.pivottable + +import org.jdom2.Element +import org.kopi.galite.visual.dsl.common.Actor +import org.kopi.galite.visual.dsl.common.LocalizationWriter +import org.kopi.galite.visual.dsl.common.Menu + +/** + * This class implements an XML localization file generator + */ +class PivotTableLocalizationWriter : LocalizationWriter() { + fun genPivotTable(title: String?, + fields: MutableList>, + menus: MutableList, + actors: MutableList) { + val self = Element("pivotTable") + + self.setAttribute("title", title) + + pushNode(self) + + // Menus + menus.forEach { menu -> + menu.genLocalization(this) + } + + // Actors + actors.forEach { actor -> + actor.genLocalization(this) + } + + fields.forEach { field -> + field.genLocalization(this) + } + // do not pop: this is the root element + } + + fun genField(ident: String, label: String?, help: String?) { + val self = Element("field") + self.setAttribute("ident", ident) + if (label != null) { + self.setAttribute("label", label) + } + if (help != null) { + self.setAttribute("help", help) + } + + peekNode(null).addContent(self) + } +} diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/PivotTableRow.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/PivotTableRow.kt new file mode 100644 index 000000000..89e111b37 --- /dev/null +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/pivottable/PivotTableRow.kt @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.kopi.galite.visual.dsl.pivottable + +import org.jetbrains.exposed.sql.statements.api.ExposedBlob +import org.kopi.galite.type.Month +import org.kopi.galite.type.Type0 +import org.kopi.galite.type.Week + +/** + * Represents a data row of a [PivotTable]. + * + * @param pivotTableFields the fields that exists in the pivot table. + */ +class PivotTableRow(private val pivotTableFields: MutableList>) { + /** A pivot table data row */ + val data = mutableMapOf, Any?>() + + /** + * Returns data value of a specific [PivotTableField] in this pivot table row. + * + * @param field the field. + * @return data value for a specific [PivotTableField]. + */ + @Suppress("UNCHECKED_CAST") + fun getValueOf(field: PivotTableField): T = data[field] as T + + /** + * Gets the value of the field in this pivot table row. + * + * @param field the field. + * @return data value for a specific [PivotTableField]. + */ + operator fun get(field: PivotTableField): T = getValueOf(field) + + /** + * Sets the value of the field in this pivot table row. + * + * @param field the field. + * @param value the field's value. + */ + operator fun set(field: PivotTableField, value: T) { + if (field in pivotTableFields) { + data.putIfAbsent(field, value) + } + } + + /** + * Sets the value of the field in this pivot table row. + * + * @param field the field. + * @param value the field's value. + */ + @JvmName("setType0") + operator fun , K> set(field: PivotTableField, value: K) { + if (field in pivotTableFields) { + data.putIfAbsent(field, field.toType0(value)) + } + } +} + +/** + * Represents the value in sql + */ +fun PivotTableField<*>.toType0(value: T): Any? { + return when(value) { + is ExposedBlob -> value + is Int -> { + when (domain.kClass) { + Month::class -> Month(value / 100, value % 100) + Week::class -> Week(value / 100, value % 100) + else -> null + } + } + else -> null + } +} diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VDictionaryForm.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VDictionaryForm.kt index 671fbce30..7b280d911 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VDictionaryForm.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VDictionaryForm.kt @@ -23,9 +23,9 @@ import org.kopi.galite.visual.MessageCode import org.kopi.galite.visual.VExecFailedException import org.kopi.galite.visual.VRuntimeException -import org.kopi.galite.visual.cross.VReportSelectionForm import org.kopi.galite.visual.form.VConstants.Companion.MOD_UPDATE import org.kopi.galite.visual.fullcalendar.VFullCalendarBlock +import org.kopi.galite.visual.pivottable.VPivotTable import org.kopi.galite.visual.report.VNoRowException import org.kopi.galite.visual.report.VReport @@ -224,6 +224,23 @@ abstract class VDictionaryForm protected constructor(source: String? = null) : V b.setRecordChanged(0, false) } + /** + * Implements interface for COMMAND CreatePivotTable + */ + fun createPivotTable(b: VBlock, pivotTableBuilder: () -> VPivotTable) { + b.validate() + try { + setWaitInfo(Message.getMessage("pivotTable_generation")) + val pivotTable = pivotTableBuilder() + pivotTable.doNotModal() + unsetWaitInfo() + } catch (e: VNoRowException) { + unsetWaitInfo() + error(MessageCode.getMessage("VIS-00057")) + } + b.setRecordChanged(0, false) + } + companion object { /** * static call to createReport. diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/l10n/ActorLocalizer.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/l10n/ActorLocalizer.kt index 85aa9c666..2ac0d15d3 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/l10n/ActorLocalizer.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/l10n/ActorLocalizer.kt @@ -50,7 +50,7 @@ class ActorLocalizer(document: Document, ident: String) { init { val root: Element = document.rootElement - val names = listOf("form", "report", "chart", "insert") // FIXME: Do we steel need insert root element? + val names = listOf("form", "report", "chart", "insert", "pivottable") // FIXME: Do we steel need insert root element? if (root.name !in names) { throw InconsistencyException("bad root element $root") diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/l10n/LocalizationManager.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/l10n/LocalizationManager.kt index 0271ca8a9..f48c20445 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/l10n/LocalizationManager.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/l10n/LocalizationManager.kt @@ -115,6 +115,15 @@ class LocalizationManager(val locale: Locale?, private val defaultLocale: Locale return ChartLocalizer(this, getDocument(source)) } + /** + * Constructs a chart localizer using the specified source. + * + * @param source the source qualified name + */ + fun getPivotTableLocalizer(source: String?): PivotTableLocalizer { + return PivotTableLocalizer(this, getDocument(source)) + } + /** * Constructs a module localizer using the specified source. * diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/l10n/MenuLocalizer.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/l10n/MenuLocalizer.kt index 6dd395f31..dd753d20f 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/l10n/MenuLocalizer.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/l10n/MenuLocalizer.kt @@ -40,7 +40,7 @@ class MenuLocalizer(document: Document, ident: String) { // ---------------------------------------------------------------------- init { val root: Element = document.rootElement - val names = listOf("form", "report", "chart", "insert") // FIXME: Do we steel need insert root element? + val names = listOf("form", "report", "chart", "insert", "pivottable") // FIXME: Do we steel need insert root element? if (root.name !in names) { throw InconsistencyException("bad root element $root") diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/l10n/PivotTableLocalizer.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/l10n/PivotTableLocalizer.kt new file mode 100644 index 000000000..b52e60a7b --- /dev/null +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/l10n/PivotTableLocalizer.kt @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.kopi.galite.visual.l10n + +import org.jdom2.Document +import org.jdom2.Element +import org.kopi.galite.util.base.InconsistencyException + +/** + * Implements a pivot table localizer. + * + * @param manager the manager to use for localization + * @param document the document containing the pivot table localization + */ +class PivotTableLocalizer(manager: LocalizationManager, document: Document) : Localizer(manager) { + + // ---------------------------------------------------------------------- + // DATA MEMBERS + // ---------------------------------------------------------------------- + private val root: Element = document.rootElement + + // ---------------------------------------------------------------------- + // CONSTRUCTOR + // ---------------------------------------------------------------------- + init { + if (root.name != "pivottable") { + throw InconsistencyException("bad root element $root") + } + } + + /** + * Returns the value of the title attribute. + */ + fun getTitle(): String = root.getAttributeValue("title") + + /** + * Returns the value of the help attribute. + */ + fun getHelp(): String? = root.getAttributeValue("help") + + /** + * Constructs a field localizer for the given field. + * + * @param ident the identifier of the field + */ + fun getFieldLocalizer(ident: String): FieldLocalizer { + return FieldLocalizer(manager, + Utils.lookupChild(root, "field", "ident", ident)) + } +} diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/Constants.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/Constants.kt index 05e330335..af70dfd31 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/Constants.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/Constants.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,51 +21,58 @@ package org.kopi.galite.visual.pivottable import org.kopi.galite.visual.form.VConstants import org.kopi.galite.visual.Constants +/** + * Collects some constants for the pivot table implementation + */ interface Constants : Constants { companion object { + // --------------------------------------------------------------------- + // PIVOT TABLE POSITION + // --------------------------------------------------------------------- + const val DIMENSION_NO_POSITION = 0 + const val DIMENSION_ROW = 1 + const val DIMENSION_COLUMN = 2 // --------------------------------------------------------------------- - // TRIGGERED EVENTS (MAX 32) + // PIVOT TABLE Aggregator // --------------------------------------------------------------------- - const val TRG_PREPIVOT = 0 - const val TRG_POSTPIVOT = 1 - const val TRG_INIT = 2 - const val TRG_FORMAT = 3 - const val TRG_COMPUTE = 4 - const val TRG_CMDACCESS = 5 - const val TRG_VOID: Int = VConstants.TRG_VOID - const val TRG_OBJECT: Int = VConstants.TRG_OBJECT - const val TRG_BOOLEAN: Int = VConstants.TRG_BOOLEAN + const val SUM_OVER_SUM = "Sum Over Sum" + const val UPPER_BOUND = "80% Upper Bound" + const val LOWER_BOUND = "80% Lower Bound" + const val SUM_FRACTION_TOTALS = "Sum as Fraction of Total" + const val SUM_FRACTION_ROWS = "Sum as Fraction of Rows" + const val SUM_FRACTION_COLUMNS = "Sum as Fraction of Columns" + const val COUNT_FRACTION_TOTALS = "Count as Fraction of Total" + const val COUNT_FRACTION_ROWS = "Count as Fraction of Rows" + const val COUNT_FRACTION_COLUMNS = "Count as Fraction of Columns" // --------------------------------------------------------------------- - // PREDEFINED COMMANDS + // PIVOT TABLE MODE // --------------------------------------------------------------------- - const val CMD_QUIT = 0 - const val CMD_PRINT = 1 - const val CMD_PREVIEW = 2 - const val CMD_PRINT_OPTIONS = 3 - const val CMD_EXPORT_CSV = 4 - const val CMD_EXPORT_XLS = 5 - const val CMD_EXPORT_XLSX = 6 - const val CMD_EXPORT_PDF = 7 - const val CMD_FOLD = 8 - const val CMD_UNFOLD = 9 - const val CMD_SORT = 10 - const val CMD_FOLD_COLUMN = 11 - const val CMD_UNFOLD_COLUMN = 12 - const val CMD_COLUMN_INFO = 13 - const val CMD_OPEN_LINE = 14 - const val CMD_REMOVE_CONFIGURATION = 15 - const val CMD_LOAD_CONFIGURATION = 16 - const val CMD_HELP = 17 + const val MODE_INTERACTIVE = 0 + const val MODE_NONINTERACTIVE = 1 + + // --------------------------------------------------------------------- + // TRIGGERED EVENTS + // --------------------------------------------------------------------- + const val TRG_INIT = 0 + const val TRG_PRE_PIVOT_TABLE = 1 + const val TRG_POST_PIVOT_TABLE = 2 + const val TRG_VOID = VConstants.TRG_VOID + const val TRG_OBJECT = VConstants.TRG_OBJECT // --------------------------------------------------------------------- // TRIGGER INFO // --------------------------------------------------------------------- val TRG_NAMES = arrayOf( - "TRG_PREPIVOT", "TRG_POSTPIVOT", "TRG_INIT", "TRG_FORMAT", "TRG_COMPUTE", "TRG_CMDACCESS") + "TRG_INIT", + "TRG_PRE_PIVOT_TABLE", + "TRG_POST_PIVOT_TABLE", + ) val TRG_TYPES = intArrayOf( - TRG_VOID, TRG_VOID, TRG_VOID, TRG_OBJECT, TRG_OBJECT, TRG_BOOLEAN + TRG_VOID, + TRG_VOID, + TRG_VOID ) } } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/MPivotTable.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/MPivotTable.kt index cbb6d8e80..1d0e81ad9 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/MPivotTable.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/MPivotTable.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -15,161 +15,56 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -package org.kopi.galite.visual.pivottable - -import org.jetbrains.kotlinx.dataframe.AnyCol -import org.jetbrains.kotlinx.dataframe.columns.ColumnGroup -import org.jetbrains.kotlinx.dataframe.columns.ValueColumn -import org.jetbrains.kotlinx.dataframe.values - -class MPivotTable(private val pivotTable: PivotTable) { - private lateinit var values: MutableList> - private var valuesSize: Int = 0 - - // Data to show - private lateinit var rows: MutableList> - private lateinit var groups: MutableList> - private lateinit var groupsSpans: MutableList> - private lateinit var groupsSpanTypes: MutableList> - private lateinit var rowsSpanTypes: MutableList> - private lateinit var spanTypes: List> - private lateinit var data: List> - - fun build() { - val cols = pivotTable.dataframe.columns() - val columnGroups = cols.filterIsInstance(ColumnGroup::class.java) - val valueColumns = cols.filterIsInstance(ValueColumn::class.java) - - values = mutableListOf() - rows = mutableListOf() - groups = mutableListOf() - groupsSpans = mutableListOf() - groupsSpanTypes = mutableListOf() - rowsSpanTypes = mutableListOf() - - valuesSize = valueColumns.size - - columnGroups.forEach { column -> - column.buildHeaderGrouping() - } - val rowGroupingValues = mutableListOf() +package org.kopi.galite.visual.pivottable - rows.add(rowGroupingValues) - valueColumns.forEach { - rowGroupingValues.add(it.name()) - } - repeat(values.size + 1) { rowGroupingValues.add("") } +import java.io.Serializable - repeat(pivotTable.dataframe.rowsCount()) { rowIndex -> - val aggregationValues = mutableListOf() - rows.add(aggregationValues) - valueColumns.forEach { vc -> - val value = vc.values.elementAt(rowIndex) - aggregationValues.add(pivotTable.aggregateField!!.model.format(value)) - } +import org.kopi.galite.visual.MessageCode - aggregationValues.add("") +class MPivotTable : Serializable { - values.forEach { - aggregationValues.add(it[rowIndex]) - } - } + // -------------------------------------------------------------------- + // DATA MEMBERS + // -------------------------------------------------------------------- + // Columns contains all columns defined by the user + var columns = mutableListOf() // array of column definitions - buildRowsSpanTypes() + // Baserows contains data give by the request of the user + internal var userRows: ArrayList? = ArrayList() - for(j in 0 until valuesSize) { - var i = 0 - - while(i < rows.size) { - var k = i + 1 - while(k < rows.size && rows[i][j] == rows[k][j] && (j == 0 || rowsSpanTypes[k][j - 1] == Span.ROW)) { - rows[k][j] = "" - rowsSpanTypes[k][j] = Span.ROW - k++ - } - i = k - } - } - - spanTypes = groupsSpanTypes + rowsSpanTypes - data = groups + rows - } - - private fun buildRowsSpanTypes() { - rows.forEachIndexed { index, row -> - val spans = row.mapIndexed { rowIndex, _ -> - if(index == 0 && rowIndex > valuesSize) { - Span.ROW - } else if (rowIndex == valuesSize) { - Span.COL - } else { - Span.NONE - } - }.toMutableList() - rowsSpanTypes.add(spans) - } + /** + * Add a row to the list of rows defined by the user + */ + fun addLine(line: Array) { + userRows!!.add(VPivotTableRow(line)) } - private fun ColumnGroup<*>.buildHeaderGrouping(columns: List = columns(), columnIndex: Int = 0) { - val group = mutableListOf() - val spans = mutableListOf() - val spansTypes = mutableListOf() - val groupingColumns = mutableListOf() - - add("", valuesSize, group, spans, spansTypes) - add(pivotTable.grouping.columns[columnIndex].model.label, 1, group, spans, spansTypes) - - groups.add(group) - groupsSpans.add(spans) - groupsSpanTypes.add(spansTypes) - - columns.forEach { column -> - if(column is ColumnGroup<*>) { - add(column.name(), column.columnsCount(), group, spans, spansTypes) - groupingColumns.addAll(column.columns()) - } else { - add(column.name(), valuesSize, group, spans, spansTypes) - values.add(column.values.map { pivotTable.aggregateField!!.model.format(it) }) - } - } - - if(groupingColumns.isNotEmpty()) { - buildHeaderGrouping(groupingColumns, columnIndex + 1) - } - } + /** + * Build the base row + initialisation + */ + internal fun build() { + // build accessible columns - private fun add(header: String, - spanSize: Int, - group: MutableList, - spans: MutableList, - spansTypes: MutableList, ) { - group.add(header) - spans.add(spanSize) - spansTypes.add(Span.NONE) - repeat(spanSize - 1) { - group.add("") - spansTypes.add(Span.COL) + if (userRows!!.size == 0) { + throw VNoRowException(MessageCode.getMessage("VIS-00015")) } } + // -------------------------------------------------------------------- + // MEMBER ACCESS + // -------------------------------------------------------------------- /** - * Returns the number of columns managed by the data source object. + * Return a column definition * - * @return the number or columns to display + * @param column the index of the desired column + * @return the desired column */ - fun getColumnCount(): Int = data.lastOrNull()?.size ?: 0 - + fun getModelColumn(column: Int): VPivotTableColumn = columns[column]!! /** - * Returns the number of records managed by the data source object. + * Returns the number of model columns * - * @return the number or rows in the model + * @return the number or columns to display */ - fun getRowCount(): Int = data.size - - fun getValueAt(row: Int, col: Int): String = data[row][col] - - fun isTitle(row: Int, col: Int): Boolean = (row in 0 .. groups.size) || (col in 0 .. valuesSize) - - fun getSpan(row: Int, col: Int): Span = spanTypes[row][col] + fun getModelColumnCount(): Int = columns.size } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/PivotTable.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/PivotTable.kt deleted file mode 100644 index 95a384527..000000000 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/PivotTable.kt +++ /dev/null @@ -1,426 +0,0 @@ -/* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.kopi.galite.visual.pivottable - -import java.io.File -import java.util.Locale - -import kotlin.reflect.full.starProjectedType - -import org.jetbrains.kotlinx.dataframe.DataFrame -import org.jetbrains.kotlinx.dataframe.aggregation.Aggregatable -import org.jetbrains.kotlinx.dataframe.api.* -import org.jetbrains.kotlinx.dataframe.AnyFrame -import org.jetbrains.kotlinx.dataframe.DataColumn -import org.kopi.galite.visual.domain.Domain -import org.kopi.galite.visual.dsl.report.ReportField -import org.kopi.galite.visual.dsl.report.ReportRow -import org.kopi.galite.visual.form.VConstants -import org.kopi.galite.visual.l10n.LocalizationManager -import org.kopi.galite.visual.report.Constants -import org.kopi.galite.visual.report.VReportColumn -import org.kopi.galite.visual.ApplicationContext -import org.kopi.galite.visual.UIFactory -import org.kopi.galite.visual.UWindow -import org.kopi.galite.visual.VWindow -import org.kopi.galite.visual.WindowBuilder -import org.kopi.galite.visual.WindowController - -open class PivotTable(title: String?, var help: String?, override val locale: Locale?) : VWindow(), Constants, VConstants { - - constructor(title: String, locale: Locale? = null) : this(title, null, locale) - - // ---------------------------------------------------------------------- - // DATA MEMBERS - // ---------------------------------------------------------------------- - private var pageTitle = "" - - /** Report's fields. */ - val fields = mutableListOf>() - - /** A report data row */ - val rows = mutableListOf() - - val columns = mutableListOf() - - lateinit var grouping: Grouping - - var funct = Function.NONE - - internal lateinit var dataframe: AnyFrame - private var aggregateFields = arrayOf() - var aggregateField: ReportField<*>? = null - private set - val model: MPivotTable = MPivotTable(this) - - init { - setTitle(title) - } - - fun aggregate(function: Function, field: ReportField) { - funct = function - aggregateFields = arrayOf(field.label!!) - aggregateField = field - } - - /** - * creates and returns a field. It uses [init] method to initialize the field. - * - * @param domain the domain of the field. - * @param init initialization method. - * @return a field. - */ - inline fun ?> field(domain: Domain, - noinline init: ReportField.() -> Unit): ReportField { - domain.kClass = T::class - - val field = ReportField(domain, init, "ANM_${fields.size}", domain.source.ifEmpty { source }) - - field.initialize() - - val pos = if(columns.size == 0) 0 else columns.size - 1 // TODO!! - columns.add(pos, field.buildReportColumn()) - fields.add(field) - - return field - } - - /** - * creates and returns a field that accept nulls. It uses [init] method to initialize the field. - * - * @param domain the domain of the field. - * @param init initialization method. - * @return a field. - */ - inline fun ?> nullableField(domain: Domain, - noinline init: ReportField.() -> Unit): ReportField { - return field(domain, init) as ReportField - } - - override fun getType() = org.kopi.galite.visual.Constants.MDL_PIVOT_TABLE - - /** - * Redisplay the pivot table after change in formatting - */ - @Deprecated("call method in display; model must not be refreshed") - fun redisplay() { - (getDisplay() as UPivotTable).redisplay() - } - - /** - * Close window - */ - @Deprecated("call method in display; model must not be closed") - fun close() { - getDisplay()!!.closeWindow() - } - - override fun destroyModel() { - /*try { TODO - callTrigger(org.kopi.galite.visual.pivottable.Constants.TRG_POSTREPORT) - } catch (v: VException) { - // ignore - }*/ - super.destroyModel() - } - - fun columnMoved(pos: IntArray) { - (getDisplay() as UPivotTable).columnMoved(pos) - } - - /** - * Sets the title - */ - fun setPageTitle(title: String) { - pageTitle = title - setTitle(title) - } - - fun getValueAt(row: Int, col: Int): String = model.getValueAt(row, col) - - // ---------------------------------------------------------------------- - // DISPLAY INTERFACE - // ---------------------------------------------------------------------- - open fun initPivotTable() { - build() - //callTrigger(Constants.TRG_PREPIVOT) TODO - } - - /** - * Localizes this pivot table. - * - * @param manager the manger to use for localization. - */ - private fun localize(manager: LocalizationManager) { - if (ApplicationContext.getDefaultLocale() != locale) { - val loc = manager.getReportLocalizer(source) - - setTitle(loc.getTitle()) - help = loc.getHelp() - columns.forEach { it.localize(loc) } - } - } - - fun build() { - localize(manager) - buildDataFrame() - model.build() - (getDisplay() as UPivotTable?)?.build() - } - - private fun buildDataFrame() { - val df = dataFrameOf(fields) - - dataframe = if (grouping.columns.isEmpty() && grouping.rows.isEmpty()) { - df.aggregate() - } else if (grouping.rows.isEmpty()) { - df.pivot().aggregate() - } else if (grouping.columns.isEmpty()) { - df.groupBy().aggregate() - } else { - df.pivot().groupBy().aggregate().sortBy() - } - } - - private fun getAllValuesOf(field: ReportField): List = rows.map { it[field] } - - private fun getAllFormattedValuesOf(field: ReportField<*>): List = rows.map { field.model.format(it[field]) } - - private fun dataFrameOf(header: Iterable>): AnyFrame = - header.map { field -> - val (values, type) = if (field == aggregateField) { - getAllValuesOf(field) to field.domain.kClass!!.starProjectedType - } else { - getAllFormattedValuesOf(field) to String::class.starProjectedType - } - - DataColumn.create( - field.model.label, - values, - type - ) - }.toDataFrame() - - private fun DataFrame.pivot(): Pivot { - return if (grouping.columns.size == 1) { - this.pivot(grouping.columns[0].label!!) - } else { - this.pivot { - grouping.columns - .subList(2, grouping.columns.size) - .fold(grouping.columns[0].label!! then grouping.columns[1].label!!) { a, b -> - a then b.label!! - } - } - } - } - - private fun DataFrame.groupBy(): GroupBy { - return if (grouping.rows.size == 1) { - this.groupBy(grouping.rows[0].label!!) - } else { - this.groupBy { - grouping.rows - .subList(2, grouping.rows.size) - .fold(grouping.rows[0].label!! and grouping.rows[1].label!!) { a, b -> - a and b.label!! - } - } - } - } - - fun Pivot<*>.groupBy(): PivotGroupBy { - return if (grouping.rows.size == 1) { - this.groupBy(grouping.rows[0].label!!) - } else { - this.groupBy { - grouping.rows - .subList(2, grouping.rows.size) - .fold(grouping.rows[0].label!! and grouping.rows[1].label!!) { a, b -> - a and b.label!! - } - } - } - } - - private fun DataFrame<*>.sortBy(): DataFrame { - return if (grouping.rows.size == 1) { - this.sortBy(grouping.rows[0].label!!) - } else { - this.sortBy { - grouping.rows - .subList(2, grouping.rows.size) - .fold(grouping.rows[0].label!! and grouping.rows[1].label!!) { a, b -> - a and b.label!! - } - } - } - } - - /** - * Adds a row to the pivot table. - * - * @param init initializes the row with values. - */ - fun add(init: ReportRow.() -> Unit) { - val row = ReportRow(fields) - row.init() - - // Last null value is added for the separator column - rows.add(row) - } - - private fun Aggregatable.aggregate(): DataFrame { - return when (funct) { - Function.MAX -> _max() - Function.MEAN -> _mean() - Function.SUM -> _sum() - Function.MIN -> _min() - else -> TODO() - } - } - - private fun Aggregatable<*>._max(): DataFrame { - return when (this) { - is Pivot<*> -> { - this.max(*aggregateFields).toDataFrame() - } - is DataFrame<*> -> { - this.max().toDataFrame() - } - is GroupBy<*, *> -> { - this.max(*aggregateFields) - } - is PivotGroupBy<*> -> { - this.max(*aggregateFields) - } - else -> { - throw UnsupportedOperationException() - } - } - } - - private fun Aggregatable<*>._mean(): DataFrame { - return when (this) { - is Pivot<*> -> { - this.mean().toDataFrame() - } - is DataFrame<*> -> { - this.mean().toDataFrame() - } - is GroupBy<*, *> -> { - this.mean(*aggregateFields) - } - is PivotGroupBy<*> -> { - this.mean(*aggregateFields) - } - else -> { - throw UnsupportedOperationException() - } - } - } - - /** - * Returns the number of columns managed by the data source object. - * - * @return the number or columns to display - */ - fun getColumnCount(): Int = model.getColumnCount() - - /** - * Returns the number of records managed by the data source object. - * - * @return the number or rows in the model - */ - fun getRowCount(): Int = model.getRowCount() - - private fun Aggregatable<*>._sum(): DataFrame { - return when (this) { - is Pivot<*> -> { - this.sum(*aggregateFields).toDataFrame() - } - is DataFrame<*> -> { - this.sum().toDataFrame() - } - is GroupBy<*, *> -> { - this.sum(*aggregateFields) - } - is PivotGroupBy<*> -> { - (this as PivotGroupBy).sum(*aggregateFields) - } - else -> { - throw UnsupportedOperationException() - } - } - } - - private fun Aggregatable<*>._min(): DataFrame { - return when (this) { - is Pivot<*> -> { - this.min().toDataFrame() - } - is DataFrame<*> -> { - this.min().toDataFrame() - } - is GroupBy<*, *> -> { - this.min(*aggregateFields) - } - is PivotGroupBy<*> -> { - this.min(*aggregateFields) - } - else -> { - throw UnsupportedOperationException() - } - } - } - - companion object { - const val TYP_CSV = 1 - const val TYP_PDF = 2 - const val TYP_XLS = 3 - const val TYP_XLSX = 4 - - init { - WindowController.windowController.registerWindowBuilder( - org.kopi.galite.visual.Constants.MDL_PIVOT_TABLE, - object : WindowBuilder { - override fun createWindow(model: VWindow): UWindow { - return UIFactory.uiFactory.createView( - model) as UPivotTable - } - } - ) - } - } - - @PublishedApi - internal val `access$sourceFile`: String get() = - this.javaClass.`package`.name.replace(".", "/") + - File.separatorChar + - this.javaClass.simpleName -} - -enum class Function { - NONE, - SUM, - MEAN, - MIN, - MAX -} - -class Grouping(val columns: List>, val rows: List>) diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/UPivotTable.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/UPivotTable.kt index 0a5f75a40..cefb9551a 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/UPivotTable.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/UPivotTable.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,10 +18,11 @@ package org.kopi.galite.visual.pivottable -import org.kopi.galite.visual.report.UReport +import org.kopi.galite.visual.UWindow -/** - * `UPivotTable` is the top-level interface that must be implemented - * by all pivot tables. It is the visual component of the [PivotTable] model. - */ -interface UPivotTable : UReport +interface UPivotTable : UWindow { + /** + * Builds the pivot table; + */ + fun build() +} diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/VHelpGenerator.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/VHelpGenerator.kt new file mode 100644 index 000000000..847bc8340 --- /dev/null +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/VHelpGenerator.kt @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.kopi.galite.visual.pivottable + +import java.io.BufferedWriter +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.io.OutputStreamWriter +import java.io.PrintWriter + +import org.kopi.galite.visual.base.Utils +import org.kopi.galite.visual.VCommand +import org.kopi.galite.visual.VHelpGenerator + +/** + * This class implements a pretty printer + */ +class VHelpGenerator : VHelpGenerator() { + // ---------------------------------------------------------------------- + // ACCESSORS + // ---------------------------------------------------------------------- + /** + * prints a compilation unit + */ + fun helpOnPivotTable(name: String, + commands: List, + model: MPivotTable, + help: String?): String? { + return try { + val file: File = Utils.getTempFile(name.replace("[:\\\\/*\"?|<>']".toRegex(), " "), "htm") + printer = PrintWriter(BufferedWriter(OutputStreamWriter(FileOutputStream(file), "UTF-8"))) + printer.println("") + printer.println("") + printer.println("
") + printer.println("$name") + printer.println("") + printer.println("") + printer.println("") + printer.println("") + printer.println("") + printer.println("") + printer.println("") + printer.println("

$name

") + if (help != null) { + printer.println("

$help

") + } + helpOnCommands(commands) + val columnCount: Int = model.getModelColumnCount() + printer.println("") + printer.println("") + printer.println("") + printer.println("") + printer.println("") + printer.println("
    
") + printer.println("
") + printer.println("
") + for (i in 0 until columnCount) { + val column: VPivotTableColumn = model.getModelColumn(i) + column.helpOnColumn(this) + } + printer.println("
") + printer.println("
") + printer.println("
") + printer.println("
") + printer.println("kopiLeft Services SARL, Tunis TN
") + printer.println("kopiRight Managed Solutions GmbH, Wien AT
") + val version = Utils.getVersion() + for (i in version.indices) { + printer.println("" + version[i] + "
") + } + printer.println("
") + printer.println("
") + printer.println("") + printer.println("") + printer.close() + file.path + } catch (e: IOException) { + System.err.println("IO ERROR $e") + null + } + } + + /** + * prints a compilation unit + */ + fun helpOnColumn(label: String?, + help: String?) { + if (label == null) { + return + } + printer.println("
") + printer.println("

$label

") + printer.println("
") + if (help != null) { + printer.println("

$help

") + } + } +} diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/Span.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/VNoRowException.kt similarity index 61% rename from galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/Span.kt rename to galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/VNoRowException.kt index 6e2e2013d..e4f0f66ed 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/Span.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/VNoRowException.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,8 +18,15 @@ package org.kopi.galite.visual.pivottable -enum class Span { - NONE, - ROW, - COL +import org.kopi.galite.visual.VRuntimeException + +/** + * This class represents exceptions occurring during execution process. + * + * @param message the associated message. It's optional + */ +class VNoRowException(message: String? = null) : VRuntimeException(message) { + companion object { + private const val serialVersionUID = 0L + } } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/VPivotTable.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/VPivotTable.kt new file mode 100644 index 000000000..d4819a0f9 --- /dev/null +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/VPivotTable.kt @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.kopi.galite.visual.pivottable + +import java.io.File +import java.net.MalformedURLException + +import org.vaadin.addons.componentfactory.PivotTable.* + +import org.kopi.galite.util.base.InconsistencyException +import org.kopi.galite.visual.* +import org.kopi.galite.visual.form.VConstants +import org.kopi.galite.visual.l10n.LocalizationManager +import org.kopi.galite.visual.dsl.common.Trigger + +/** + * Represents a pivot table model. + */ +abstract class VPivotTable internal constructor() : VWindow(), VConstants { + companion object { + + init { + WindowController.windowController.registerWindowBuilder( + org.kopi.galite.visual.Constants.MDL_PIVOT_TABLE, + object : WindowBuilder { + override fun createWindow(model: VWindow): UWindow { + return UIFactory.uiFactory.createView(model) as UPivotTable + } + } + ) + } + } + + // ---------------------------------------------------------------------- + // DATA MEMBERS + // ---------------------------------------------------------------------- + val model: MPivotTable = MPivotTable() + private var built = false + private var pageTitle = "" + var defaultRenderer = Renderer.TABLE // Default pivot table type + var aggregator = Pair(Aggregator.COUNT, "") // default Aggregator + var disabledRerenders = mutableListOf() + var interactive = Constants.MODE_INTERACTIVE + val PIVOT_TABLE_Triggers = listOf(arrayOfNulls(Constants.TRG_TYPES.size)) + private val activeCommands = ArrayList() + var help: String? = null + + override fun getType() = org.kopi.galite.visual.Constants.MDL_PIVOT_TABLE + + /** + * Close window + */ + fun close() { + getDisplay()!!.closeWindow() + } + + override fun destroyModel() { + super.destroyModel() + } + + /** + * Sets the new type of this pivot table model. + * @param type The new pivot table type. + */ + internal fun setDefaultRenderer(renderer: String) { + defaultRenderer = renderer + } + + /** + * Sets aggregation function of this pivot table model. + * @param aggregate The pivot table aggregation function. + */ + internal fun setAggregator(aggregate: Pair) { + aggregator = aggregate + } + + /** + * Sets aggregation function of this pivot table model. + * @param aggregate The pivot table aggregation function. + */ + internal fun setDisabledRerenders(rerenders: MutableList) { + disabledRerenders = rerenders + } + + /** + * Sets the mode of this pivot table model. + * @param interactive The pivot table mode. + */ + internal fun setInteractive(mode: Int) { + interactive = mode + } + + /** + * initialise fields + */ + protected abstract fun init() + + /** + * build everything after loading + */ + protected fun build() { + init() + // localize the pivot table using the default locale + localize() + if (hasTrigger(Constants.TRG_INIT)) { + callTrigger(Constants.TRG_INIT) + } + model.build() + (getDisplay() as UPivotTable?)?.build() + built = true + + // all commands are by default enabled + activeCommands.clear() + commands.forEach { vCommand -> + setCommandEnabled(vCommand, true) + } + } + + // ---------------------------------------------------------------------- + // LOCALIZATION + // ---------------------------------------------------------------------- + override fun getLocalizationManger(): LocalizationManager { + return LocalizationManager(ApplicationContext.getDefaultLocale(), ApplicationContext.getDefaultLocale()) + } + + /** + * Localizes this pivot table + * + */ + open fun localize() { + localize(manager) + } + + /** + * Localizes this pivot table + * + * @param manager the manger to use for localization + */ + private fun localize(manager: LocalizationManager) { + if(ApplicationContext.getDefaultLocale() != locale) { + val loc = manager.getPivotTableLocalizer(source) + + setPageTitle(loc.getTitle()) + help = loc.getHelp() + model.columns.forEach { it?.localize(loc) } + } + } + + // ---------------------------------------------------------------------- + // DISPLAY INTERFACE + // ---------------------------------------------------------------------- + open fun initPivotTable() { + build() + callTrigger(Constants.TRG_PRE_PIVOT_TABLE) + } + // ---------------------------------------------------------------------- + // INTERFACE (COMMANDS) + // ---------------------------------------------------------------------- + + /** + * Enables/disables the actor. + */ + fun setCommandEnabled(command: VCommand, enable: Boolean) { + command.setEnabled(enable) + + if (enable) { + activeCommands.add(command) + } else { + activeCommands.remove(command) + } + } + + /** + * Sets the title + */ + fun setPageTitle(title: String) { + pageTitle = title + setTitle(title) + } + + // ---------------------------------------------------------------------- + // PRIVATE METHODS + // ---------------------------------------------------------------------- + + fun setMenu() { + if (!built) { + // only when commands are displayed + return + } + } + + // -------------------------------------------------------------------- + // TRIGGER HANDLING + // -------------------------------------------------------------------- + + override fun executeVoidTrigger(trigger: Trigger?) { + trigger?.action?.method?.invoke() + super.executeVoidTrigger(trigger) + } + + fun executeObjectTrigger(trigger: Trigger?): Any? { + return (trigger?.action?.method as () -> Any?).invoke() + } + + /** + * Calls trigger for given event, returns last trigger called 's value. + */ + protected fun callTrigger(event: Int): Any? { + return callTrigger(event, 0, PIVOT_TABLE_Triggers) + } + + /** + * Calls trigger for given event, returns last trigger called 's value. + */ + private fun callTrigger(event: Int, index: Int, triggers: List>): Any? { + return when (Constants.TRG_TYPES[event]) { + Constants.TRG_VOID -> { + executeVoidTrigger(triggers[index][event]) + null + } + Constants.TRG_OBJECT -> executeObjectTrigger(triggers[index][event]) + else -> throw InconsistencyException("BAD TYPE" + Constants.TRG_TYPES[event]) + } + } + + /** + * Returns true if there is trigger associated with given event. + */ + internal fun hasTrigger(event: Int): Boolean = PIVOT_TABLE_Triggers[0][event] != null + + // ---------------------------------------------------------------------- + // HELP + // ---------------------------------------------------------------------- + + fun genHelp(): String? { + val surl = StringBuffer() + val fileName: String? = VHelpGenerator().helpOnPivotTable(pageTitle, + commands, + model, + help) + + return if (fileName == null) { + null + } else { + try { + surl.append(File(fileName).toURI().toURL().toString()) + } catch (mue: MalformedURLException) { + throw InconsistencyException(mue) + } + surl.toString() + } + } + + fun showHelp() { + VHelpViewer().showHelp(genHelp()) + } + + // ---------------------------------------------------------------------- + // Command + // ---------------------------------------------------------------------- + + fun addDefaultPivotTableCommands() { + initDefaultCommands() + } + + private fun initDefaultCommands() { + actors.forEachIndexed { index, vActor -> + commands.add(VCommand(VConstants.MOD_ANY, this, vActor, index, vActor.ident)) + } + } +} diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/VPivotTableColumn.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/VPivotTableColumn.kt new file mode 100644 index 000000000..27e826d9d --- /dev/null +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/VPivotTableColumn.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.kopi.galite.visual.pivottable + +import org.kopi.galite.visual.dsl.pivottable.Dimension +import org.kopi.galite.visual.l10n.FieldLocalizer +import org.kopi.galite.visual.l10n.PivotTableLocalizer + +/** + * Represents a pivot table column description + * @param ident The identifier of the field + * @param position The position of the dimension field + */ +class VPivotTableColumn(val ident: String?, val position: Dimension.Position?) { + + // ---------------------------------------------------------------------- + // DATA MEMBERS + // ---------------------------------------------------------------------- + var label: String = "" + var help: String? = null + + // ---------------------------------------------------------------------- + // LOCALIZATION + // ---------------------------------------------------------------------- + /** + * Localizes this field + * + * @param parent the caller localizer + */ + fun localize(parent: PivotTableLocalizer) { + if (ident != "") { + val loc: FieldLocalizer = parent.getFieldLocalizer(ident!!) + + label = loc.getLabel() ?: "" + } + } + + fun helpOnColumn(help: VHelpGenerator) { + help.helpOnColumn(label, this.help) + } +} diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/VPivotTableRow.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/VPivotTableRow.kt new file mode 100644 index 000000000..fb1d02c35 --- /dev/null +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/pivottable/VPivotTableRow.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.kopi.galite.visual.pivottable + +import javax.swing.tree.DefaultMutableTreeNode + +class VPivotTableRow(val data: Array) : DefaultMutableTreeNode() { + + /** + * Return the object at column + * + * @param column the index of the column + * @return the object to be displayed + */ + fun getValueAt(column: Int): Any? = data[column] + + companion object { + private const val serialVersionUID = 0L + } +} diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/pivottable/DPivotTable.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/pivottable/DPivotTable.kt index d963e3e30..96c924ca1 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/pivottable/DPivotTable.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/pivottable/DPivotTable.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,262 +17,80 @@ */ package org.kopi.galite.visual.ui.vaadin.pivottable -import java.awt.Color +import org.vaadin.addons.componentfactory.PivotTable +import com.vaadin.flow.component.dependency.CssImport -import org.kopi.galite.visual.pivottable.PivotTable +import org.kopi.galite.visual.dsl.pivottable.Dimension.Position +import org.kopi.galite.visual.pivottable.MPivotTable import org.kopi.galite.visual.pivottable.UPivotTable -import org.kopi.galite.visual.report.Parameters -import org.kopi.galite.visual.report.Point -import org.kopi.galite.visual.report.UReport -import org.kopi.galite.visual.ui.vaadin.base.BackgroundThreadHandler.access -import org.kopi.galite.visual.ui.vaadin.base.BackgroundThreadHandler.accessAndPush +import org.kopi.galite.visual.pivottable.VPivotTable import org.kopi.galite.visual.ui.vaadin.visual.DWindow -import com.vaadin.flow.component.Component -import com.vaadin.flow.component.HasComponents -import com.vaadin.flow.component.Unit -import com.vaadin.flow.component.button.Button -import com.vaadin.flow.component.dnd.DragSource -import com.vaadin.flow.component.dnd.DropEffect -import com.vaadin.flow.component.dnd.DropTarget -import com.vaadin.flow.component.dnd.EffectAllowed -import com.vaadin.flow.component.grid.Grid -import com.vaadin.flow.component.orderedlayout.HorizontalLayout -import com.vaadin.flow.component.orderedlayout.VerticalLayout -import com.vaadin.flow.component.select.Select - -/** - * The `DPivotTable` is the visual part of the [PivotTable] model. - * - * The `DPivotTable` ensure the implementation of the [UPivotTable] - * specifications. - * - * @param pivottable The report model. - */ -class DPivotTable(private val pivottable: PivotTable) : DWindow(pivottable), UPivotTable { +@CssImport("./styles/galite/pivottable.css") +class DPivotTable(private val pivotTable: VPivotTable) : DWindow(pivotTable), UPivotTable { //--------------------------------------------------- // DATA MEMBERS //--------------------------------------------------- - private val model = pivottable.model // report model - private lateinit var table: DTable - private var parameters: Parameters? = null - private val mainLayout = org.kopi.galite.visual.ui.vaadin.common.VTable(3, 2) + private val model: MPivotTable = pivotTable.model // pivot table model + private val pivotData = PivotTable.PivotData() + private val pivotOptions = PivotTable.PivotOptions() + private val rows = mutableListOf() + private val columns = mutableListOf() init { getModel()!!.setDisplay(this) - setSizeFull() } //--------------------------------------------------- // IMPLEMENTATIONS //--------------------------------------------------- override fun run() { - pivottable.initPivotTable() - table.focus() - setInfoTable() + pivotTable.initPivotTable() + pivotTable.setMenu() } override fun build() { - // load personal configuration - parameters = Parameters(Color(71, 184, 221)) - table = DTable(VTable(model, buildRows())) - table.isColumnReorderingAllowed = true - // 200 px is approximately the header window size + the actor pane size - ui.ifPresent { ui -> - ui.page.retrieveExtendedClientDetails { - table.setHeight(it.windowInnerHeight.toFloat() - 200, Unit.PIXELS) + model.columns.forEach { + pivotData.addColumn(it?.label, it?.javaClass) + if (it?.position == Position.ROW) { + rows.add(it.label) } - } - initLayout() - setContent(mainLayout) - resetWidth() - addTableListeners() - } - - private fun initLayout() { - addAggregations() - addAllGroupingFields() - addRowGroupingFields() - addColumnGroupingFields() - mainLayout.add(2, 1, table) - } - - private fun addAggregations() { - val aggregationLayout = VerticalLayout() - val aggregations = Select("Sum", "Mean", "Min", "Max") // TODO - val fields = Select() - - fields.setItems(pivottable.columns.map { it.label }) - - aggregationLayout.add(aggregations) - aggregationLayout.add(fields) - mainLayout.add(1, 0, aggregationLayout) - } - - private fun addAllGroupingFields() { - val fieldsContainer = HorizontalLayout() - - setDropTargetOf(fieldsContainer) - - pivottable.columns.forEach { - val button = Button(it.label) - val draggableButton = DragSource.create(button) - - draggableButton.effectAllowed = EffectAllowed.MOVE - fieldsContainer.add(button) - } - - mainLayout.add(0, 1, fieldsContainer) - } - - private fun addRowGroupingFields() { - val groupingFields = buildGroupingFieldsLayout() - - mainLayout.add(2, 0, groupingFields) - } - - private fun addColumnGroupingFields() { - val groupingFields = buildGroupingFieldsLayout() - - mainLayout.add(1, 1, groupingFields) - } - - private fun buildGroupingFieldsLayout(): VerticalLayout { - val groupingFields = VerticalLayout() - - setDropTargetOf(groupingFields) - - return groupingFields - } - - private fun setDropTargetOf(groupingFieldsLayout: T) where T: HasComponents, T: Component { - val dndLayout = DropTarget.create(groupingFieldsLayout) - - dndLayout.dropEffect = DropEffect.MOVE - - dndLayout.addDropListener { - groupingFieldsLayout.add(it.component) - } - } - - override fun redisplay() { - contentChanged() - } - - override fun removeColumn(position: Int) { - - } - - override fun addColumn(position: Int) { - - } - - override fun addColumn() { - - } - - override fun getTable(): UReport.UTable { - return table - } - - override fun contentChanged() { - if (this::table.isInitialized) { - accessAndPush(currentUI) { - table.setItems(buildRows()) - table.model.fireContentChanged() + if (it?.position == Position.COLUMN) { + columns.add(it.label) } } - } - override fun columnMoved(pos: IntArray) { + model.userRows + ?.flatMap { it.data.asIterable() } + ?.chunked(model.columns.count()) { rows -> + pivotData.addRow(*rows.map { it ?: "" }.toTypedArray())} - } + // Pivot table dimension + pivotOptions.setRows(*rows.toTypedArray()) + pivotOptions.setCols(*columns.toTypedArray()) - override fun resetWidth() { - access(currentUI) { - table.resetWidth() - } - } - - override fun getSelectedColumn(): Int { - return table.selectedColumn - } + // Pivot table default renderer + pivotOptions.setRenderer(pivotTable.defaultRenderer) - override fun getSelectedCell(): Point = Point(table.selectedColumn, table.selectedRow) + // Pivot table aggregate function + pivotOptions.setAggregator(pivotTable.aggregator.first, pivotTable.aggregator.second) - override fun setColumnLabel(column: Int, label: String) { - access(currentUI) { - table.getColumnByKey(column.toString()).setHeader(label) + // Pivot table renderer + if(rows.isNotEmpty() && columns.isNotEmpty()) { + pivotOptions.setCharts(true) } - } + pivotOptions.setDisabledRenderers(*pivotTable.disabledRerenders.toTypedArray()) - /** - * Return the columns display order. - * @return The columns display order. - */ - val displayOrder: IntArray - get() { - val displayOrder = IntArray(model.getColumnCount()) - for (i in 0 until model.getColumnCount()) { - displayOrder[i] = table.convertColumnIndexToModel(i) - } - return displayOrder + // Pivot table mode + val pivotMode = if (pivotTable.interactive == 0) { + PivotTable.PivotMode.INTERACTIVE + } else { + PivotTable.PivotMode.NONINTERACTIVE } - /** - * Returns the number of columns displayed in the table - * @return The number of columns displayed - */ - val columnCount: Int - get() = table.getColumnCount() - - /** - * Add listeners to the report table. - */ - private fun addTableListeners() { - - } - - private fun addHeaderListeners(gridColumn: Grid.Column<*>, header: VerticalLayout) { - - } - - fun getSelectedColumnIndex(gridColumn: Grid.Column<*>): Int = gridColumn.key.toInt() + val pivot = PivotTable(pivotData, pivotOptions, pivotMode) - /** - * Display table information in the footer of the table - */ - private fun setInfoTable() { - - } - - /** - * Builds the grid rows. - */ - private fun buildRows(): List { - val rows = mutableListOf() - for (i in 0 until model.getRowCount()) { - rows.add(ReportModelItem(i)) - } - return rows - } - - //--------------------------------------------------- - // TABLE MODEL ITEM - //--------------------------------------------------- - /** - * The `TableModelItem` is the report table - * data model. - * - * @param rowIndex The row index. - */ - inner class ReportModelItem(val rowIndex: Int) { - //--------------------------------------- - // IMPLEMENTATIONS - //--------------------------------------- - fun getValueAt(columnIndex: Int): String { - return model.getValueAt(rowIndex, columnIndex) - } + add(pivot) } } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/pivottable/DTable.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/pivottable/DTable.kt deleted file mode 100644 index 67c80d45e..000000000 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/pivottable/DTable.kt +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.kopi.galite.visual.ui.vaadin.pivottable - -import org.kopi.galite.visual.pivottable.Span -import org.kopi.galite.visual.report.UReport.UTable - -import com.vaadin.flow.component.AttachEvent -import com.vaadin.flow.component.DetachEvent -import com.vaadin.flow.component.dependency.CssImport -import com.vaadin.flow.component.grid.Grid -import com.vaadin.flow.component.grid.GridVariant -import com.vaadin.flow.component.page.Page -import com.vaadin.flow.function.ValueProvider -import com.vaadin.flow.shared.Registration - -import elemental.json.JsonObject -import elemental.json.JsonValue - -/** - * The `DTable` is a table implementing the [UTable] - * specifications. - * - * @param model The table model. - */ - -@CssImport.Container(value = [ - CssImport("./styles/galite/report.css"), - CssImport(value = "./styles/galite/report.css", themeFor = "vaadin-grid") -]) -class DTable(val model: VTable) : Grid(), UTable { - - //--------------------------------------------------- - // DATA MEMBERS - //--------------------------------------------------- - - /** - * The table selected row. - */ - val selectedRow: Int get() = asSingleSelect().value?.rowIndex ?: -1 - - /** - * The selected column. - */ - var selectedColumn = -1 - - /** - * The indexes of the columns in the grid view - */ - var viewColumns = mutableListOf() - - lateinit var browserWindowResizeListener: Registration - - init { - setItems(model) - buildColumns() - addThemeVariants(GridVariant.LUMO_COMPACT, GridVariant.LUMO_COLUMN_BORDERS) - themeNames.add("report") - classNames.add("small") - classNames.add("borderless") - classNames.add("report") - setWidthFull() - } - - //--------------------------------------------------- - // IMPLEMENTATIONS - //--------------------------------------------------- - - override fun onAttach(attachEvent: AttachEvent) { - val page = attachEvent.ui.page - - page.setWindowHeight() - - browserWindowResizeListener = page.addBrowserWindowResizeListener { event -> - height = (event.height - 200).toString() + "px" - } - } - - private fun Page.setWindowHeight() { - val js = "return Vaadin.Flow.getBrowserDetailsParameters();" - executeJs(js).then { - height = ((it as JsonObject).get("v-wh").asNumber() - 200).toString() + "px" - } - } - - override fun onDetach(detachEvent: DetachEvent) { - browserWindowResizeListener.remove() - } - - /** - * Builds the grid columns. - */ - private fun buildColumns() { - repeat(model.getColumnCount()) { index -> - val gridColumn = addColumn(index) - - gridColumn.flexGrow = 0 - } - } - - /** - * Maps the index of the column in the grid at [viewColumnIndex] to the index of the column in the table model. - */ - override fun convertColumnIndexToModel(viewColumnIndex: Int): Int = viewColumns[viewColumnIndex] - - /** - * Maps the index of the column in the table model at [modelColumnIndex] to the index of the column in the grid. - */ - override fun convertColumnIndexToView(modelColumnIndex: Int): Int = viewColumns.indexOf(modelColumnIndex) - - /** - * Adds a new text column to this table with a column value provider and a key for the column. - * - * @param key the key of the column provider - * @return the created column - */ - fun addColumn(key: Int): Column { - val provider = ColumnValueProvider(key) - - viewColumns.add(key) - - return addColumn(provider).also { - provider.column = it - it.setKey(key.toString()) - .setResizable(true) - .setSortable(false) - .setClassNameGenerator { item -> - buildString { - val isTitle = model.model.isTitle(item.rowIndex, key) - val span = model.model.getSpan(item.rowIndex, key) - - if (isTitle) { - append("title ") - } - - if (item.rowIndex == model.getRowCount () - 1) { - append("last-row ") - } - - if (span == Span.COL) { - append("colspan") - } else if (span == Span.ROW) { - append("rowspan") - } - } - } - } - } - - override fun removeColumnByKey(columnKey: String) { - viewColumns.remove(columnKey.toInt()) - super.removeColumnByKey(columnKey) - } - - override fun removeColumn(column: Column) { - viewColumns.remove(column.key.toInt()) - super.removeColumn(column) - } - - /** - * Returns the column count. - * @return the column count. - */ - fun getColumnCount(): Int = model.getColumnCount() - - /** - * Reset all columns widths. - */ - fun resetWidth() { - for (i in 0 until model.getColumnCount()) { - resetColumnSize(i) - } - } - - /** - * Resets the column size at a given position. - * @param pos The column position. - */ - private fun resetColumnSize(pos: Int) { - - } - - /** - * Provides the value for the column with index [columnIndex] - * - * @param columnIndex the index of the column - */ - inner class ColumnValueProvider( - private val columnIndex: Int - ) : ValueProvider { - var column: Column? = null - - override fun apply(source: DPivotTable.ReportModelItem): String { - return source.getValueAt(columnIndex) - } - } -} diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/pivottable/VTable.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/pivottable/VTable.kt deleted file mode 100644 index f3591230d..000000000 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/pivottable/VTable.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.kopi.galite.visual.ui.vaadin.pivottable - -import org.kopi.galite.visual.pivottable.MPivotTable - -import com.vaadin.flow.component.grid.Grid -import com.vaadin.flow.data.provider.ListDataProvider -import com.vaadin.flow.data.provider.Query -import com.vaadin.flow.function.SerializablePredicate - -/** - * The VTable is a vaadin [Grid] data provider adapted - * to dynamic reports needs. - * - * @param model The table model. - */ -class VTable( - internal val model: MPivotTable, - reportItems: List -): ListDataProvider(reportItems) { - - init { - addFilter { - it != null - } - } - - override fun size(query: Query>?): Int { - return model.getRowCount() - } - - /** - * Notify the report table that the report content has been - * change in order to update the table content. - */ - fun fireContentChanged() { - refreshAll() - } - - /** - * Returns the column count. - * @return the column count. - */ - fun getColumnCount(): Int = model.getColumnCount() - - /** - * Returns the row count. - * @return the row count. - */ - fun getRowCount(): Int = model.getRowCount() -} diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/VUIFactory.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/VUIFactory.kt index 7a820a417..2208b5943 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/VUIFactory.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/VUIFactory.kt @@ -21,7 +21,7 @@ import org.kopi.galite.visual.base.UComponent import org.kopi.galite.visual.chart.VChart import org.kopi.galite.visual.form.VForm import org.kopi.galite.visual.form.VListDialog -import org.kopi.galite.visual.pivottable.PivotTable +import org.kopi.galite.visual.pivottable.VPivotTable import org.kopi.galite.visual.preview.VPreviewWindow import org.kopi.galite.visual.report.VReport import org.kopi.galite.visual.ui.vaadin.chart.DChart @@ -70,7 +70,7 @@ class VUIFactory : UIFactory() { is VListDialog -> { createListDialog(model) } - is PivotTable -> { + is VPivotTable -> { createPivotTable(model) } else -> { @@ -160,7 +160,7 @@ class VUIFactory : UIFactory() { * @param model The pivot table model * @return The [DPivotTable] view. */ - internal fun createPivotTable(model: PivotTable): DPivotTable { + internal fun createPivotTable(model: VPivotTable): DPivotTable { return DPivotTable(model) } } diff --git a/galite-core/src/main/resources/META-INF/resources/frontend/styles/galite/pivottable.css b/galite-core/src/main/resources/META-INF/resources/frontend/styles/galite/pivottable.css new file mode 100644 index 000000000..6adf21106 --- /dev/null +++ b/galite-core/src/main/resources/META-INF/resources/frontend/styles/galite/pivottable.css @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +.pvtUi { + color: #333; + height: 785px; + width: 1920px; + overflow: auto; + display: flow; + padding-bottom: 10px; +} + + +table.pvtTable { + font-size: 8pt; + text-align: left; + border-collapse: collapse; +} +table.pvtTable thead tr th, table.pvtTable tbody tr th { + background-color: #e6EEEE; + border: 1px solid #CDCDCD; + font-size: 8pt; + padding: 5px !important; +} + +table.pvtTable .pvtColLabel {text-align: center;} +table.pvtTable .pvtTotalLabel {text-align: right;} + +table.pvtTable tbody tr td { + color: #3D3D3D; + padding: 5px; + background-color: #FFF; + border: 1px solid #CDCDCD; + vertical-align: top; + text-align: center; +} + +.pvtTotal, .pvtGrandTotal { font-weight: bold; } + +.pvtVals { text-align: center; white-space: nowrap;} +.pvtRowOrder, .pvtColOrder { + cursor:pointer; + width: 15px; + margin-left: 5px; + display: inline-block; } +.pvtAggregator { margin-bottom: 5px ;} + +.pvtAxisContainer, .pvtVals { + border: 1px solid gray; + background: #EEE; + padding: 5px; + min-width: 20px; + min-height: 20px; + + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -khtml-user-select: none; + -ms-user-select: none; +} +.pvtAxisContainer li { + padding: 8px 6px; + list-style-type: none; + cursor:move; +} +.pvtAxisContainer li.pvtPlaceholder { + -webkit-border-radius: 5px; + padding: 3px 15px; + -moz-border-radius: 5px; + border-radius: 5px; + border: 1px dashed #aaa; +} + +.pvtAxisContainer li span.pvtAttr { + -webkit-text-size-adjust: 100%; + background: #F3F3F3; + border: 1px solid #DEDEDE; + padding: 2px 5px; + white-space:nowrap; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} + +.pvtTriangle { + cursor:pointer; + color: grey; +} + +.pvtHorizList li { display: inline; } +.pvtVertList { vertical-align: top; } + +.pvtFilteredAttribute { font-style: italic } + +.pvtFilterBox{ + z-index: 100; + width: 300px; + border: 1px solid gray; + background-color: #fff; + position: absolute; + text-align: center; +} + +.pvtFilterBox h4{ margin: 15px; } +.pvtFilterBox p { margin: 10px auto; } +.pvtFilterBox label { font-weight: normal; } +.pvtFilterBox input[type='checkbox'] { margin-right: 10px; margin-left: 10px; } +.pvtFilterBox input[type='text'] { width: 230px; } +.pvtFilterBox .count { color: gray; font-weight: normal; margin-left: 3px;} + +.pvtCheckContainer{ + text-align: left; + font-size: 14px; + white-space: nowrap; + overflow-y: scroll; + width: 100%; + max-height: 250px; + border-top: 1px solid lightgrey; + border-bottom: 1px solid lightgrey; +} + +.pvtCheckContainer p{ margin: 5px; } + +.pvtRendererArea {padding: 5px;} diff --git a/galite-core/src/main/resources/org/kopi/galite/visual/VKMessages-ar_TN.xml b/galite-core/src/main/resources/org/kopi/galite/visual/VKMessages-ar_TN.xml index 582beac09..f031aba9c 100755 --- a/galite-core/src/main/resources/org/kopi/galite/visual/VKMessages-ar_TN.xml +++ b/galite-core/src/main/resources/org/kopi/galite/visual/VKMessages-ar_TN.xml @@ -23,6 +23,7 @@ + diff --git a/galite-core/src/main/resources/org/kopi/galite/visual/VKMessages-de_AT.xml b/galite-core/src/main/resources/org/kopi/galite/visual/VKMessages-de_AT.xml index 5e46c7ab7..0c64ade6f 100644 --- a/galite-core/src/main/resources/org/kopi/galite/visual/VKMessages-de_AT.xml +++ b/galite-core/src/main/resources/org/kopi/galite/visual/VKMessages-de_AT.xml @@ -23,6 +23,7 @@ + diff --git a/galite-core/src/main/resources/org/kopi/galite/visual/VKMessages-en_GB.xml b/galite-core/src/main/resources/org/kopi/galite/visual/VKMessages-en_GB.xml index fa938c004..99e8161ae 100644 --- a/galite-core/src/main/resources/org/kopi/galite/visual/VKMessages-en_GB.xml +++ b/galite-core/src/main/resources/org/kopi/galite/visual/VKMessages-en_GB.xml @@ -23,6 +23,7 @@ + diff --git a/galite-core/src/main/resources/org/kopi/galite/visual/VKMessages-fr_FR.xml b/galite-core/src/main/resources/org/kopi/galite/visual/VKMessages-fr_FR.xml index fc4b306aa..985474f70 100644 --- a/galite-core/src/main/resources/org/kopi/galite/visual/VKMessages-fr_FR.xml +++ b/galite-core/src/main/resources/org/kopi/galite/visual/VKMessages-fr_FR.xml @@ -23,6 +23,7 @@ + diff --git a/galite-core/src/main/resources/org/kopi/galite/visual/dsl/form/Form-ar_TN.xml b/galite-core/src/main/resources/org/kopi/galite/visual/dsl/form/Form-ar_TN.xml index d668e1d42..1e7da6227 100644 --- a/galite-core/src/main/resources/org/kopi/galite/visual/dsl/form/Form-ar_TN.xml +++ b/galite-core/src/main/resources/org/kopi/galite/visual/dsl/form/Form-ar_TN.xml @@ -29,6 +29,7 @@ + diff --git a/galite-core/src/main/resources/org/kopi/galite/visual/dsl/form/Form-de_AT.xml b/galite-core/src/main/resources/org/kopi/galite/visual/dsl/form/Form-de_AT.xml index 327e22d0c..29540267b 100644 --- a/galite-core/src/main/resources/org/kopi/galite/visual/dsl/form/Form-de_AT.xml +++ b/galite-core/src/main/resources/org/kopi/galite/visual/dsl/form/Form-de_AT.xml @@ -29,6 +29,7 @@ + diff --git a/galite-core/src/main/resources/org/kopi/galite/visual/dsl/form/Form-en_GB.xml b/galite-core/src/main/resources/org/kopi/galite/visual/dsl/form/Form-en_GB.xml index f59f7503c..993a290c5 100644 --- a/galite-core/src/main/resources/org/kopi/galite/visual/dsl/form/Form-en_GB.xml +++ b/galite-core/src/main/resources/org/kopi/galite/visual/dsl/form/Form-en_GB.xml @@ -29,6 +29,7 @@ + diff --git a/galite-core/src/main/resources/org/kopi/galite/visual/dsl/form/Form-fr_FR.xml b/galite-core/src/main/resources/org/kopi/galite/visual/dsl/form/Form-fr_FR.xml index a1e85411e..d70ab13ad 100644 --- a/galite-core/src/main/resources/org/kopi/galite/visual/dsl/form/Form-fr_FR.xml +++ b/galite-core/src/main/resources/org/kopi/galite/visual/dsl/form/Form-fr_FR.xml @@ -29,6 +29,7 @@ + diff --git a/galite-demo/galite-vaadin/build.gradle.kts b/galite-demo/galite-vaadin/build.gradle.kts index ef2bfc027..773eca40c 100644 --- a/galite-demo/galite-vaadin/build.gradle.kts +++ b/galite-demo/galite-vaadin/build.gradle.kts @@ -57,6 +57,9 @@ dependencies { // EnhancedDialog dependency testImplementation("com.vaadin.componentfactory", "enhanced-dialog", Versions.ENHANCED_DIALOG) + + // Pivot Table dependency + implementation("org.vaadin.addons.componentfactory", "pivottable-flow", Versions.PIVOT_TABLE) } tasks { diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/bill/BillForm.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/bill/BillForm.kt index f80cfa6dc..1fd767dc0 100644 --- a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/bill/BillForm.kt +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/bill/BillForm.kt @@ -53,12 +53,12 @@ class BillForm : DictionaryForm(title = "Bills", locale = Locale.UK) { help = "The bill number" columns(u.numBill) } - val addressBill = mustFill(domain = STRING(30), position = at(1, 1)) { + val addressBill = visit(domain = STRING(30), position = at(1, 1)) { label = "Address" help = "The bill address" columns(u.addressBill) } - val dateBill = mustFill(domain = DATE, position = at(2, 1)) { + val dateBill = visit(domain = DATE, position = at(2, 1)) { label = "Date" help = "The bill date" columns(u.dateBill) diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/client/ClientForm.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/client/ClientForm.kt index 9f87cda8d..fd4a57eae 100644 --- a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/client/ClientForm.kt +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/client/ClientForm.kt @@ -135,6 +135,11 @@ class ClientForm : DictionaryForm(title = "Clients", locale = Locale.UK) { ClientR() } } + command(item = pivotTable) { + createPivotTable { + ClientP() + } + } command(item = dynamicReport) { createDynamicReport() } @@ -196,6 +201,11 @@ class ClientForm : DictionaryForm(title = "Clients", locale = Locale.UK) { ClientR() } } + command(item = pivotTable) { + createPivotTable { + ClientP() + } + } command(item = dynamicReport) { createDynamicReport() } diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/client/ClientP.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/client/ClientP.kt new file mode 100644 index 000000000..3aa1e4a2d --- /dev/null +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/client/ClientP.kt @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.kopi.galite.demo.client + +import java.util.Locale + +import org.jetbrains.exposed.sql.JoinType +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.transaction + +import org.vaadin.addons.componentfactory.PivotTable.Renderer + +import org.kopi.galite.demo.database.Client +import org.kopi.galite.demo.database.Product +import org.kopi.galite.demo.database.Purchase +import org.kopi.galite.visual.domain.BOOL +import org.kopi.galite.visual.domain.DECIMAL +import org.kopi.galite.visual.domain.INT +import org.kopi.galite.visual.domain.STRING +import org.kopi.galite.visual.dsl.common.Icon +import org.kopi.galite.visual.dsl.form.Key +import org.kopi.galite.visual.dsl.pivottable.Dimension.Position +import org.kopi.galite.visual.dsl.pivottable.PivotTable +import org.kopi.galite.visual.pivottable.Constants + +/** + * Client Report + */ +class ClientP : PivotTable(title = "Clients_Pivot_Table", locale = Locale.UK) { + val action = menu("Action") + val file = menu("File") + + val quit = actor(menu = file, label = "Quit", help = "Close Report.", ident = "quit") { + key = Key.ESCAPE + icon = Icon.QUIT + } + + val helpForm = actor(menu = action, label = "Help", help = " Help", ident = "help") { + key = Key.F1 + icon = Icon.HELP + } + + val cmdQuit = command(item = quit) { + model.close() + } + val helpCmd = command(item = helpForm) { + model.showHelp() + } + + + val firstName = dimension(STRING(25), Position.ROW) { + label = "First Name" + help = "The client first name" + } + + val lastName = dimension(STRING(25), Position.ROW) { + label = "Last Name" + help = "The client last name" + } + + val countryClt = dimension(STRING(50), Position.COLUMN) { + label = "Country" + help = "The client country" + } + + val addressClt = dimension(STRING(50), Position.COLUMN) { + label = "Address" + help = "The client address" + } + + val ageClt = dimension(INT(2), Position.NONE) { + label = "Age" + help = "The client age" + } + + val cityClt = dimension(STRING(50), Position.NONE) { + label = "City" + help = "The client city" + } + + val zipCodeClt = dimension(INT(2), Position.NONE) { + label = "Zip code" + help = "The client zip code" + } + + val activeClt = dimension(BOOL, Position.NONE) { + label = "Status" + help = "Is the client active?" + } + + val quantity = measure(INT(10)) { + label = "Quantity" + help = "Product quantity" + } + + val price = measure(DECIMAL(9,3)) { + label = "Price" + help = "Product price" + } + + val init = trigger(INIT) { + defaultRenderer = Renderer.TABLE + aggregator = Pair(Constants.COUNT_FRACTION_COLUMNS, "") + disabledRerenders = mutableListOf(Renderer.SCATTER_CHART, Renderer.LINE_CHART, Renderer.HORIZONTAL_BAR_CHART, Renderer.HORIZONTAL_STACKED_BAR_CHART) + //interactive = Constants.MODE_NONINTERACTIVE + } + + val purchase = Client.join(Purchase, JoinType.LEFT) { Purchase.idClt eq Client.idClt} + .join(Product, JoinType.LEFT) { Purchase.idPdt eq Product.idPdt } + .selectAll() + + init { + transaction { + purchase.forEach { result -> + add { + this[firstName] = result[Client.firstNameClt] + this[lastName] = result[Client.lastNameClt] + this[addressClt] = result[Client.addressClt] + this[ageClt] = result[Client.ageClt] + this[countryClt] = result[Client.countryClt] + this[cityClt] = result[Client.cityClt] + this[zipCodeClt] = result[Client.zipCodeClt] + this[activeClt] = result[Client.activeClt] + this[quantity] = result[Purchase.quantity] + this[price] = result[Product.price] + } + } + } + } +} diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/command/CommandForm.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/command/CommandForm.kt index 9e62bf082..7e6a921d7 100644 --- a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/command/CommandForm.kt +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/command/CommandForm.kt @@ -124,4 +124,4 @@ class CommandForm : DictionaryForm(title = "Commands", locale = Locale.UK) { fun main() { runForm(form = CommandForm::class) -} +} \ No newline at end of file diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Migration.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Migration.kt index 339189321..b915efef4 100644 --- a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Migration.kt +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Migration.kt @@ -253,10 +253,20 @@ fun addClient(id: Int, } fun addProducts() { + // That data is used in automated tests addProduct(0, "description Product 0", 1, "tax 1", "Men", "Supplier 0", BigDecimal("263")) addProduct(1, "description Product 1", 2, "tax 2", "Men","Supplier 0", BigDecimal("314")) addProduct(2, "description Product 2", 3, "tax 2", "Women","Supplier 0", BigDecimal("180")) addProduct(3, "description Product 3", 1, "tax 3", "Children","Supplier 0", BigDecimal("65")) + for (i in 4..499) { + val description = "description Product $i" + val category = (1..5).random() + val tax = "tax $category" + val gender = listOf("Men", "Women", "Children").random() + val supplier = listOf("Supplier 0", "Supplier 1", "Supplier 2").random() + val price = (50..500).random().toBigDecimal() + addProduct(i, description, category, tax, gender, supplier, price) + } } fun addSales() { diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Tables.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Tables.kt index 5a28482e9..63b1df1b5 100644 --- a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Tables.kt +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Tables.kt @@ -127,4 +127,4 @@ object TaxRule : Table("TAX_RULE") { override val primaryKey = PrimaryKey(idTaxe) } -val TASKId = Sequence("TASKId") +val TASKId = Sequence("TASKId") \ No newline at end of file diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/product/ProductForm.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/product/ProductForm.kt index be5ec81e0..a826e7968 100644 --- a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/product/ProductForm.kt +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/product/ProductForm.kt @@ -81,6 +81,11 @@ class ProductForm : DictionaryForm(title = "Products", locale = Locale.UK) { ProductR() } } + command(item = pivotTable) { + createPivotTable { + ProductP() + } + } } } diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/product/ProductP.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/product/ProductP.kt new file mode 100644 index 000000000..92d60e764 --- /dev/null +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/product/ProductP.kt @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.kopi.galite.demo.product + +import java.util.Locale + +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.transaction +import org.kopi.galite.demo.database.Product +import org.kopi.galite.visual.domain.DECIMAL +import org.kopi.galite.visual.domain.STRING +import org.kopi.galite.visual.dsl.common.Icon +import org.kopi.galite.visual.dsl.form.Key +import org.kopi.galite.visual.dsl.pivottable.PivotTable +import org.kopi.galite.visual.dsl.pivottable.Dimension.Position + +/** + * Product Report + */ +class ProductP : PivotTable(title = "Products", locale = Locale.UK) { + + val action = menu("Action") + + val quit = actor(menu = action, label = "Quit", help = "Quit", ident = "quit") { + key = Key.F1 + icon = Icon.QUIT + } + + val cmdQuit = command(item = quit) { + model.close() + } + + val category = dimension(STRING(10), Position.ROW) { + label = "Category" + help = "The product category" + } + + val department = dimension(STRING(20), Position.COLUMN) { + label = "Department" + help = "The product department" + } + + val supplier = dimension(STRING(20), Position.COLUMN) { + label = "Supplier" + help = "The supplier" + } + + val taxName = dimension(STRING(10), Position.COLUMN) { + label = "Tax" + help = "The product tax name" + } + + val price = measure(DECIMAL(10, 5)) { + label = "Price" + help = "The product unit price excluding VAT" + } + + val products = Product.selectAll() + + init { + transaction { + products.forEach { result -> + add { + this[department] = result[Product.department] + this[supplier] = result[Product.supplier] + this[category] = décoderCategory(result[Product.category]) + this[taxName] = décoderTaxe(result[Product.taxName]) + this[price] = result[Product.price] + } + } + } + } + fun décoderCategory(category: Int) : String { + var result: String + when (category) { + 1 -> result= "shoes" + 2 -> result = "shirts" + 3 -> result = "glasses" + 4 -> result = "pullovers" + 5 -> result = "jeans" + else -> result = "inconnu" + } + return result + } + + fun décoderTaxe(taxe: String) : String { + var result: String + when (taxe) { + "tax 0" -> result= "0%" + "tax 1" -> result= "19%" + "tax 2" -> result = "9%" + "tax 3" -> result = "13%" + "tax 4" -> result = "22%" + "tax 5" -> result = "11%" + else -> result = "inconnu" + } + return result + } +} diff --git a/galite-demo/galite-vaadin/src/main/resources/org/kopi/galite/demo/client/ClientP-fr_FR.xml b/galite-demo/galite-vaadin/src/main/resources/org/kopi/galite/demo/client/ClientP-fr_FR.xml new file mode 100644 index 000000000..cf1d16026 --- /dev/null +++ b/galite-demo/galite-vaadin/src/main/resources/org/kopi/galite/demo/client/ClientP-fr_FR.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/galite-demo/galite-vaadin/src/main/resources/org/kopi/galite/demo/product/ProductP-fr_FR.xml b/galite-demo/galite-vaadin/src/main/resources/org/kopi/galite/demo/product/ProductP-fr_FR.xml new file mode 100644 index 000000000..a7d963d44 --- /dev/null +++ b/galite-demo/galite-vaadin/src/main/resources/org/kopi/galite/demo/product/ProductP-fr_FR.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/galite-demo/galite-vaadin/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/form/FormTests.kt b/galite-demo/galite-vaadin/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/form/FormTests.kt index 688ff5252..bb684d253 100644 --- a/galite-demo/galite-vaadin/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/form/FormTests.kt +++ b/galite-demo/galite-vaadin/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/form/FormTests.kt @@ -110,7 +110,7 @@ class FormTests: GaliteVUITestBase() { arrayOf("3", "description Product 3", "3", "65,00000") ) - data.forEachIndexed { index, row -> + data.take(4).forEachIndexed { index, row -> block.grid.expectRow(index, *row) } } diff --git a/galite-tests/build.gradle.kts b/galite-tests/build.gradle.kts index f2e0c610c..537cfb20e 100644 --- a/galite-tests/build.gradle.kts +++ b/galite-tests/build.gradle.kts @@ -65,6 +65,9 @@ dependencies { // Vaadin addons dependency testImplementation("com.vaadin.componentfactory", "enhanced-dialog", Versions.ENHANCED_DIALOG) testImplementation("org.vaadin.stefan", "fullcalendar2", Versions.FULL_CALENDAR) + + // Pivot Table dependency + implementation("org.vaadin.addons.componentfactory", "pivottable-flow", Versions.PIVOT_TABLE) } tasks { diff --git a/galite-tests/src/main/resources/org/kopi/galite/test/Menu-en_GB.xml b/galite-tests/src/main/resources/org/kopi/galite/test/Menu-en_GB.xml index 559cdda3c..f8ee0e3e3 100644 --- a/galite-tests/src/main/resources/org/kopi/galite/test/Menu-en_GB.xml +++ b/galite-tests/src/main/resources/org/kopi/galite/test/Menu-en_GB.xml @@ -4,6 +4,7 @@ + diff --git a/galite-tests/src/test/kotlin/org/kopi/galite/tests/examples/CommandsForm.kt b/galite-tests/src/test/kotlin/org/kopi/galite/tests/examples/CommandsForm.kt index 558485bf0..ff392791c 100644 --- a/galite-tests/src/test/kotlin/org/kopi/galite/tests/examples/CommandsForm.kt +++ b/galite-tests/src/test/kotlin/org/kopi/galite/tests/examples/CommandsForm.kt @@ -71,14 +71,6 @@ class CommandsForm : DictionaryForm(title = "Commands Form", locale = Locale.UK) key = Key.F7 icon = Icon.DETAIL_VIEW } - val pivottable = actor( - menu = actionMenu, - label = "Pivot table", - help = " Pivot table", - ) { - key = Key.F8 - icon = Icon.REPORT - } val helpCmd = command(item = help) { showHelp() @@ -117,9 +109,6 @@ class CommandsForm : DictionaryForm(title = "Commands Form", locale = Locale.UK) command(item = insertMode) { insertMode() } - command(item = pivottable) { - PivotTableExample().doNotModal() - } } } diff --git a/galite-tests/src/test/kotlin/org/kopi/galite/tests/examples/DocumentationReport.kt b/galite-tests/src/test/kotlin/org/kopi/galite/tests/examples/DocumentationReport.kt index 4030fd2de..997326bb6 100644 --- a/galite-tests/src/test/kotlin/org/kopi/galite/tests/examples/DocumentationReport.kt +++ b/galite-tests/src/test/kotlin/org/kopi/galite/tests/examples/DocumentationReport.kt @@ -20,9 +20,7 @@ import java.util.Locale import org.kopi.galite.tests.desktop.runForm import org.kopi.galite.visual.domain.INT -import org.kopi.galite.visual.dsl.common.Icon import org.kopi.galite.visual.dsl.form.Block -import org.kopi.galite.visual.dsl.form.Key import org.kopi.galite.visual.dsl.form.ReportSelectionForm class DocumentationReport : ReportSelectionForm(title = "Test Report Form", locale = Locale.UK) { diff --git a/galite-tests/src/test/kotlin/org/kopi/galite/tests/examples/PivotTableExample.kt b/galite-tests/src/test/kotlin/org/kopi/galite/tests/examples/PivotTableExample.kt deleted file mode 100644 index 314666b32..000000000 --- a/galite-tests/src/test/kotlin/org/kopi/galite/tests/examples/PivotTableExample.kt +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.kopi.galite.tests.examples - -import java.util.Locale - -import org.kopi.galite.visual.domain.INT -import org.kopi.galite.visual.domain.STRING -import org.kopi.galite.visual.pivottable.Function -import org.kopi.galite.visual.pivottable.Grouping -import org.kopi.galite.visual.pivottable.PivotTable - -class PivotTableExample : PivotTable(title = "Form to test Blocks", locale = Locale.UK) { - val city = field(STRING(15)) { - label = "Country" - help = "The country" - } - - val delegation = field(STRING(15)) { - label = "Delegation" - help = "The delegation" - } - - val gender = field(STRING(6)) { - label = "Gender" - help = "The gender" - } - - val age = field(INT(3)) { - label = "Age" - help = "The age" - } - - init { - aggregate(Function.SUM, age) - grouping = Grouping(listOf(city), listOf(delegation, gender)) - - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Rades" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Tunis" - this[delegation] = "Notre damme" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Rades" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Tunis" - this[delegation] = "Notre damme" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Tunis" - this[delegation] = "Notre damme" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Green" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Tunis" - this[delegation] = "Notre damme" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Female" - this[age] = 22 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Mourouj" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Male" - this[age] = 26 - } - add { - this[city] = "Ben Arous" - this[delegation] = "Yassminet" - this[gender] = "Male" - this[age] = 26 - } - } -} diff --git a/gradle.properties b/gradle.properties index ce7b320fb..68ea47791 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.jvmargs=-Xmx4g # group=org.kopi -version=1.4.1 +version=1.4.1-01QYB