Subversion Repository Public Repository

litesoft

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
package org.litesoft.core.simpletypes.temporal;

import org.litesoft.core.simpletypes.nonpublic.*;
import org.litesoft.core.simpletypes.temporal.nonpublic.*;
import org.litesoft.core.util.*;

import java.util.*;

/**
 * Format Control for SimpleDate which provides varying levels of resolution based on the Format String
 * (which supports formatting similar to SimpledateFormat).
 * <p/>
 * <pre>
 *     y  	Year    - YY (1996 becomes 96),
 *                    otherwise it is the complete Year, eg: 1996, 2001, 10050.
 *     M 	Month   - M generates no leading 0 months as numbers,
 *                    MM produces 2 digit month numbers,
 *                    MMM produces 3 letter month abbreviated form,
 *                    MMMM (4 or more) produces the full form.
 *     d 	Day     - d generates no leading 0 day of month as numbers,
 *                    dd produces 2 digit day of month numbers.
 * </pre>
 */
public class DateFormatControl<T extends DateFormatControl> extends CalendarSupport<T> implements TemporalParsingSupport, SimpleType
{
    public static final String DEFAULT_DATE_FORMAT = "dd MMM yyyy";

    private static final char[] ACCEPTABLE_INDICATORS = //
            { //
                    DateRes.INDICATOR_YEAR, //
                    DateRes.INDICATOR_MONTH, //
                    DateRes.INDICATOR_DAY, //
            };

    private String mFormat = DEFAULT_DATE_FORMAT;

    private int mResolutionIndex = DateRes.DAYindex;

    // Late Bound:
    private transient String mFieldOrder = null;
    private transient Chunk[] mToStringChunks = null;

    /**
     * @deprecated - for Serialization
     */
    protected DateFormatControl()
    {
    }

    public DateFormatControl( String pFormat )
    {
        if ( null == (pFormat = UtilsCommon.noEmpty( pFormat )) )
        {
            mFormat = DEFAULT_DATE_FORMAT;
        }
        else
        {
            mFormat = pFormat;
        }
        int zIndicatorBits = indicatorBit( 1, DateRes.ToYEAR ) + //
                             indicatorBit( 2, DateRes.ToMONTH ) + //
                             indicatorBit( 4, DateRes.ToDAY );
        switch ( zIndicatorBits )
        {
            case 6: // Month & Day
                // fall thru *** Common format used when the Year is implicit (e.g. 4JAN means the next January 4th)
            case 7: // Year,Month, & Day
                mResolutionIndex = DateRes.DAYindex;
                break;
            case 3: // Year & Month
                mResolutionIndex = DateRes.MONTHindex;
                break;
            case 1: // Year Only
                mResolutionIndex = DateRes.YEARindex;
                break;
            case 5: // Year & Day ?
                throw doesNotContain( DateRes.ToMONTH, "" );
            case 2: // Month Only ?
                // Fall thru
            case 4: // Day Only ?
                // Fall thru
            case 0: // None!
                throw doesNotContain( DateRes.ToYEAR, "" );
        }
    }

    public String getFormat()
    {
        return mFormat;
    }

    public DateRes getDateRes()
    {
        return DateRes.fromIndex( mResolutionIndex );
    }

    public boolean isValidToMonth()
    {
        return DateRes.MONTHindex <= mResolutionIndex;
    }

    public boolean isValidToDay()
    {
        return DateRes.DAYindex <= mResolutionIndex;
    }

    public String toString()
    {
        return getFormat();
    }

    /**
     * Validate the date indicated against this' resolution.
     * <p/>
     * If the resolution is less than ToDAY, then pDay may be 0.
     * If the resolution is less than ToMONTH, then pMonth may be 0, but then pDay MUST be 0.
     * <p/>
     * If pDay != 0, the complete date will be validated regardless of the resolution.
     * If pMonth != 0, the month will be validated regardless of the resolution.
     *
     * @param pMonth 0-12, (0 ok if pDay is 0, AND resolution is ToYEAR)
     * @param pDay   0-(28-31), (0 ok if the resolution is less than ToDAY)
     */
    public void validate( int pYear, int pMonth, int pDay )
            throws IllegalArgumentException
    {
        if ( isValidToDay() || (pDay != 0) )
        {
            String error = validDate( pYear, pMonth, pDay );
            if ( error != null )
            {
                throw new IllegalArgumentException( error );
            }
        }
        else if ( isValidToMonth() || (pMonth != 0) )
        {
            LLcheckMonth( pMonth );
        }
        // Any Year is OK
    }

