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
472
// This Source Code is in the Public Domain per: http://litesoft.org/License.txt
package org.litesoft.core.simpletypes.temporal;

import java.util.*;

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

/**
 * Format Control for CalendarAccessorYMD 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 DateFormat extends CalendarSupport<DateFormat> implements TemporalParsingSupport,
                                                                       SimpleType
{
    private static final long serialVersionUID = 1L;

    public static final String DEFAULT_YMD_FORMAT = "dd MMM yyyy";
    public static final DateFormat DEFAULT_YMD = new DateFormat( DEFAULT_YMD_FORMAT );

    public static final String DEFAULT_YM_FORMAT = "MMM yyyy";
    public static final DateFormat DEFAULT_YM = new DateFormat( DEFAULT_YM_FORMAT );

    public static final String DEFAULT_Y_FORMAT = "yyyy";
    public static final DateFormat DEFAULT_Y = new DateFormat( DEFAULT_Y_FORMAT );

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

    private String mFormat = DEFAULT_YMD_FORMAT;

    private DateRes mDateRes = DateRes.ToDAY;

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

    @SuppressWarnings({"deprecation", "UnusedDeclaration"}) @Deprecated /** for Serialization */
    protected DateFormat()
    {
    }

    public DateFormat( String pFormat )
    {
        mFormat = Strings.noEmpty( pFormat, DEFAULT_YMD_FORMAT );
        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
                mDateRes = DateRes.ToDAY;
                break;
            case 3: // Year & Month
                mDateRes = DateRes.ToMONTH;
                break;
            case 1: // Year Only
                mDateRes = DateRes.ToYEAR;
                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 format( CalendarAccessorYMD pDate )
    {
        if ( pDate == null )
        {
            return null; // TODO: Should this be ""?
        }
        StringBuilder sb = new StringBuilder();
        for ( Chunk chunk : getToStringChunks() )
        {
            chunk.appendTo( sb, pDate );
        }
        return sb.toString();
    }

    public String getFormat()
    {
        return mFormat;
    }

    public DateRes getDateRes()
    {
        return mDateRes;
    }

    public boolean isValidToMonth()
    {
        return mDateRes.isValidToMonth();
    }

    public boolean isValidToDay()
    {
        return mDateRes.isValidToDay();
    }

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

    /**
     * The expected order of the expected fields of a CalendarYMD.
     * <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()
    {
        if ( mToStringChunks == null )
        {
            mToStringChunks = createChunks( ACCEPTABLE_INDICATORS );
        }
        return mToStringChunks;
    }

    protected void validateMonthAdjustable()
            throws IllegalArgumentException
    {
        if ( !isValidToMonth() )
        {
            throw doesNotContain( DateRes.ToMONTH, ", but Month adjustment attempted" );
        }
    }

    protected void validateDayAdjustable()
            throws IllegalArgumentException
    {
        if ( !isValidToDay() )
        {
            throw doesNotContain( DateRes.ToDAY, ", but Day adjustment attempted" );
        }
    }

    protected void validateValidToDay( String pWhat )
    {
        if ( !isValidToDay() )
        {
            throw doesNotContain( DateRes.ToDAY, ", but " + pWhat + " attempted" );
        }
    }

    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 noMonthProvided()
            throws IllegalArgumentException
    {
        if ( isValidToMonth() )
        {
            throw doesContain( DateRes.ToMONTH );
        }
        noDayProvided();
    }

    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;
        }

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

        @Override
        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 ) //
                   );
        }

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

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

    protected Chunk[] createChunks( char[] pAcceptableIndicators )
    {
        ChunkCollector zCC = new ChunkCollector( 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 char[] mAcceptableIndicators;

        public ChunkCollector( char[] pAcceptableIndicators )
        {
            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( length );
                    case DateRes.INDICATOR_MONTH:
                        return get_M_nChunk( length );
                    case DateRes.INDICATOR_DAY:
                        return get_d_nChunk( 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 literalChunk( pLiteral );
    }

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

    protected static Chunk get_y_nChunk( int pLength )
    {
        switch ( pLength )
        {
            case 1:
                return y_Chunk;
            case 2:
                return yy_Chunk;
            case 4:
                return yyyy_Chunk;
            default:
                return yyy_Chunk( pLength );
        }
    }

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

    @Override
    public boolean equals( Object o )
    {
        return (o instanceof DateFormat) && equals( (DateFormat) o );
    }

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

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

    @Override
    public int compareTo( DateFormat them )
    {
        return this.mFormat.compareTo( them.mFormat );
    }
}

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

Diff revisions: vs.
Revision Author Commited Message
860 Diff Diff GeorgeS picture GeorgeS Mon 05 Nov, 2012 01:39:02 +0000
859 Diff Diff GeorgeS picture GeorgeS Mon 05 Nov, 2012 01:26:38 +0000
853 Diff Diff GeorgeS picture GeorgeS Sun 04 Nov, 2012 15:18:21 +0000
851 Diff Diff GeorgeS picture GeorgeS Mon 08 Oct, 2012 00:05:32 +0000

Breaking the code as Temporal changes are implemented...

849 Diff Diff GeorgeS picture GeorgeS Tue 11 Sep, 2012 17:11:59 +0000

Clean up serialization

801 Diff Diff GeorgeS picture GeorgeS Wed 15 Aug, 2012 03:59:02 +0000
151 Diff Diff GeorgeS picture GeorgeS Thu 17 Mar, 2011 04:16:22 +0000
50 Diff Diff GeorgeS picture GeorgeS Tue 13 Apr, 2010 11:51:38 +0000
49 Diff Diff GeorgeS picture GeorgeS Mon 12 Apr, 2010 02:59:10 +0000

License Text

2 GeorgeS picture GeorgeS Sun 07 Feb, 2010 12:50:58 +0000