Statistics
| Revision:

svn-gvsig-desktop / tags / v1_1_Build_1003 / extensions / extWMS / src / com / iver / cit / gvsig / fmap / layers / TimeDimension.java @ 12271

History | View | Annotate | Download (26.3 KB)

1
/* gvSIG. Sistema de Informaci�n Geogr�fica de la Generalitat Valenciana
2
 *
3
 * Copyright (C) 2005 IVER T.I. and Generalitat Valenciana.
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
18
 *
19
 * For more information, contact:
20
 *
21
 *  Generalitat Valenciana
22
 *   Conselleria d'Infraestructures i Transport
23
 *   Av. Blasco Ib��ez, 50
24
 *   46010 VALENCIA
25
 *   SPAIN
26
 *
27
 *      +34 963862235
28
 *   gvsig@gva.es
29
 *      www.gvsig.gva.es
30
 *
31
 *    or
32
 *
33
 *   IVER T.I. S.A
34
 *   Salamanca 50
35
 *   46005 Valencia
36
 *   Spain
37
 *
38
 *   +34 963163400
39
 *   dac@iver.es
40
 */
41

    
42
/* CVS MESSAGES:
43
*
44
* $Id: TimeDimension.java 12271 2007-06-21 10:50:33Z  $
45
* $Log$
46
* Revision 1.7.6.1  2007-03-08 12:06:28  jaume
47
* fixed bug in YYYY date formats
48
*
49
* Revision 1.7  2006/05/12 07:47:39  jaume
50
* removed unnecessary imports
51
*
52
* Revision 1.6  2006/04/21 11:53:04  jaume
53
* *** empty log message ***
54
*
55
* Revision 1.5  2006/02/28 15:25:14  jaume
56
* *** empty log message ***
57
*
58
* Revision 1.3.2.6  2006/02/28 12:54:12  jaume
59
* *** empty log message ***
60
*
61
* Revision 1.3.2.5  2006/02/23 10:36:30  jaume
62
* *** empty log message ***
63
*
64
* Revision 1.3.2.4  2006/02/10 13:22:35  jaume
65
* now analyzes dimensions on demand
66
*
67
* Revision 1.3.2.3  2006/01/31 16:25:24  jaume
68
* correcciones de bugs
69
*
70
* Revision 1.4  2006/01/26 16:07:14  jaume
71
* *** empty log message ***
72
*
73
* Revision 1.3.2.1  2006/01/26 12:59:32  jaume
74
* 0.5
75
*
76
* Revision 1.3  2006/01/24 18:01:17  jaume
77
* *** empty log message ***
78
*
79
* Revision 1.2  2006/01/24 14:36:33  jaume
80
* This is the new version
81
*
82
* Revision 1.1.2.11  2006/01/20 15:59:13  jaume
83
* *** empty log message ***
84
*
85
* Revision 1.1.2.10  2006/01/20 15:22:46  jaume
86
* *** empty log message ***
87
*
88
* Revision 1.1.2.9  2006/01/20 08:50:52  jaume
89
* handles time dimension for the NASA Jet Propulsion Laboratory WMS
90
*
91
* Revision 1.1.2.8  2006/01/19 16:09:30  jaume
92
* *** empty log message ***
93
*
94
* Revision 1.1.2.7  2006/01/11 12:20:30  jaume
95
* *** empty log message ***
96
*
97
* Revision 1.1.2.6  2006/01/10 11:33:31  jaume
98
* Time dimension working against Jet Propulsion Laboratory's WMS server
99
*
100
* Revision 1.1.2.5  2006/01/09 18:10:38  jaume
101
* casi con el time dimension
102
*
103
* Revision 1.1.2.4  2006/01/05 23:15:53  jaume
104
* *** empty log message ***
105
*
106
* Revision 1.1.2.3  2006/01/04 18:09:02  jaume
107
* Time dimension
108
*
109
* Revision 1.1.2.2  2006/01/04 16:49:44  jaume
110
* Time dimensios
111
*
112
* Revision 1.1.2.1  2006/01/03 18:08:40  jaume
113
* *** empty log message ***
114
*
115
*
116
*/
117
/**
118
 *
119
 */