    /**
     * The expected order of the expected fields of a SimpleDate.
     * <p/>
     * E.g. "dd MMM yyyy" -> "dMy"
     * E.g. "MM/dd/yy" -> "Mdy"
     * E.g. "ddMMM" -> "dM"
     */
    public synchronized String getFieldOrder()
    {
        if ( mFieldOrder == null )
        {
            mFieldOrder = determineFieldOrder( ACCEPTABLE_INDICATORS );
        }
        return mFieldOrder;
    }

    protected synchronized Chunk[] getToStringChunks( YMDaccessor pYMDaccessor )
    {
        if ( mToStringChunks == null )
        {
            mToStringChunks = createChunks( pYMDaccessor, ACCEPTABLE_INDICATORS );
        }
        return mToStringChunks;
    }

    protected IllegalArgumentException formatPlusIndicatorWhy( String pPlus, char pIndicator, String pWhy )
    {
        return new IllegalArgumentException( "Format '" + mFormat + "' " + pPlus + //
                                             " '" + pIndicator + "' indicator" + pWhy );
    }

    protected IllegalArgumentException doesNotContain( DateRes pDateRes, String pWhy )
    {
        return formatPlusIndicatorWhy( "does NOT contain a " + pDateRes.getFriendlyName(), //
                                       pDateRes.getIndicator(), //
                                       pWhy );
    }

    protected IllegalArgumentException doesContain( DateRes pDateRes )
    {
        return formatPlusIndicatorWhy( "contains a " + pDateRes.getFriendlyName(), //
                                       pDateRes.getIndicator(), //
                                       ", but no " + pDateRes.getFriendlyName() + " provided" );
    }

    protected void dayProvided( int pDay )
            throws IllegalArgumentException
    {
        if ( !isValidToDay() )
        {
            throw doesNotContain( DateRes.ToDAY, ", but Day provided" );
        }
        LLcheckDay( pDay );
    }

    protected void monthProvided( int pMonth )
            throws IllegalArgumentException
    {
        if ( !isValidToMonth() )
        {
            throw doesNotContain( DateRes.ToMONTH, ", but Month provided" );
        }
        LLcheckMonth( pMonth );
    }

    protected void noMonthProvided()
            throws IllegalArgumentException
    {
        if ( isValidToMonth() )
        {
            throw doesContain( DateRes.ToMONTH );
        }
    }

    protected void noDayProvided()
            throws IllegalArgumentException
    {
        if ( isValidToDay() )
        {
            throw doesContain( DateRes.ToDAY );
        }
    }

    private String determineFieldOrder( char[] pAcceptableIndicators )
    {
        List<FoundIndicator> zFounds = new ArrayList<FoundIndicator>();
        for ( char indicator : pAcceptableIndicators )
        {
            int at = mFormat.indexOf( indicator );
            if ( at != -1 )
            {
                zFounds.add( new FoundIndicator( at, indicator ) );
            }
        }
        if ( zFounds.size() > 1 )
        {
            Collections.sort( zFounds );
        }
        StringBuilder sb = new StringBuilder( zFounds.size() );
        for ( FoundIndicator zFound : zFounds )
        {
            sb.append( zFound );
        }
        return sb.toString();
    }

    private int indicatorBit( int pBit, DateRes pDateRes )
    {
        return (-1 != mFormat.indexOf( pDateRes.getIndicator() )) ? pBit : 0;
    }

    private static class FoundIndicator implements Comparable
    {
        private int mIndex;
        private char mIndicator;

        public FoundIndicator( int pIndex, char pIndicator )
        {
            mIndex = pIndex;
            mIndicator = pIndicator;
        }

        public String toString()
        {
            return Character.toString( mIndicator );
        }

        public boolean equals( Object o )
        {
            return (this == o) || //
                   ((o instanceof FoundIndicator) && equals( (FoundIndicator) o ));
        }

        public boolean equals( FoundIndicator them )
        {
            return (this == them) || //
                   ((them != null) //
                    && equal( this.mIndex, them.mIndex ) //
                    && equal( this.mIndicator, them.mIndicator ) //
                   );
        }

        public int hashCode()
        {
            return hashCodeEm( calcHashCode( mIndex ), //
                               calcHashCode( (int) mIndicator ) );
        }

        public int compareTo( Object pObject )
        {
            FoundIndicator them = (FoundIndicator) pObject;
            return compare( this.mIndex, them.mIndex );
        }
    }

