1
package org.hisp.dhis.reporttable;
4
* Copyright (c) 2004-2007, University of Oslo
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions are met:
9
* * Redistributions of source code must retain the above copyright notice, this
10
* list of conditions and the following disclaimer.
11
* * Redistributions in binary form must reproduce the above copyright notice,
12
* this list of conditions and the following disclaimer in the documentation
13
* and/or other materials provided with the distribution.
14
* * Neither the name of the HISP project nor the names of its contributors may
15
* be used to endorse or promote products derived from this software without
16
* specific prior written permission.
18
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
import java.io.Serializable;
31
import java.util.ArrayList;
32
import java.util.List;
33
import java.util.regex.Matcher;
34
import java.util.regex.Pattern;
36
import org.hisp.dhis.common.MetaObject;
37
import org.hisp.dhis.dataelement.DataElement;
38
import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo;
39
import org.hisp.dhis.dataset.DataSet;
40
import org.hisp.dhis.i18n.I18nFormat;
41
import org.hisp.dhis.indicator.Indicator;
42
import org.hisp.dhis.organisationunit.OrganisationUnit;
43
import org.hisp.dhis.period.Period;
46
* @author Lars Helge Overland
49
public class ReportTable
50
implements Serializable
52
public static final String DATAELEMENT_ID = "dataelementid";
53
public static final String DATAELEMENT_NAME = "dataelementname";
54
public static final String CATEGORYCOMBO_ID = "categoryoptioncomboid";
55
public static final String CATEGORYCOMBO_NAME = "categoryoptioncomboname";
56
public static final String INDICATOR_ID = "indicatorid";
57
public static final String INDICATOR_NAME = "indicatorname";
58
public static final String DATASET_ID = "datasetid";
59
public static final String DATASET_NAME = "datasetname";
60
public static final String PERIOD_ID = "periodid";
61
public static final String PERIOD_NAME = "periodname";
62
public static final String ORGANISATIONUNIT_ID = "organisationunitid";
63
public static final String ORGANISATIONUNIT_NAME = "organisationunitname";
65
public static final String SEPARATOR = "_";
67
public static final String MODE_DATAELEMENTS = "dataelements";
68
public static final String MODE_INDICATORS = "indicators";
69
public static final String MODE_DATASETS = "datasets";
71
public static final String REGRESSION_COLUMN_PREFIX = "regression_";
73
private static final String EMPTY_REPLACEMENT = "_";
74
private static final String TABLE_PREFIX = "_report_";
75
private static final String REGEX_NUMERIC = "([0-9]*)";
77
// -------------------------------------------------------------------------
78
// Persisted properties
79
// -------------------------------------------------------------------------
85
private String tableName;
87
private String existingTableName;
91
private Boolean regression;
93
private List<DataElement> dataElements = new ArrayList<DataElement>();
95
private List<DataElementCategoryOptionCombo> categoryOptionCombos = new ArrayList<DataElementCategoryOptionCombo>();
97
private List<Indicator> indicators = new ArrayList<Indicator>();
99
private List<DataSet> dataSets = new ArrayList<DataSet>();
101
private List<Period> periods = new ArrayList<Period>();
103
private List<OrganisationUnit> units = new ArrayList<OrganisationUnit>();
105
private Boolean doIndicators;
107
private Boolean doCategoryOptionCombos;
109
private Boolean doPeriods;
111
private Boolean doUnits;
113
private RelativePeriods relatives;
115
private ReportParams reportParams;
117
// -------------------------------------------------------------------------
118
// Transient properties
119
// -------------------------------------------------------------------------
122
* Periods relative to the reporting month.
124
private List<Period> relativePeriods = new ArrayList<Period>();
127
* Static periods and relative periods.
129
private List<Period> allPeriods = new ArrayList<Period>();
132
* Indicators that will be crosstabulated on the columns axis. Indicators
133
* comprises dataelements, indicators, datasets.
135
private List<MetaObject> crossTabIndicators = new ArrayList<MetaObject>();
138
* CategoryCombos that will be crosstabulated on the columns axis. Optional dimension.
140
private List<DataElementCategoryOptionCombo> crossTabCategoryOptionCombos = new ArrayList<DataElementCategoryOptionCombo>();
143
* Periods that will be crosstabulated on the columns axis. Mandatory dimension.
145
private List<Period> crossTabPeriods = new ArrayList<Period>();
148
* OrganisationUnits that will be crosstabulated on the columns axis. Mandatory dimension.
150
private List<OrganisationUnit> crossTabUnits = new ArrayList<OrganisationUnit>();
153
* Indicators that will be present on the rows axis.
155
private List<MetaObject> reportIndicators = new ArrayList<MetaObject>();
158
* CategoryOptionCombos that will be present on the rows axis. Optional dimension.
160
private List<DataElementCategoryOptionCombo> reportCategoryOptionCombos = new ArrayList<DataElementCategoryOptionCombo>();
163
* Periods that will be present on the rows axis. Mandatory dimension.
165
private List<Period> reportPeriods = new ArrayList<Period>();
168
* OrganisationUnits that will be present on the rows axis. Mandatory dimension.
170
private List<OrganisationUnit> reportUnits = new ArrayList<OrganisationUnit>();
173
* Names of the columns used to query the datavalue table and as index columns
174
* in the report table.
176
private List<String> indexColumns = new ArrayList<String>();
179
* Names of the columns holding entry names used to query the datavalue table.
181
private List<String> indexNameColumns = new ArrayList<String>();
184
* Names of the columns which should be retrieved from the datavalue table.
186
private List<String> selectColumns = new ArrayList<String>();
189
* Generated names for crosstabulated columns in the report table.
191
private List<String> crossTabColumns = new ArrayList<String>();
194
* Generated unique identifiers used to retrieve the corresponding value from the datavalue table.
196
private List<String> crossTabIdentifiers = new ArrayList<String>();
199
* The I18nFormat used for internationalization of ie. periods.
201
private I18nFormat i18nFormat;
204
* The name of the reporting month.
206
private String reportingMonthName;
208
// -------------------------------------------------------------------------
210
// -------------------------------------------------------------------------
213
* Constructor for persistence purposes.
220
* Constructor for testing purposes.
222
* @param name the name.
223
* @param tableName the table name.
225
public ReportTable( String name, String tableName )
228
this.tableName = tableName;
232
* Default constructor.
234
* @param name the name.
235
* @param mode the mode.
236
* @param dataElements the data elements.
237
* @param indicators the indicators.
238
* @param dataSets the datasets.
239
* @param categoryOptionCombos the category option combos.
240
* @param periods the periods. These periods cannot have the name property set.
241
* @param relativePeriods the relative periods. These periods must have the name property set. Not persisted.
242
* @param units the organisation units.
243
* @param doIndicators indicating whether indicators should be crosstabulated.
244
* @param doCategoryOptionCombos indicating whether category option combos should be crosstabulated.
245
* @param doPeriods indicating whether periods should be crosstabulated.
246
* @param doUnits indicating whether organisation units should be crosstabulated.
247
* @param relatives the relative periods.
248
* @param i18nFormat the i18n format. Not persisted.
249
* @param reportingMonthName the reporting month name. Not persisted.
251
public ReportTable( String name,
254
List<DataElement> dataElements,
255
List<Indicator> indicators,
256
List<DataSet> dataSets,
257
List<DataElementCategoryOptionCombo> categoryOptionCombos,
258
List<Period> periods,
259
List<Period> relativePeriods,
260
List<OrganisationUnit> units,
261
boolean doIndicators,
262
boolean doCategoryOptionCombos,
265
RelativePeriods relatives,
266
ReportParams reportParams,
267
I18nFormat i18nFormat,
268
String reportingMonthName )
271
this.tableName = generateTableName( name );
272
this.existingTableName = generateTableName( name );
274
this.regression = regression;
275
this.dataElements = dataElements;
276
this.indicators = indicators;
277
this.dataSets = dataSets;
278
this.categoryOptionCombos = categoryOptionCombos;
279
this.periods = periods;
280
this.relativePeriods = relativePeriods;
282
this.doIndicators = doIndicators;
283
this.doCategoryOptionCombos = doCategoryOptionCombos;
284
this.doPeriods = doPeriods;
285
this.doUnits = doUnits;
286
this.relatives = relatives;
287
this.reportParams = reportParams;
288
this.i18nFormat = i18nFormat;
289
this.reportingMonthName = reportingMonthName;
294
// -------------------------------------------------------------------------
296
// -------------------------------------------------------------------------
300
if ( nonEmptyLists( dataElements, indicators, dataSets ) > 1 )
302
throw new IllegalArgumentException( "ReportTable cannot contain more than one out of dataelements, indicators, and datasets" );
305
if ( listIsNonEmpty( categoryOptionCombos ) && ( mode != null && !mode.equalsIgnoreCase( MODE_DATAELEMENTS ) ) )
307
throw new IllegalArgumentException( "ReportTable cannot contain category option combos when not in dataelement mode" );
310
// ---------------------------------------------------------------------
311
// Init tableName and allPeriods
312
// ---------------------------------------------------------------------
314
this.tableName = generateTableName( name );
316
allPeriods.addAll( periods );
317
allPeriods.addAll( relativePeriods );
319
// ---------------------------------------------------------------------
320
// Init indexColumns and selectColumns
321
// ---------------------------------------------------------------------
323
if ( isDoIndicators() )
325
crossTabIndicators = new ArrayList<MetaObject>();
326
crossTabIndicators.addAll( indicators );
327
crossTabIndicators.addAll( dataElements );
328
crossTabIndicators.addAll( dataSets );
329
reportIndicators.add( null );
330
selectColumns.add( getIdentifier( mode ) );
334
crossTabIndicators.add( null );
335
reportIndicators = new ArrayList<MetaObject>();
336
reportIndicators.addAll( indicators );
337
reportIndicators.addAll( dataElements );
338
reportIndicators.addAll( dataSets );
339
indexColumns.add( getIdentifier( mode ) );
340
indexNameColumns.add( getName( mode ) );
343
if ( isDoCategoryOptionCombos() )
345
reportCategoryOptionCombos.add( null );
347
if ( listIsNonEmpty( categoryOptionCombos ) ) // Optional dimension
349
crossTabCategoryOptionCombos = new ArrayList<DataElementCategoryOptionCombo>( categoryOptionCombos );
350
selectColumns.add( CATEGORYCOMBO_ID );
354
crossTabCategoryOptionCombos.add( null );
359
crossTabCategoryOptionCombos.add( null );
361
if ( listIsNonEmpty( categoryOptionCombos ) ) // Optional dimension
363
reportCategoryOptionCombos = new ArrayList<DataElementCategoryOptionCombo>( categoryOptionCombos );
364
indexColumns.add( CATEGORYCOMBO_ID );
365
indexNameColumns.add( CATEGORYCOMBO_NAME );
369
reportCategoryOptionCombos.add( null );
375
crossTabPeriods = new ArrayList<Period>( periods );
376
crossTabPeriods.addAll( relativePeriods );
377
reportPeriods.add( null );
378
selectColumns.add( PERIOD_ID );
382
crossTabPeriods.add( null );
383
reportPeriods = new ArrayList<Period>( periods );
384
reportPeriods.addAll( relativePeriods );
385
indexColumns.add( PERIOD_ID );
386
indexNameColumns.add( PERIOD_NAME );
391
crossTabUnits = new ArrayList<OrganisationUnit>( units );
392
reportUnits.add( null );
393
selectColumns.add( ORGANISATIONUNIT_ID );
397
crossTabUnits.add( null );
398
reportUnits = new ArrayList<OrganisationUnit>( units );
399
indexColumns.add( ORGANISATIONUNIT_ID );
400
indexNameColumns.add( ORGANISATIONUNIT_NAME );
403
// ---------------------------------------------------------------------
404
// Init crossTabColumns and crossTabIdentifiers
405
// ---------------------------------------------------------------------
407
for ( MetaObject indicator : crossTabIndicators )
409
for ( DataElementCategoryOptionCombo categoryOptionCombo : crossTabCategoryOptionCombos )
411
for ( Period period : crossTabPeriods )
413
for ( OrganisationUnit unit : crossTabUnits )
415
String columnName = getColumnName( indicator, categoryOptionCombo, period, unit );
416
String columnIdentifier = getColumnIdentifier( indicator, categoryOptionCombo, period, unit );
418
crossTabColumns.add( columnName );
419
crossTabIdentifiers.add( columnIdentifier );
426
// -------------------------------------------------------------------------
428
// -------------------------------------------------------------------------
430
public boolean isRegression()
432
return regression != null && regression;
435
public void updateExistingTableName()
437
this.existingTableName = generateTableName( name );
440
public boolean hasCategoryOptionCombos()
442
return categoryOptionCombos != null && categoryOptionCombos.size() > 0;
445
public boolean isDoIndicators()
447
return doIndicators != null && doIndicators;
450
public boolean isDoCategoryOptionCombos()
452
return doCategoryOptionCombos != null && doCategoryOptionCombos;
455
public boolean isDoPeriods()
457
return doPeriods != null && doPeriods;
460
public boolean isDoUnits()
462
return doUnits != null && doUnits;
465
// -------------------------------------------------------------------------
466
// Supportive methods
467
// -------------------------------------------------------------------------
469
private String generateTableName( String name )
471
return TABLE_PREFIX + databaseEncode( name );
474
private String getIdentifier( String mode )
476
if ( mode == null || mode.equals( MODE_INDICATORS ) )
480
else if ( mode.equals( MODE_DATAELEMENTS ) )
482
return DATAELEMENT_ID;
484
else if ( mode.equals( MODE_DATASETS ) )
492
private String getName( String mode )
494
if ( mode == null || mode.equals( MODE_INDICATORS ) )
496
return INDICATOR_NAME;
498
else if ( mode.equals( MODE_DATAELEMENTS ) )
500
return DATAELEMENT_NAME;
502
else if ( mode.equals( MODE_DATASETS ) )
510
private int nonEmptyLists( List<?>... lists )
514
for ( List<?> list : lists )
516
if ( list != null && list.size() > 0 )
525
private boolean listIsNonEmpty( List<?> list )
527
return list != null && list.size() > 0;
530
private String getColumnName( MetaObject metaObject, DataElementCategoryOptionCombo categoryOptionCombo, Period period, OrganisationUnit unit )
532
StringBuffer buffer = new StringBuffer();
534
if ( metaObject != null )
536
buffer.append( databaseEncode( metaObject.getShortName() ) + SEPARATOR );
538
if ( categoryOptionCombo != null )
540
buffer.append( databaseEncode( categoryOptionCombo.getShortName() ) + SEPARATOR );
542
if ( period != null )
544
String periodName = period.getName() != null ? period.getName() : i18nFormat.formatPeriod( period );
546
buffer.append( databaseEncode( periodName ) + SEPARATOR );
550
buffer.append( databaseEncode( unit.getShortName() ) + SEPARATOR );
553
// ---------------------------------------------------------------------
554
// Columns cannot start with numeric character
555
// ---------------------------------------------------------------------
557
if ( buffer.length() > 0 && buffer.substring( 0, 1 ).matches( REGEX_NUMERIC ) )
559
buffer.insert( 0, SEPARATOR );
562
return buffer.length() > 0 ? buffer.substring( 0, buffer.lastIndexOf( SEPARATOR ) ) : buffer.toString();
565
private String getColumnIdentifier( MetaObject metaObject, DataElementCategoryOptionCombo categoryOptionCombo, Period period, OrganisationUnit unit )
567
StringBuffer buffer = new StringBuffer();
569
if ( metaObject != null )
571
buffer.append( metaObject.getId() + SEPARATOR );
573
if ( categoryOptionCombo != null )
575
buffer.append( categoryOptionCombo.getId() + SEPARATOR );
577
if ( period != null )
579
buffer.append( period.getId() + SEPARATOR );
583
buffer.append( unit.getId() + SEPARATOR );
586
return buffer.length() > 0 ? buffer.substring( 0, buffer.lastIndexOf( SEPARATOR ) ) : buffer.toString();
589
private String databaseEncode( String string )
591
if ( string != null )
593
string = string.toLowerCase();
595
string = string.replaceAll( " ", EMPTY_REPLACEMENT );
596
string = string.replaceAll( "<", EMPTY_REPLACEMENT + "lt" + EMPTY_REPLACEMENT );
597
string = string.replaceAll( ">", EMPTY_REPLACEMENT + "gt" + EMPTY_REPLACEMENT );
599
StringBuffer buffer = new StringBuffer();
601
Pattern pattern = Pattern.compile( "[a-zA-Z0-9_]" );
603
Matcher matcher = pattern.matcher( string );
605
while ( matcher.find() )
607
buffer.append( matcher.group() );
610
string = buffer.toString();
612
string = string.replaceAll( EMPTY_REPLACEMENT + "+", EMPTY_REPLACEMENT );
614
if ( string.length() > 255 )
616
string = string.substring( 0, 255 );
623
// -------------------------------------------------------------------------
624
// Equals and hashCode
625
// -------------------------------------------------------------------------
628
public int hashCode()
630
final int PRIME = 31;
634
result = PRIME * result + ( ( name == null ) ? 0 : name.hashCode() );
640
public boolean equals( Object object )
642
if ( this == object )
647
if ( object == null )
652
if ( getClass() != object.getClass() )
657
final ReportTable other = (ReportTable) object;
659
return name.equals( other.getName() );
662
// -------------------------------------------------------------------------
663
// Get- and set-methods for persisted properties
664
// -------------------------------------------------------------------------
671
public void setId( int id )
676
public String getName()
681
public void setName( String name )
686
public String getTableName()
691
public void setTableName( String tableName )
693
this.tableName = tableName;
696
public String getExistingTableName()
698
return existingTableName;
701
public void setExistingTableName( String existingTableName )
703
this.existingTableName = existingTableName;
706
public String getMode()
711
public void setMode( String mode )
716
public Boolean getRegression()
721
public void setRegression( Boolean regression )
723
this.regression = regression;
726
public List<DataElement> getDataElements()
731
public void setDataElements( List<DataElement> dataElements )
733
this.dataElements = dataElements;
736
public List<DataElementCategoryOptionCombo> getCategoryOptionCombos()
738
return categoryOptionCombos;
741
public void setCategoryOptionCombos( List<DataElementCategoryOptionCombo> categoryOptionCombos )
743
this.categoryOptionCombos = categoryOptionCombos;
746
public List<Indicator> getIndicators()
751
public void setIndicators( List<Indicator> indicators )
753
this.indicators = indicators;
756
public List<Period> getPeriods()
761
public List<DataSet> getDataSets()
766
public void setDataSets( List<DataSet> dataSets )
768
this.dataSets = dataSets;
771
public void setPeriods( List<Period> periods )
773
this.periods = periods;
776
public List<OrganisationUnit> getUnits()
781
public void setUnits( List<OrganisationUnit> units )
786
public Boolean getDoIndicators()
791
public void setDoIndicators( Boolean doIndicators )
793
this.doIndicators = doIndicators;
796
public Boolean getDoCategoryOptionCombos()
798
return doCategoryOptionCombos;
801
public void setDoCategoryOptionCombos( Boolean doCategoryOptionCombos )
803
this.doCategoryOptionCombos = doCategoryOptionCombos;
806
public Boolean getDoPeriods()
811
public void setDoPeriods( Boolean doPeriods )
813
this.doPeriods = doPeriods;
816
public Boolean getDoUnits()
821
public void setDoUnits( Boolean doUnits )
823
this.doUnits = doUnits;
826
public RelativePeriods getRelatives()
831
public void setRelatives( RelativePeriods relatives )
833
this.relatives = relatives;
836
public ReportParams getReportParams()
841
public void setReportParams( ReportParams reportParams )
843
this.reportParams = reportParams;
846
// -------------------------------------------------------------------------
847
// Get- and set-methods for transient properties
848
// -------------------------------------------------------------------------
850
public List<Period> getRelativePeriods()
852
return relativePeriods;
855
public void setRelativePeriods( List<Period> relativePeriods )
857
this.relativePeriods = relativePeriods;
860
public List<Period> getAllPeriods()
865
public I18nFormat getI18nFormat()
870
public void setI18nFormat( I18nFormat format )
875
public List<MetaObject> getReportIndicators()
877
return reportIndicators;
880
public List<DataElementCategoryOptionCombo> getReportCategoryOptionCombos()
882
return reportCategoryOptionCombos;
885
public List<Period> getReportPeriods()
887
return reportPeriods;
890
public List<OrganisationUnit> getReportUnits()
895
public List<String> getIndexColumns()
900
public List<String> getIndexNameColumns()
902
return indexNameColumns;
905
public List<String> getSelectColumns()
907
return selectColumns;
910
public List<String> getCrossTabColumns()
912
return crossTabColumns;
915
public List<String> getCrossTabIdentifiers()
917
return crossTabIdentifiers;
920
public String getReportingMonthName()
922
return reportingMonthName;
925
public void setReportingMonthName( String reportingMonthName )
927
this.reportingMonthName = reportingMonthName;