~mortenoh/+junk/dhis2-detailed-import-export

« back to all changes in this revision

Viewing changes to dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/expression/DefaultExpressionService.java

  • Committer: larshelge at gmail
  • Date: 2009-03-03 16:46:36 UTC
  • Revision ID: larshelge@gmail.com-20090303164636-2sjlrquo7ib1gf7r
Initial check-in

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package org.hisp.dhis.expression;
 
2
 
 
3
/*
 
4
 * Copyright (c) 2004-2007, University of Oslo
 
5
 * All rights reserved.
 
6
 *
 
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.
 
17
 *
 
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.
 
28
 */
 
29
 
 
30
import static org.hisp.dhis.expression.Expression.SEPARATOR;
 
31
import static org.hisp.dhis.system.util.MathUtils.calculateExpression;
 
32
 
 
33
import java.util.Collection;
 
34
import java.util.HashSet;
 
35
import java.util.Iterator;
 
36
import java.util.Map;
 
37
import java.util.Set;
 
38
import java.util.regex.Matcher;
 
39
import java.util.regex.Pattern;
 
40
 
 
41
import org.hisp.dhis.dataelement.CalculatedDataElement;
 
42
import org.hisp.dhis.dataelement.DataElement;
 
43
import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo;
 
44
import org.hisp.dhis.dataelement.DataElementCategoryOptionComboService;
 
45
import org.hisp.dhis.dataelement.DataElementService;
 
46
import org.hisp.dhis.dataelement.Operand;
 
47
import org.hisp.dhis.datavalue.DataValue;
 
48
import org.hisp.dhis.datavalue.DataValueService;
 
49
import org.hisp.dhis.period.Period;
 
50
import org.hisp.dhis.source.Source;
 
51
import org.hisp.dhis.system.util.MathUtils;
 
52
 
 
53
/**
 
54
 * @author Margrethe Store
 
55
 * @author Lars Helge Overland
 
56
 * @version $Id: DefaultExpressionService.java 6463 2008-11-24 12:05:46Z larshelg $
 
57
 */
 
58
public class DefaultExpressionService
 
59
    implements ExpressionService
 