120
package com.iver.cit.gvsig.fmap.layers;
121

    
122
import java.util.ArrayList;
123
import java.util.GregorianCalendar;
124

    
125
/**
126
 * Class for WMS TIME dimension from a WMS. It allows you to handle the correct
127
 * values for this kind of dimension.
128
 * <br>
129
 * <p>
130
 * At the moment this class was written the WMS TIME dimension is defined as the
131
 * ISO8601 standard for expressing times.
132
 * </p>
133
 * <br>
134
 * <p>
135
 * As far as this class implements IFMapWMSDimension it uses the same interface
136
 * and documentation.
137
 * </p>
138
 *
139
 * @author jaume dominguez faus - jaume.dominguez@iver.es
140
 */
141
public class TimeDimension implements IFMapWMSDimension {
142
        static private final byte YEAR_FORMAT          = 1;
143
        static private final byte YEAR_TO_MONTH_FORMAT = 2;
144
        static private final byte YEAR_TO_DAY_FORMAT   = 3;
145
        static private final byte FULL_FORMAT          = 4;
146

    
147
    static private final long millisXsec    = 1000;
148
    static private final long millisXminute = 60 * millisXsec;
149
    static private final long millisXhour   = 60 * millisXminute;
150
    static private final long millisXday    = 24 * millisXhour;
151
    static private final long millisXmonth  = 30 * millisXday;
152
    // according on the Wikipedia (1 year = 365 days 6 hours 9 minutes 9,7 seconds)
153
    static private final long millisXyear   = (365*millisXday) + (6*millisXhour) + (9*millisXminute) + 9700;
154

    
155
    static private final String digit = "[0-9]";
156
    static private final String nonZeroDigit = "[1-9]";
157
    static private final String seconds = "([0-5]"+digit+"((\\.|,)"+digit+digit+")?)";
158
    static private final String minutes = "([0-5]"+digit+")";
159
    static private final String hours = "(0"+digit+"|1"+digit+"|2[0-3])";
160
    static private final String time = hours+":"+minutes+"(:"+seconds+")?";
161
    static private final String days = "(0?"+nonZeroDigit+"|1"+digit+"|2"+digit+"|30|31)";
162
    static private final String months = "(0?"+nonZeroDigit+"|10|11|12)";
163
    static private final String year = "("+digit+digit+")";
164
    static private final String century = "("+digit+digit+")";
165
    static private final String floatingPointNumber = "("+digit+"+(\\."+digit+"+)?)";
166

    
167
    private String unit;
168
        private String unitSymbol;
169
        private ArrayList valueList;
170
        private boolean compiled;
171
        private boolean isGeologic;
172
        private String expression;
173
        private int type;
174
        private byte format = 0;
175

    
176
    static private final String regexDateExtendedForBCE1 = "B?"+century+year;
177
    static private final String regexDateExtendedForBCE2 = "B?"+century+year+"-"+months;
178
    static private final String regexDateExtendedForBCE3 = "B?"+century+year+"-"+months+"-"+days;
179
    static private final String regexDateExtendedForBCE4 = "B?"+century+year+"-"+months+"-"+days+"(T| )"+time+"Z";
180
    // Note: in WMS 1.1.0 the suffix Z is optional
181

    
182
    static private final String regexDateExtendedForBCE =
183
        "(" +  regexDateExtendedForBCE1  + "|"
184
            +  regexDateExtendedForBCE2  + "|"
185
            +  regexDateExtendedForBCE3  + "|"
186
            +  regexDateExtendedForBCE4  +      ")";
187

    
188
    static private final String periodMagnitude = "(Y|M|D)";
189
    static private final String p1 = "(("+digit+")+"+periodMagnitude+")";
190

    
191
    static private final String timeMagnitude = "(H|M|S)";
192
    static private final String p2 = "("+floatingPointNumber+timeMagnitude+")";
193
    static private final String regexPeriod = "P(("+p1+"+"+"(T"+p2+")*)|("+p1+"*"+"(T"+p2+")+))";
194

    
195
    static private final String regexIntervalTimeDimension =
196
        "("+regexDateExtendedForBCE+")/("+regexDateExtendedForBCE+")/("+regexPeriod+")";
197

    
198
    static private final String geologicDatasets = "(K|M|G)";
199
    static private final String regexDateForGeologicDatasets = geologicDatasets+floatingPointNumber;
200

    
201
    /**
202
     * Creates a new instance of TimeDimension.
203
     * @param units
204
     * @param unitSymbol
205
     * @param expression
206
     */
207
    public TimeDimension(String _units, String _unitSymbol, String _dimensionExpression) {
208
        this.unit = _units;
209
        this.unitSymbol = _unitSymbol;
210
        setExpression(_dimensionExpression);
211
    }
212

    
213
        public String getName() {
214
        return "TIME";
215
    }
216

    
217
    public String getUnit() {
218
        return unit;
219
    }
220

    
221
    public String getUnitSymbol() {
222
        return unitSymbol;
223
    }
224

    
225
        public String getLowLimit() {
226
                return (String) valueList.get(0);
227
        }
228

    
229
        public String getHighLimit() {
230
                return (String) valueList.get(valueList.size()-1);
231
        }
232

    
233

    
234
        public String getResolution() {
235
                return null;
236
        }
237

    
238
        public boolean isValidValue(String value) {
239
                return (value.matches(regexDateForGeologicDatasets) || value.matches(regexDateExtendedForBCE));
240
        }
241

    
242
        public Object valueOf(String value) throws IllegalArgumentException {
243
                if (compiled) {
244
                    // TODO Missing geological dates
245
                    String myValue = value.toUpperCase();
246
                    if (isValidValue(myValue)) {
247
                            Object val = null;
248
                            if (!isGeologic){
249
                                    // This is a normal date
250
                                    int myYear;
251
                                    int myMonth;
252
                                    int myDay;
253
                                    int myHour;
254
                                    int myMinute;
255
                                    float mySecond;
256
                                    String[] s = myValue.split("-");
257
                                    myYear = (s[0].charAt(0)=='B') ? -Integer.parseInt(s[0].substring(1, 5)) : Integer.parseInt(s[0].substring(0, 4));
258
                                    myMonth = (s.length>1) ? Integer.parseInt(s[1])-1 : 0;
259
                                    if (myValue.matches(regexDateExtendedForBCE4)){
260
                                            if (s[2].endsWith("Z"))
261
                                                    s[2] = s[2].substring(0,s[2].length()-1);
262
                                            s = (s[2].indexOf('T')!=-1) ? s[2].split("T") : s[2].split(" ");
263
                                            myDay = Integer.parseInt(s[0]);
264

    
265
                                            // Go with the time
266
                                            s = s[1].split(":");
267
                                            myHour = Integer.parseInt(s[0]);
268
                                            myMinute = (s.length>1) ? Integer.parseInt(s[1]) : 0;
269
                                            mySecond = (s.length>2) ? Float.parseFloat(s[2]) : 0;
270
                                    } else {
271
                                            myDay = (s.length>2) ? Integer.parseInt(s[2]) : 1;
272

    
273
                                            myHour = 0;
274
                                            myMinute = 0;
275
                                            mySecond = 0;
276
                                    }
277
                                    GregorianCalendar cal = new GregorianCalendar(myYear, myMonth, myDay, myHour, myMinute, (int)mySecond);
278
                                    val = cal;
279
                            } else{
280
                                    // this is a geological date >:-(
281
                            }
282
                            return val;
283
                    } else throw new IllegalArgumentException(myValue);
284
            }
285
            return null;
286
        }
287

    
288
        public String valueAt(int pos) throws ArrayIndexOutOfBoundsException {
289
                return toString((GregorianCalendar) valueList.get(pos));
290
        }
291

    
292
        public int valueCount() {
293
                return valueList.size();
294
        }
295

    
296
        public String getExpression() {
297
                return expression;
298
        }
299

    
300
        public void setExpression(String expr) {
301
                this.expression = expr;
302
        }
303

    
304
        public int getType() {
305
                return type;
306
        }
307

    
308
        public void compile() throws IllegalArgumentException {
309
                compiled = true;
310
                valueList = new ArrayList();
311
                String[] items = expression.split(",");
312
                for (int i = 0; i < items.length; i++) {
313
                        // Each iteration is a value of a comma-separated list
314
                        // which can be a single date or even an expression defining
315
                        // an interval.
316
                        if (items[i].matches(regexDateExtendedForBCE1)) {
317
                                upgradeFormat(YEAR_FORMAT);
318
                                valueList.add(valueOf(items[i]));
319
                        } else if (items[i].matches(regexDateExtendedForBCE2)) {
320
                                upgradeFormat(YEAR_TO_MONTH_FORMAT);
321
                                valueList.add(valueOf(items[i]));
322
                        } else if (items[i].matches(regexDateExtendedForBCE3)) {
323
                                upgradeFormat(YEAR_TO_DAY_FORMAT);
324
                                valueList.add(valueOf(items[i]));
325
                        } else if (items[i].matches(regexDateExtendedForBCE4)) {
326
                                upgradeFormat(FULL_FORMAT);
327
                                valueList.add(valueOf(items[i]));
328
                        } else if (items[i].matches(regexIntervalTimeDimension)) {
329
                                // Analyze and transform this interval expression to a list
330
                                // of values.
331

    
332
                                // min value
333
                                String[] s = items[i].split("/");
334
                                if (s[0].matches(regexDateExtendedForBCE1)) {
335
                                        upgradeFormat(YEAR_FORMAT);
336
                                } else if (s[0].matches(regexDateExtendedForBCE2)) {
337
                                        upgradeFormat(YEAR_TO_MONTH_FORMAT);
338
                                } else if (s[0].matches(regexDateExtendedForBCE3)) {
339
                                        upgradeFormat(YEAR_TO_DAY_FORMAT);
340
                                } else if (s[0].matches(regexDateExtendedForBCE4)) {
341
                                        upgradeFormat(FULL_FORMAT);
342
                                }
343
                                Object minValue = valueOf(s[0]);
344

    
345
                                // max value
346
                                if (s[0].matches(regexDateExtendedForBCE1)) {
347
                                        upgradeFormat(YEAR_FORMAT);
348
                                } else if (s[1].matches(regexDateExtendedForBCE2)) {
349
                                        upgradeFormat(YEAR_TO_MONTH_FORMAT);
350
                                } else if (s[1].matches(regexDateExtendedForBCE3)) {
351
                                        upgradeFormat(YEAR_TO_DAY_FORMAT);
352
                                } else if (s[1].matches(regexDateExtendedForBCE4)) {
353
                                        upgradeFormat(FULL_FORMAT);
354
                                }
355
                                Object maxValue = valueOf(s[1]);
356

    
357
                                String period = s[2];
358

    
359
                                if (period == null) {
360
                                        valueList.add(minValue);
361
                                        valueList.add(maxValue);
362
                                        continue;
363
                                }
364

    
365
                                long x1 = ((GregorianCalendar) maxValue).getTimeInMillis();
366
                                long x0 = ((GregorianCalendar) minValue).getTimeInMillis();
367
                                long distance = x1-x0;
368
                                long step = 0;
369

    
370
                                boolean isTimeField = false;
371
                                String val = "";
372

    
373
                                for (int j = 0; j < period.length(); j++) {
374
                                        if (period.charAt(j) == 'P')
375
                                                continue;
376
                                        if (period.charAt(j) == 'T') {
377
                                                isTimeField = true;
378
                                                continue;
379
                                        }
380
                                        switch (period.charAt(j)) {
381
                                        case 'Y':
382
                                                step += Integer.parseInt(val) * millisXyear;
383
                                                val = "";
384
                                                break;
385
                                        case 'M':
386
                                                if (isTimeField)
387
                                                        step += Integer.parseInt(val) * millisXminute;
388
                                                else
389
                                                        step += Integer.parseInt(val) * millisXmonth;
390
                                                val = "";
391
                                                break;
392
                                        case 'D':
393
                                                step += Integer.parseInt(val) * millisXday;
394
                                                val = "";
395
                                                break;
396
                                        case 'H':
397
                                                step += Integer.parseInt(val) * millisXhour;
398
                                                val = "";
399
                                                break;
400
                                        case 'S':
401
                                                step += Integer.parseInt(val) * 1000;
402
                                                val = "";
403
                                                break;
404
                                        default:
405
                                                val += period.charAt(j);
406
                                                break;
407
                                        }
408
                                }
409
                                // Now that we know the earliest and the latest date and the period
410
                                // between two dates, I'm going to populate the list of values
411
                                // considering these values.
412
                                int valueCount = (int)(distance/step) + 1; // + 1 for the initial point
413
                                valueList.add(minValue);
414
                                for (int pos = 1; pos < valueCount; pos++) {
415
                                        long newTime = ((GregorianCalendar) minValue).getTimeInMillis();
416
                                    newTime += (step*pos);
417
                                    GregorianCalendar cal = new GregorianCalendar();
418
                                    cal.setTimeInMillis(newTime);
419
                                    if (cal.after(maxValue) || cal.before(minValue))
420
                                            continue;
421

    
422
                                    valueList.add(cal);
423
                                }
424
                                valueList.add(maxValue);
425
                        } else {
426
                                compiled = false;
427
                                throw new IllegalArgumentException();
428
                        }
429
                }
430
    }
431

    
432
        /**
433
         * Prints a GregorianCalendar value in WMS1.1.1 format.
434
         * @param cal
435
         * @return
436
         */
437
        private String toString(GregorianCalendar cal) {
438
                int iYear   = cal.get(cal.YEAR);
439
                int iMonth  = cal.get(cal.MONTH) + 1;
440
                int iDay    = cal.get(cal.DAY_OF_MONTH);
441
                int iHour   = cal.get(cal.HOUR_OF_DAY);
442
                int iMinute = cal.get(cal.MINUTE);
443
                int iSecond = cal.get(cal.SECOND);
444
                String myYear;
445
                if (iYear<10)
446
                        myYear = "200"+iYear;
447
                else if (iYear<100)
448
                        myYear = "20"+iYear;
449
                else if (iYear<1000)
450
                        myYear = "2"+iYear;
451
                else
452
                        myYear = ""+iYear;
453
                String myMonth       = (iMonth<10) ? "0"+iMonth  : ""+iMonth;
454
                String myDay         = (iDay<10)   ? "0"+iDay    : ""+iDay;
455
                String myHour        = (iHour<10)  ? "0"+iHour   : ""+iHour;
456
                String myMinute      = (iMinute<10)? "0"+iMinute : ""+iMinute;
457
                String mySecond      = (iSecond<10)? "0"+iSecond : ""+iSecond;
458
                int myMilliSecond = cal.get(cal.MILLISECOND);
459

    
460

    
461
                String s = null;
462
                if (format == YEAR_FORMAT)
463
                        s = myYear+"";
464
                else if (format == YEAR_TO_MONTH_FORMAT)
465
                        s = myYear+"-"+myMonth;
466
                else if (format == YEAR_TO_DAY_FORMAT)
467
                        s = myYear+"-"+myMonth+"-"+myDay;
468
                else if (format == FULL_FORMAT)
469
                        s = myYear+"-"+myMonth+"-"+myDay+"T"+myHour+":"+myMinute+":"+mySecond+"."+(myMilliSecond/10)+"Z";
470
                if (iYear<0)
471
                        s = "B"+s;
472
                return s;
473
        }
474

    
475
        private void upgradeFormat(byte sFormat) {
476
                switch (sFormat) {
477
                case YEAR_FORMAT:
478
                        if (format < YEAR_FORMAT)
479
                                format = YEAR_FORMAT;
480
                        break;
481
                case YEAR_TO_MONTH_FORMAT:
482
                        if (format < YEAR_TO_MONTH_FORMAT)
483
                                format = YEAR_TO_MONTH_FORMAT;
484
                        break;
485
                case YEAR_TO_DAY_FORMAT:
486
                        if (format < YEAR_TO_DAY_FORMAT)
487
                                format = YEAR_TO_DAY_FORMAT;
488
                        break;
489
                case FULL_FORMAT:
490
                        if (format < FULL_FORMAT)
491
                                format = FULL_FORMAT;
492
                        break;
493
                }
494
        }
495
}
496

    
497

    
498
/*
499

500
 // an old (and good one) version. However, the new version is a bit better.
501

502
 public class TimeDimension implements IFMapWMSDimension {
503
    static private final long millisXsec    = 1000;
504
    static private final long millisXminute = 60 * millisXsec;
505
    static private final long millisXhour   = 60 * millisXminute;
506
    static private final long millisXday    = 24 * millisXhour;
507
    static private final long millisXmonth  = 30 * millisXday;
508
    // according on the Wikipedia (1 year = 365 days 6 hours 9 minutes 9,7 seconds)
509
    static private final long millisXyear   = (365*millisXday) + (6*millisXhour) + (9*millisXminute) + 9700;
510

511
    static private final String digit = "[0-9]";
512
    static private final String nonZeroDigit = "[1-9]";
513
    static private final String letter = "[a-zA-Z]";
514
    static private final String seconds = "([0-5]"+digit+"((\\.|,)"+digit+digit+")?)";
515
    static private final String minutes = "([0-5]"+digit+")";
516
    static private final String hours = "(0"+digit+"|1"+digit+"|2[0-3])";
517
    static private final String time = hours+":"+minutes+"(:"+seconds+")?";
518
    static private final String days = "(0?"+nonZeroDigit+"|1"+digit+"|2"+digit+"|30|31)";
519
    static private final String months = "(0?"+nonZeroDigit+"|10|11|12)";
520
    static private final String year = "("+digit+digit+")";
521
    static private final String century = "("+digit+digit+")";
522

523
    static private final String geologicDatasets = "(K|M|G)";
524
    static private final String floatingPointNumber = "("+digit+"+(\\."+digit+"+)?)";
525

526

527
    static private final String regexDateExtendedForBCE1 = "B?"+century+year+"-";
528
    static private final String regexDateExtendedForBCE2 = "B?"+century+year+"-"+months;
529
    static private final String regexDateExtendedForBCE3 = "B?"+century+year+"-"+months+"-"+days;
530
    static private final String regexDateExtendedForBCE4 = "B?"+century+year+"-"+months+"-"+days+"(T| )"+time+"Z";
531
    // Note: in WMS 1.1.0 the suffix Z is optional
532
    // TODO truncated values not yet allowed
533

534
    static private final String regexDateExtendedForBCE =
535
        "(" +  regexDateExtendedForBCE1  + "|"
536
            +  regexDateExtendedForBCE2  + "|"
537
            +  regexDateExtendedForBCE3  + "|"
538
            +  regexDateExtendedForBCE4  +      ")";
539

540

541
    static private final String regexDateForGeologicDatasets = geologicDatasets+floatingPointNumber;
542

543
    static private final String periodMagnitude = "(Y|M|D)";
544
    static private final String p1 = "(("+digit+")+"+periodMagnitude+")";
545

546
    static private final String timeMagnitude = "(H|M|S)";
547
    static private final String p2 = "("+floatingPointNumber+timeMagnitude+")";
548
    static private final String regexPeriod = "P(("+p1+"+"+"(T"+p2+")*)|("+p1+"*"+"(T"+p2+")+))";
549

550
    static private final String regexTimeDimension =
551
        "("+regexDateExtendedForBCE+"(,"+regexDateExtendedForBCE+")*|("+regexDateExtendedForBCE+")/("+regexDateExtendedForBCE+")/("+regexPeriod+"))";
552

553
    private String name = "TIME";
554
    private String unit;
555
    private String unitSymbol;
556
    private String expression;
557
    private Object minValue;
558
    private Object maxValue;
559
    private boolean isGeologic = false;
560
    private Integer valueCount;
561
    private String period;
562
    private long step; // Distance between two points in milliseconds.
563
    private int type;
564
    private boolean compiled = false;
565

566
    /**
567
     * Creates a new instance of TimeDimension.
568
     * @param units
569
     * @param unitSymbol
570
     * @param expression
571
     * /
572
    public TimeDimension(String _units, String _unitSymbol, String _dimensionExpression) {
573
        this.unit = _units;
574
        this.unitSymbol = _unitSymbol;
575
        setExpression(_dimensionExpression);
576
    }
577

578
    public String getName() {
579
        return name;
580
    }
581

582
    public String getUnit() {
583
        return unit;
584
    }
585

586
    public String getUnitSymbol() {
587
        return unitSymbol;
588
    }
589

590

591
    public String getLowLimit() {
592
            String separator = (type == INTERVAL) ? "/" : ",";
593
        return expression.split(separator)[0];
594
    }
595

596
    public String getHighLimit() {
597
            if (type == INTERVAL) {
598
                    String[] s = expression.split("/");
599
                    return (s.length > 1) ? s[1] : s[0];
600
            } else if (type == MULTIPLE_VALUE) {
601
                    String[] s = expression.split(",");
602
                    return s[s.length-1];
603
            } else {
604
                    return expression;
605
            }
606

607
    }
608

609
    public String getResolution() {
610
            if (type == INTERVAL) {
611
                    String[] s = expression.split("/");
612
                    return (s.length == 1) ? s[3] : null;
613
            } else return null;
614
    }
615

616
    public boolean isValidValue(String value) {
617
        return (value.matches(regexDateForGeologicDatasets) || value.matches(regexDateExtendedForBCE));
618
    }
619

620
    public Object valueOf(String value) throws IllegalArgumentException {
621
            if (compiled) {
622
                    // TODO Missing geological dates
623
                    String myValue = value.toUpperCase();
624
                    if (isValidValue(myValue)) {
625
                            Object val = null;
626
                            if (!isGeologic){
627
                                    // This is a normal date
628
                                    int myYear;
629
                                    int myMonth;
630
                                    int myDay;
631
                                    int myHour;
632
                                    int myMinute;
633
                                    float mySecond;
634
                                    String[] s = myValue.split("-");
635
                                    myYear = (s[0].charAt(0)=='B')? -Integer.parseInt(s[0].substring(1, 5)) : Integer.parseInt(s[0].substring(0, 4));
636
                                    myMonth = (s.length>1) ? Integer.parseInt(s[1])-1 : 0;
637
                                    if (myValue.matches(regexDateExtendedForBCE4)){
638
                                            if (s[2].endsWith("Z"))
639
                                                    s[2] = s[2].substring(0,s[2].length()-1);
640
                                            s = (s[2].indexOf('T')!=-1) ? s[2].split("T") : s[2].split(" ");
641
                                            myDay = Integer.parseInt(s[0]);
642

643
                                            // Go with the time
644
                                            s = s[1].split(":");
645
                                            myHour = Integer.parseInt(s[0]);
646
                                            myMinute = (s.length>1) ? Integer.parseInt(s[1]) : 0;
647
                                            mySecond = (s.length>2) ? Float.parseFloat(s[2]) : 0;
648
                                    } else {
649
                                            myDay = (s.length>2) ? Integer.parseInt(s[2]) : 1;
650

651
                                            myHour = 0;
652
                                            myMinute = 0;
653
                                            mySecond = 0;
654
                                    }
655
                                    GregorianCalendar cal = new GregorianCalendar(myYear, myMonth, myDay, myHour, myMinute, (int)mySecond);
656
                                    val = cal;
657
                            } else{
658
                                    // this is a geological date >:-(
659
                            }
660
                            return val;
661
                    } else throw new IllegalArgumentException(myValue);
662
            }
663
            return null;
664
    }
665

666
    public String valueAt(int pos) throws ArrayIndexOutOfBoundsException {
667
            if (compiled) {
668
                    if (pos<0 || pos>valueCount())
669
                            throw new ArrayIndexOutOfBoundsException(pos+"(must be >= 0 and <="+valueCount()+")");
670

671
                    if (type == SINGLE_VALUE)
672
                            return expression;
673

674
                    if (type == MULTIPLE_VALUE)
675
                            return expression.split(",")[pos];
676

677
                    if (!isGeologic){
678
                            long newTime = ((GregorianCalendar) minValue).getTimeInMillis();
679
                            newTime += (step*pos);
680
                            GregorianCalendar cal = new GregorianCalendar();
681
                            cal.setTimeInMillis(newTime);
682
                            if (cal.after(maxValue))
683
                                    return toString((GregorianCalendar) maxValue);
684
                            else if (cal.before(minValue))
685
                                    return toString((GregorianCalendar) minValue);
686
                            else
687
                                    return toString(cal);
688
                    }
689
            }
690
        return null;
691
    }
692

693
    /**
694
     * Prints a GregorianCalendar value in WMS1.1.1 format.
695
     * @param cal
696
     * @return
697
     * /
698
    private String toString(GregorianCalendar cal) {
699
        int iYear   = cal.get(cal.YEAR);
700
        int iMonth  = cal.get(cal.MONTH) + 1;
701
        int iDay    = cal.get(cal.DAY_OF_MONTH);
702
        int iHour   = cal.get(cal.HOUR_OF_DAY);
703
        int iMinute = cal.get(cal.MINUTE);
704
        int iSecond = cal.get(cal.SECOND);
705
        String myYear;
706
        if (iYear<10)
707
            myYear = "200"+iYear;
708
        else if (iYear<100)
709
            myYear = "20"+iYear;
710
        else if (iYear<1000)
711
            myYear = "2"+iYear;
712
        else
713
            myYear = ""+iYear;
714
        String myMonth       = (iMonth<10) ? "0"+iMonth  : ""+iMonth;
715
        String myDay         = (iDay<10)   ? "0"+iDay    : ""+iDay;
716
        String myHour        = (iHour<10)  ? "0"+iHour   : ""+iHour;
717
        String myMinute      = (iMinute<10)? "0"+iMinute : ""+iMinute;
718
        String mySecond      = (iSecond<10)? "0"+iSecond : ""+iSecond;
719
        int myMilliSecond = cal.get(cal.MILLISECOND);
720

721

722
        String s = myYear+"-"+myMonth+"-"+myDay+"T"+myHour+":"+myMinute+":"+mySecond+"."+(myMilliSecond/10)+"Z";
723
        if (iYear<0)
724
            s = "B"+s;
725
        return s;
726
    }
727

728
    public int valueCount() {
729
            if (compiled) {
730
                    if (valueCount==null){
731
                            if (type == MULTIPLE_VALUE) {
732
                                    return expression.split(",").length;
733
                            } else if (type == INTERVAL) {
734
                                    if (period == null) {
735
                                            valueCount = new Integer(0);
736
                                            return valueCount.intValue();
737
                                    }
738

739
                                    if (!isGeologic){
740

741
                                            long x1 = ((GregorianCalendar) maxValue).getTimeInMillis();
742
                                            long x0 = ((GregorianCalendar) minValue).getTimeInMillis();
743
                                            long distance = x1-x0;
744
                                            step = 0;
745

746
                                            boolean isTimeField = false;
747
                                            String val = "";
748

749
                                            for (int i = 0; i < period.length(); i++) {
750
                                                    if (period.charAt(i) == 'P')
751
                                                            continue;
752
                                                    if (period.charAt(i) == 'T'){
753
                                                            isTimeField = true;
754
                                                            continue;
755
                                                    }
756
                                                    switch (period.charAt(i)){
757
                                                    case 'Y':
758
                                                            step += Integer.parseInt(val) * millisXyear;
759
                                                            val = "";
760
                                                            break;
761
                                                    case 'M':
762
                                                            if (isTimeField)
763
                                                                    step += Integer.parseInt(val) * millisXminute;
764
                                                            else
765
                                                                    step += Integer.parseInt(val) * millisXmonth;
766
                                                            val = "";
767
                                                            break;
768
                                                    case 'D':
769
                                                            step += Integer.parseInt(val) * millisXday;
770
                                                            val = "";
771
                                                            break;
772
                                                    case 'H':
773
                                                            step += Integer.parseInt(val) * millisXhour;
774
                                                            val = "";
775
                                                            break;
776
                                                    case 'S':
777
                                                            step += Integer.parseInt(val) * 1000;
778
                                                            val = "";
779
                                                            break;
780
                                                    default:
781
                                                            val += period.charAt(i);
782
                                                    break;
783
                                                    }
784
                                            }
785
                                            valueCount = new Integer((int)(distance/step) + 1); // + 1 for the initial point
786
                                    }
787
                            } else {
788
                                    // this is a single value expression
789
                                    valueCount = new Integer(1);
790
                                    return valueCount.intValue();
791
                            }
792
                    }
793

794
                    return valueCount.intValue();
795
            }
796
            return -1;
797
    }
798

799
    public void setExpression(String expr) {
800
        expression = expr.toUpperCase();
801
    }
802

803
        public String getExpression() {
804
                return expression;
805
        }
806

807
        public int getType() {
808
                return type;
809
        }
810

811
        public void compile() {
812
                if (expression.matches(regexTimeDimension)){
813
            isGeologic = false;
814
        } else if (expression.matches(regexDateExtendedForBCE)) {
815
            isGeologic = false;
816
        } else if (expression.matches(regexDateForGeologicDatasets)){
817
            isGeologic = true;
818
        } else  {
819
            throw new IllegalArgumentException();
820
        }
821
                String separator;
822

823
        if (expression.indexOf("/")!=-1) {
824
                separator = "/";
825
                type = INTERVAL;
826
        } else if (expression.indexOf(",")!=-1) {
827
                separator = ",";
828
                type = MULTIPLE_VALUE;
829
        } else {
830
                separator = ",";
831
                type = SINGLE_VALUE;
832
        }
833

834
        compiled = true;
835
        String[] s = expression.split(separator);
836
        minValue = valueOf(s[0]);
837
        if (type == INTERVAL) {
838
                maxValue = (s.length>1) ? valueOf(s[1]) : valueOf(s[0]);
839
                period = (s.length>2 && s[2].matches(regexPeriod)) ? s[2] : null;
840
        } else if (type == MULTIPLE_VALUE) {
841
                maxValue = valueOf(s[s.length-1]);
842
        } else {
843
                maxValue = valueOf(s[0]);
844
        }
845
        }
846
}*/
847

    
848