    protected Chunk[] createChunks( YMDaccessor pYMDaccessor, char[] pAcceptableIndicators )
    {
        ChunkCollector zCC = new ChunkCollector( pYMDaccessor, pAcceptableIndicators );
        int from = 0;
        char curChunkChar = 0;
        for ( int to = 0; to < mFormat.length(); to++ )
        {
            char c = mFormat.charAt( to );
            if ( curChunkChar == 0 )
            {
                if ( zCC.isAcceptableIndicators( c ) )
                {
                    zCC.addChunk( from, to, curChunkChar );
                    from = to;
                    curChunkChar = c;
                }
            }
            else if ( curChunkChar != c )
            {
                zCC.addChunk( from, to, curChunkChar );
                from = to;
                curChunkChar = zCC.isAcceptableIndicators( c ) ? c : 0;
            }
        }
        zCC.addChunk( from, curChunkChar );
        return zCC.getChunks();
    }

    private class ChunkCollector
    {
        List<Chunk> mChunks = new ArrayList<Chunk>();
        private YMDaccessor mYMDaccessor;
        private char[] mAcceptableIndicators;

        public ChunkCollector( YMDaccessor pYMDaccessor, char[] pAcceptableIndicators )
        {
            mYMDaccessor = pYMDaccessor;
            mAcceptableIndicators = pAcceptableIndicators;
        }

        public boolean isAcceptableIndicators( char pChar )
        {
            for ( char zAcceptableIndicator : mAcceptableIndicators )
            {
                if ( pChar == zAcceptableIndicator )
                {
                    return true;
                }
            }
            return false;
        }

        public Chunk[] getChunks()
        {
            return mChunks.toArray( new Chunk[mChunks.size()] );
        }

        public void addChunk( int pFrom, char pCurChunkChar )
        {
            addChunk( pFrom, mFormat.length(), pCurChunkChar );
        }

        public void addChunk( int pFrom, int pTo, char pCurChunkChar )
        {
            Chunk chunk = getChunk( pFrom, pTo, pCurChunkChar );
            if ( chunk != null )
            {
                mChunks.add( chunk );
            }
        }

        public Chunk getChunk( int pFrom, int pTo, char pCurChunkChar )
        {
            if ( pFrom == pTo )
            {
                return null;
            }
            if ( pCurChunkChar != 0 )
            {
                int length = pTo - pFrom;
                switch ( pCurChunkChar )
                {
                    case DateRes.INDICATOR_YEAR:
                        return get_y_nChunk( mYMDaccessor, length );
                    case DateRes.INDICATOR_MONTH:
                        return get_M_nChunk( mYMDaccessor, length );
                    case DateRes.INDICATOR_DAY:
                        return get_d_nChunk( mYMDaccessor, length );
                    default:
                        break;
                }
            }
            return getLiteralChunk( mFormat.substring( pFrom, pTo ) );
        }
    }

    protected static Chunk getLiteralChunk( String pLiteral )
    {
        if ( pLiteral.length() == 1 )
        {
            switch ( pLiteral.charAt( 0 ) )
            {
                case '/':
                    return SLASH;
                case ' ':
                    return SPACE;
                case ':':
                    return COLON;
                case ',':
                    return COMMA;
                case '-':
                    return DASH;
                default:
                    break;
            }
        }
        return new LiteralChunk( pLiteral );
    }

    protected static Chunk get_M_nChunk( YMDaccessor pYMDaccessor, int pLength )
    {
        switch ( pLength )
        {
            case 1:
                return new M_Chunk( pYMDaccessor );
            case 2:
                return new MM_Chunk( pYMDaccessor );
            case 3:
                return new MMM_Chunk( pYMDaccessor );
            default:
                return new MMMM_Chunk( pYMDaccessor );
        }
    }

    protected static Chunk get_y_nChunk( YMDaccessor pYMDaccessor, int pLength )
    {
        switch ( pLength )
        {
            default:
                return new yyy_Chunk( pYMDaccessor, pLength );
            case 2:
                return new yy_Chunk( pYMDaccessor );
            case 1:
                return new y_Chunk( pYMDaccessor );
        }
    }

    protected static Chunk get_d_nChunk( YMDaccessor pYMDaccessor, int pLength )
    {
        return (pLength == 2) ? new dd_Chunk( pYMDaccessor ) : new d_Chunk( pYMDaccessor );
    }

    public boolean equals( Object o )
    {
        return (o instanceof DateFormatControl) && equals( (DateFormatControl) o );
    }

    public boolean equals( DateFormatControl them )
    {
        return (this == them) || //
               ((them != null) //
                && this.mFormat.equals( them.mFormat ) //
               );
    }

    public int hashCode()
    {
        return mFormat.hashCode();
    }

    public int compareTo( T them )
    {
        return this.mFormat.compareTo( them.mFormat );
    }
}

Commits for litesoft/trunk/Java/core/Anywhere/src/org/litesoft/core/simpletypes/temporal/DateFormatControl.java

Diff revisions: vs.
Revision Author Commited Message
2 GeorgeS picture GeorgeS Sun 07 Feb, 2010 12:50:58 +0000