60
{
 
61
    private static final String NULL_REPLACEMENT = "0";
 
62
 
 
63
    // -------------------------------------------------------------------------
 
64
    // Dependencies
 
65
    // -------------------------------------------------------------------------
 
66
 
 
67
    private ExpressionStore expressionStore;
 
68
 
 
69
    public void setExpressionStore( ExpressionStore expressionStore )
 
70
    {
 
71
        this.expressionStore = expressionStore;
 
72
    }
 
73
 
 
74
    private DataElementService dataElementService;
 
75
 
 
76
    public void setDataElementService( DataElementService dataElementService )
 
77
    {
 
78
        this.dataElementService = dataElementService;
 
79
    }
 
80
 
 
81
    private DataValueService dataValueService;
 
82
 
 
83
    public void setDataValueService( DataValueService dataValueService )
 
84
    {
 
85
        this.dataValueService = dataValueService;
 
86
    }
 
87
 
 
88
    private DataElementCategoryOptionComboService categoryOptionComboService;
 
89
 
 
90
    public void setCategoryOptionComboService( DataElementCategoryOptionComboService dataElementCategoryOptionComboService )
 
91
    {
 
92
        this.categoryOptionComboService = dataElementCategoryOptionComboService;
 
93
    }
 
94
 
 
95
    // -------------------------------------------------------------------------
 
96
    // Expression CRUD operations
 
97
    // -------------------------------------------------------------------------
 
98
 
 
99
    public int addExpression( Expression expression )
 
100
    {
 
101
        return expressionStore.addExpression( expression );
 
102
    }
 
103
 
 
104
    public void deleteExpression( Expression expression )
 
105
    {
 
106
        expressionStore.deleteExpression( expression );
 
107
    }
 
108
 
 
109
    public Expression getExpression( int id )
 
110
    {
 
111
        return expressionStore.getExpression( id );
 
112
    }
 
113
 
 
114
    public void updateExpression( Expression expression )
 
115
    {
 
116
        expressionStore.updateExpression( expression );
 
117
    }
 
118
 
 
119
    public Collection<Expression> getAllExpressions()
 
120
    {
 
121
        return expressionStore.getAllExpressions();
 
122
    }
 
123
 
 
124
    // -------------------------------------------------------------------------
 
125
    // Business logic
 
126
    // -------------------------------------------------------------------------
 
127
 
 
128
    public Double getExpressionValue( Expression expression, Period period, Source source )
 
129
    {
 
130
        String expressionString = generateExpression( expression.getExpression(), period, source );
 
131
 
 
132
        return expressionString != null ? calculateExpression( expressionString ) : null;
 
133
    }
 
134
 
 
135
    public Set<DataElement> getDataElementsInExpression( String expression )
 
136
    {
 
137
        Set<DataElement> dataElementsInExpression = null;
 
138
 
 
139
        if ( expression != null )
 
140
        {
 
141
            dataElementsInExpression = new HashSet<DataElement>();
 
142
 
 
143
            Matcher matcher = getMatcher( "(\\[\\d+\\" + SEPARATOR + "\\d+\\])", expression );
 
144
 
 
145
            while ( matcher.find() )
 
146
            {
 
147
                String replaceString = matcher.group();
 
148
 
 
149
                replaceString = replaceString.replaceAll( "[\\[\\]]", "" );
 
150
 
 
151
                replaceString = replaceString.substring( 0, replaceString.indexOf( SEPARATOR ) );
 
152
 
 
153
                int dataElementId = Integer.parseInt( replaceString );
 
154
 
 
155
                DataElement dataElement = dataElementService.getDataElement( dataElementId );
 
156
 
 
157
                if ( dataElement != null )
 
158
                {
 
159
                    dataElementsInExpression.add( dataElement );
 
160
                }
 
161
            }
 
162
        }
 
163
 
 
164
        return dataElementsInExpression;
 
165
    }
 
166
    
 
167
    public String convertExpression( String expression, Map<Object, Integer> dataElementMapping, Map<Object, Integer> categoryOptionComboMapping )
 
168
    {
 
169
        StringBuffer convertedFormula = new StringBuffer();
 
170
        
 
171
        if ( expression != null )
 
172
        {
 
173
            Matcher matcher = getMatcher( "(\\[\\d+\\" + SEPARATOR + "\\d+\\])", expression );
 
174
 
 
175
            while ( matcher.find() )
 
176
            {
 
177
                String match = matcher.group();
 
178
 
 
179
                match = match.replaceAll( "[\\[\\]]", "" );
 
180
 
 
181
                String dataElementIdString = match.substring( 0, match.indexOf( SEPARATOR ) );
 
182
                String categoryOptionComboIdString = match.substring( match.indexOf( SEPARATOR ) + 1, match.length() );
 
183
 
 
184
                int dataElementId = Integer.parseInt( dataElementIdString );
 
185
                int categoryOptionComboId = Integer.parseInt( categoryOptionComboIdString );
 
186
                
 
187
                dataElementId = dataElementMapping.get( dataElementId );
 
188
                categoryOptionComboId = categoryOptionComboMapping.get( categoryOptionComboId );
 
189
                
 
190
                match = "[" + dataElementId + SEPARATOR + categoryOptionComboId + "]";
 
191
                
 
192
                matcher.appendReplacement( convertedFormula, match );
 
193
            }
 
194
 
 
195
            matcher.appendTail( convertedFormula );
 
196
        }
 
197
        
 
198
        return convertedFormula.toString();
 
199
    }
 
200
 
 
201
    public Set<Operand> getOperandsInExpression( String expression )
 
202
    {
 
203
        Set<Operand> operandsInExpression = null;
 
204
 
 
205
        if ( expression != null )
 
206
        {
 
207
            operandsInExpression = new HashSet<Operand>();
 
208
 
 
209
            Matcher matcher = getMatcher( "(\\[\\d+\\" + SEPARATOR + "\\d+\\])", expression );
 
210
 
 
211
            while ( matcher.find() )
 
212
            {
 
213
                final Operand operand = new Operand();
 
214
                
 
215
                String match = matcher.group().replaceAll( "[\\[\\]]", "" );
 
216
 
 
217
                operand.setDataElementId( Integer.parseInt( match.substring( 0, match.indexOf( SEPARATOR ) ) ) );
 
218
                operand.setOptionComboId( Integer.parseInt( match.substring( match.indexOf( SEPARATOR ) + 1, match.length() ) ) );
 
219
                
 
220
                operandsInExpression.add( operand );
 
221
            }
 
222
        }
 
223
 
 
224
        return operandsInExpression;
 
225
    }
 
226
 
 
227
    public int expressionIsValid( String formula )
 
228
    {
 
229
        StringBuffer buffer = new StringBuffer();
 
230
        
 
231
        Matcher matcher = getMatcher( "\\[.+?\\" + SEPARATOR + ".+?\\]", formula );
 
232
 
 
233
        int dataElementId = -1;
 
234
        int categoryOptionComboId = -1;
 
235
 
 
236
        while ( matcher.find() )
 
237
        {
 
238
            String match = matcher.group();
 
239
 
 
240
            match = match.replaceAll( "[\\[\\]]", "" );
 
241
 
 
242
            String dataElementIdString = match.substring( 0, match.indexOf( SEPARATOR ) );
 
243
            String categoryOptionComboIdString = match.substring( match.indexOf( SEPARATOR ) + 1, match.length() );
 
244
 
 
245
            try
 
246
            {
 
247
                dataElementId = Integer.parseInt( dataElementIdString );
 
248
            }
 
249
            catch ( NumberFormatException ex )
 
250
            {
 
251
                return DATAELEMENT_ID_NOT_NUMERIC;
 
252
            }
 
253
 
 
254
            try
 
255
            {
 
256
                categoryOptionComboId = Integer.parseInt( categoryOptionComboIdString );
 
257
            }
 
258
            catch ( NumberFormatException ex )
 
259
            {
 
260
                return CATEGORYOPTIONCOMBO_ID_NOT_NUMERIC;
 
261
            }
 
262
 
 
263
            if ( dataElementService.getDataElement( dataElementId ) == null )
 
264
            {
 
265
                return DATAELEMENT_DOES_NOT_EXIST;
 
266
            }
 
267
 
 
268
            if ( categoryOptionComboService.getDataElementCategoryOptionCombo( categoryOptionComboId ) == null )
 
269
            {
 
270
                return CATEGORYOPTIONCOMBO_DOES_NOT_EXIST;
 
271
            }
 
272
 
 
273
            // -----------------------------------------------------------------
 
274
            // Replacing the operand with 1 in order to later be able to verify
 
275
            // that the formula is mathematically valid
 
276
            // -----------------------------------------------------------------
 
277
 
 
278
            matcher.appendReplacement( buffer, "1" );
 
279
        }
 
280
        
 
281
        matcher.appendTail( buffer );
 
282
        
 
283
        if ( MathUtils.expressionHasErrors( buffer.toString() ) )
 
284
        {
 
285
            return EXPRESSION_NOT_WELL_FORMED;
 
286
        }        
 
287
 
 
288
        return VALID;
 
289
    }
 
290
 
 
291
    public String getExpressionDescription( String formula )
 
292
    {
 
293
        StringBuffer buffer = null;
 
294
 
 
295
        if ( formula != null )
 
296
        {
 
297
            buffer = new StringBuffer();
 
298
 
 
299
            Matcher matcher = getMatcher( "\\[.+?\\" + SEPARATOR + ".+?\\]", formula );
 
300
 
 
301
            int dataElementId = -1;
 
302
            int categoryOptionComboId = -1;
 
303
 
 
304
            DataElement dataElement = null;
 
305
            DataElementCategoryOptionCombo categoryOptionCombo = null;
 
306
 
 
307
            while ( matcher.find() )
 
308
            {
 
309
                String replaceString = matcher.group();
 
310
 
 
311
                replaceString = replaceString.replaceAll( "[\\[\\]]", "" );
 
312
 
 
313
                String dataElementIdString = replaceString.substring( 0, replaceString.indexOf( SEPARATOR ) );
 
314
                String optionComboIdString = replaceString.substring( replaceString.indexOf( SEPARATOR ) + 1, replaceString.length() );
 
315
 
 
316
                try
 
317
                {
 
318
                    dataElementId = Integer.parseInt( dataElementIdString );
 
319
                }
 
320
                catch ( NumberFormatException ex )
 
321
                {
 
322
                    throw new IllegalArgumentException( "Data element identifier must be a number: " + replaceString );
 
323
                }
 
324
 
 
325
                try
 
326
                {
 
327
                    categoryOptionComboId = Integer.parseInt( optionComboIdString );
 
328
                }
 
329
                catch ( NumberFormatException ex )
 
330
                {
 
331
                    throw new IllegalArgumentException( "Category option combo identifier must be a number: "
 
332
                        + replaceString );
 
333
                }
 
334
 
 
335
                dataElement = dataElementService.getDataElement( dataElementId );
 
336
                categoryOptionCombo = categoryOptionComboService.getDataElementCategoryOptionCombo( categoryOptionComboId );
 
337
 
 
338
                if ( dataElement == null )
 
339
                {
 
340
                    throw new IllegalArgumentException( "Identifier does not reference a data element: "
 
341
                        + dataElementId );
 
342
                }
 
343
 
 
344
                if ( categoryOptionCombo == null )
 
345
                {
 
346
                    throw new IllegalArgumentException( "Identifier does not reference a category option combo: "
 
347
                        + categoryOptionComboId );
 
348
                }
 
349
 
 
350
                replaceString = dataElement.getName() + SEPARATOR + categoryOptionComboService.getOptionNames( categoryOptionCombo );
 
351
 
 
352
                if ( replaceString.endsWith( SEPARATOR ) )
 
353
                {
 
354
                    replaceString = replaceString.substring( 0, replaceString.length() - 1 );
 
355
                }
 
356
 
 
357
                matcher.appendReplacement( buffer, replaceString );
 
358
            }
 
359
 
 
360
            matcher.appendTail( buffer );
 
361
        }
 
362
 
 
363
        return buffer != null ? buffer.toString() : null;
 
364
    }
 
365
 
 
366
    public String replaceCDEsWithTheirExpression( String expression )
 
367
    {
 
368
        StringBuffer buffer = null;
 
369
 
 
370
        if ( expression != null )
 
371
        {
 
372
            buffer = new StringBuffer();
 
373
 
 
374
            Set<DataElement> caclulatedDataElementsInExpression = getDataElementsInExpression( expression );
 
375
 
 
376
            Iterator<DataElement> iterator = caclulatedDataElementsInExpression.iterator();
 
377
 
 
378
            while ( iterator.hasNext() )
 
379
            {
 
380
                DataElement dataElement = iterator.next();
 
381
 
 
382
                if ( !(dataElement instanceof CalculatedDataElement) )
 
383
                {
 
384
                    iterator.remove();
 
385
                }
 
386
            }
 
387
 
 
388
            Matcher matcher = getMatcher( "(\\[\\d+\\" + SEPARATOR + "\\d+\\])", expression );
 
389
 
 
390
            while ( matcher.find() )
 
391
            {
 
392
                String replaceString = matcher.group();
 
393
 
 
394
                for ( DataElement dataElement : caclulatedDataElementsInExpression )
 
395
                {
 
396
                    if ( replaceString.startsWith( "[" + dataElement.getId() + SEPARATOR ) )
 
397
                    {
 
398
                        CalculatedDataElement calculatedDataElement = (CalculatedDataElement) dataElement;
 
399
 
 
400
                        replaceString = calculatedDataElement.getExpression().getExpression();
 
401
 
 
402
                        break;
 
403
                    }
 
404
                }
 
405
 
 
406
                matcher.appendReplacement( buffer, replaceString );
 
407
            }
 
408
 
 
409
            matcher.appendTail( buffer );
 
410
        }
 
411
 
 
412
        return buffer != null ? buffer.toString() : null;
 
413
    }
 
414
 
 
415
    public String generateExpression( String expression, Period period, Source source )
 
416
    {
 
417
        StringBuffer buffer = null;
 
418
 
 
419
        if ( expression != null )
 
420
        {
 
421
            Matcher matcher = getMatcher( "(\\[\\d+\\" + SEPARATOR + "\\d+\\])", expression );
 
422
 
 
423
            buffer = new StringBuffer();
 
424
 
 
425
            while ( matcher.find() )
 
426
            {
 
427
                String replaceString = matcher.group();
 
428
 
 
429
                replaceString = replaceString.replaceAll( "[\\[\\]]", "" );
 
430
 
 
431
                String dataElementIdString = replaceString.substring( 0, replaceString.indexOf( SEPARATOR ) );
 
432
                String categoryOptionComboIdString = replaceString.substring( replaceString.indexOf( SEPARATOR ) + 1, replaceString.length() );
 
433
 
 
434
                int dataElementId = Integer.parseInt( dataElementIdString );
 
435
                int optionComboId = Integer.parseInt( categoryOptionComboIdString );
 
436
 
 
437
                DataElement dataElement = dataElementService.getDataElement( dataElementId );
 
438
 
 
439
                DataElementCategoryOptionCombo categoryOptionCombo = categoryOptionComboService.getDataElementCategoryOptionCombo( optionComboId );
 
440
 
 
441
                DataValue dataValue = dataValueService.getDataValue( source, dataElement, period, categoryOptionCombo );
 
442
 
 
443
                if ( dataValue == null )
 
444
                {
 
445
                    replaceString = NULL_REPLACEMENT;
 
446
                }
 
447
                else
 
448
                {
 
449
                    replaceString = String.valueOf( dataValue.getValue() );
 
450
                }
 
451
 
 
452
                matcher.appendReplacement( buffer, replaceString );
 
453
            }
 
454
 
 
455
            matcher.appendTail( buffer );
 
456
        }
 
457
 
 
458
        return buffer != null ? buffer.toString() : null;
 
459
    }
 
460
    
 
461
    // -------------------------------------------------------------------------
 
462
    // Supportive methods
 
463
    // -------------------------------------------------------------------------
 
464
 
 
465
    private Matcher getMatcher( String regex, String expression )
 
466
    {
 
467
        Pattern pattern = Pattern.compile( regex );
 
468
 
 
469
        return pattern.matcher( expression );
 
470
    }
 
471
}