Home | History | Annotate | Download | only in text
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html#License
      3 /*
      4  *******************************************************************************
      5  * Copyright (C) 1996-2012, International Business Machines Corporation and    *
      6  * others. All Rights Reserved.                                                *
      7  *******************************************************************************
      8  */
      9 
     10 package com.ibm.icu.text;
     11 
     12 import java.text.AttributedCharacterIterator;
     13 import java.text.AttributedCharacterIterator.Attribute;
     14 import java.text.AttributedString;
     15 import java.text.CharacterIterator;
     16 import java.text.FieldPosition;
     17 import java.text.ParsePosition;
     18 import java.util.Date;
     19 import java.util.Locale;
     20 import java.util.Map;
     21 import java.util.Map.Entry;
     22 
     23 import com.ibm.icu.util.Calendar;
     24 import com.ibm.icu.util.ULocale;
     25 
     26 
     27 /**
     28  * {@icuenhanced java.text.SimpleDateFormat}.{@icu _usage_}
     29  *
     30  * <p><code>SimpleDateFormat</code> is a concrete class for formatting and
     31  * parsing dates in a locale-sensitive manner. It allows for formatting
     32  * (date -> text), parsing (text -> date), and normalization.
     33  *
     34  * <p>
     35  * <code>SimpleDateFormat</code> allows you to start by choosing
     36  * any user-defined patterns for date-time formatting. However, you
     37  * are encouraged to create a date-time formatter with either
     38  * <code>getTimeInstance</code>, <code>getDateInstance</code>, or
     39  * <code>getDateTimeInstance</code> in <code>DateFormat</code>. Each
     40  * of these class methods can return a date/time formatter initialized
     41  * with a default format pattern. You may modify the format pattern
     42  * using the <code>applyPattern</code> methods as desired.
     43  * For more information on using these methods, see
     44  * {@link DateFormat}.
     45  *
     46  * <p>
     47  * <strong>Time Format Syntax:</strong>
     48  * <p>
     49  * To specify the time format use a <em>time pattern</em> string.
     50  * In this pattern, all ASCII letters are reserved as pattern letters,
     51  * which are defined as the following:
     52  * <blockquote>
     53  * <pre>
     54  * Symbol   Meaning                 Presentation        Example
     55  * ------   -------                 ------------        -------
     56  * G        era designator          (Text)              AD
     57  * y&#x2020;       year                    (Number)            1996
     58  * Y*       year (week of year)     (Number)            1997
     59  * u*       extended year           (Number)            4601
     60  * M        month in year           (Text & Number)     July & 07
     61  * d        day in month            (Number)            10
     62  * h        hour in am/pm (1~12)    (Number)            12
     63  * H        hour in day (0~23)      (Number)            0
     64  * m        minute in hour          (Number)            30
     65  * s        second in minute        (Number)            55
     66  * S        fractional second       (Number)            978
     67  * E        day of week             (Text)              Tuesday
     68  * e*       day of week (local 1~7) (Text & Number)     Tuesday & 2
     69  * D        day in year             (Number)            189
     70  * F        day of week in month    (Number)            2 (2nd Wed in July)
     71  * w        week in year            (Number)            27
     72  * W        week in month           (Number)            2
     73  * a        am/pm marker            (Text)              PM
     74  * k        hour in day (1~24)      (Number)            24
     75  * K        hour in am/pm (0~11)    (Number)            0
     76  * z        time zone               (Text)              Pacific Standard Time
     77  * Z        time zone (RFC 822)     (Number)            -0800
     78  * v        time zone (generic)     (Text)              Pacific Time
     79  * V        time zone (location)    (Text)              United States (Los Angeles)
     80  * g*       Julian day              (Number)            2451334
     81  * A*       milliseconds in day     (Number)            69540000
     82  * Q*       quarter in year         (Text & Number)     Q1 & 01
     83  * c*       stand alone day of week (Text & Number)     Tuesday & 2
     84  * L*       stand alone month       (Text & Number)     July & 07
     85  * q*       stand alone quarter     (Text & Number)     Q1 & 01
     86  * '        escape for text         (Delimiter)         'Date='
     87  * ''       single quote            (Literal)           'o''clock'
     88  * </pre>
     89  * </blockquote>
     90  * <tt><b>*</b></tt> These items are not supported by Java's SimpleDateFormat.<br>
     91  * <tt><b>&#x2020;</b></tt> ICU interprets a single 'y' differently than Java.</p>
     92  * <p>
     93  * The count of pattern letters determine the format.
     94  * <p>
     95  * <strong>(Text)</strong>: 4 or more pattern letters--use full form,
     96  * &lt; 4--use short or abbreviated form if one exists.
     97  * <p>
     98  * <strong>(Number)</strong>: the minimum number of digits. Shorter
     99  * numbers are zero-padded to this amount. Year is handled specially;
    100  * that is, if the count of 'y' is 2, the Year will be truncated to 2 digits.
    101  * (e.g., if "yyyy" produces "1997", "yy" produces "97".)
    102  * Unlike other fields, fractional seconds are padded on the right with zero.
    103  * <p>
    104  * <strong>(Text & Number)</strong>: 3 or over, use text, otherwise use number.
    105  * <p>
    106  * Any characters in the pattern that are not in the ranges of ['a'..'z']
    107  * and ['A'..'Z'] will be treated as quoted text. For instance, characters
    108  * like ':', '.', ' ', '#' and '@' will appear in the resulting time text
    109  * even they are not embraced within single quotes.
    110  * <p>
    111  * A pattern containing any invalid pattern letter will result in a thrown
    112  * exception during formatting or parsing.
    113  *
    114  * <p>
    115  * <strong>Examples Using the US Locale:</strong>
    116  * <blockquote>
    117  * <pre>
    118  * Format Pattern                         Result
    119  * --------------                         -------
    120  * "yyyy.MM.dd G 'at' HH:mm:ss vvvv" ->>  1996.07.10 AD at 15:08:56 Pacific Time
    121  * "EEE, MMM d, ''yy"                ->>  Wed, July 10, '96
    122  * "h:mm a"                          ->>  12:08 PM
    123  * "hh 'o''clock' a, zzzz"           ->>  12 o'clock PM, Pacific Daylight Time
    124  * "K:mm a, vvv"                     ->>  0:00 PM, PT
    125  * "yyyyy.MMMMM.dd GGG hh:mm aaa"    ->>  01996.July.10 AD 12:08 PM
    126  * </pre>
    127  * </blockquote>
    128  * <strong>Code Sample:</strong>
    129  * <blockquote>
    130  * <pre>
    131  * SimpleTimeZone pdt = new SimpleTimeZone(-8 * 60 * 60 * 1000, "PST");
    132  * pdt.setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2*60*60*1000);
    133  * pdt.setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*60*60*1000);
    134  * <br>
    135  * // Format the current time.
    136  * SimpleDateFormat formatter
    137  *     = new SimpleDateFormat ("yyyy.MM.dd G 'at' hh:mm:ss a zzz");
    138  * Date currentTime_1 = new Date();
    139  * String dateString = formatter.format(currentTime_1);
    140  * <br>
    141  * // Parse the previous string back into a Date.
    142  * ParsePosition pos = new ParsePosition(0);
    143  * Date currentTime_2 = formatter.parse(dateString, pos);
    144  * </pre>
    145  * </blockquote>
    146  * In the example, the time value <code>currentTime_2</code> obtained from
    147  * parsing will be equal to <code>currentTime_1</code>. However, they may not be
    148  * equal if the am/pm marker 'a' is left out from the format pattern while
    149  * the "hour in am/pm" pattern symbol is used. This information loss can
    150  * happen when formatting the time in PM.
    151  *
    152  * <p>When parsing a date string using the abbreviated year pattern ("yy"),
    153  * SimpleDateFormat must interpret the abbreviated year
    154  * relative to some century.  It does this by adjusting dates to be
    155  * within 80 years before and 20 years after the time the SimpleDateFormat
    156  * instance is created. For example, using a pattern of "MM/dd/yy" and a
    157  * SimpleDateFormat instance created on Jan 1, 1997,  the string
    158  * "01/11/12" would be interpreted as Jan 11, 2012 while the string "05/04/64"
    159  * would be interpreted as May 4, 1964.
    160  * During parsing, only strings consisting of exactly two digits, as defined by
    161  * {@link com.ibm.icu.lang.UCharacter#isDigit(int)}, will be parsed into the default
    162  * century.
    163  * Any other numeric string, such as a one digit string, a three or more digit
    164  * string, or a two digit string that isn't all digits (for example, "-1"), is
    165  * interpreted literally.  So "01/02/3" or "01/02/003" are parsed, using the
    166  * same pattern, as Jan 2, 3 AD.  Likewise, "01/02/-3" is parsed as Jan 2, 4 BC.
    167  *
    168  * <p>If the year pattern does not have exactly two 'y' characters, the year is
    169  * interpreted literally, regardless of the number of digits.  So using the
    170  * pattern "MM/dd/yyyy", "01/11/12" parses to Jan 11, 12 A.D.
    171  *
    172  * <p>When numeric fields abut one another directly, with no intervening delimiter
    173  * characters, they constitute a run of abutting numeric fields.  Such runs are
    174  * parsed specially.  For example, the format "HHmmss" parses the input text
    175  * "123456" to 12:34:56, parses the input text "12345" to 1:23:45, and fails to
    176  * parse "1234".  In other words, the leftmost field of the run is flexible,
    177  * while the others keep a fixed width.  If the parse fails anywhere in the run,
    178  * then the leftmost field is shortened by one character, and the entire run is
    179  * parsed again. This is repeated until either the parse succeeds or the
    180  * leftmost field is one character in length.  If the parse still fails at that
    181  * point, the parse of the run fails.
    182  *
    183  * <p>For time zones that have no names, use strings GMT+hours:minutes or
    184  * GMT-hours:minutes.
    185  *
    186  * <p>The calendar defines what is the first day of the week, the first week
    187  * of the year, whether hours are zero based or not (0 vs 12 or 24), and the
    188  * time zone. There is one common decimal format to handle all the numbers;
    189  * the digit count is handled programmatically according to the pattern.
    190  *
    191  * <h4>Synchronization</h4>
    192  *
    193  * Date formats are not synchronized. It is recommended to create separate
    194  * format instances for each thread. If multiple threads access a format
    195  * concurrently, it must be synchronized externally.
    196  *
    197  * @see          com.ibm.icu.util.Calendar
    198  * @see          com.ibm.icu.util.GregorianCalendar
    199  * @see          com.ibm.icu.util.TimeZone
    200  * @see          DateFormat
    201  * @see          DateFormatSymbols
    202  * @see          DecimalFormat
    203  * @author       Mark Davis, Chen-Lieh Huang, Alan Liu
    204  * @stable ICU 2.0
    205  */
    206 public class SimpleDateFormat extends DateFormat {
    207     private static final long serialVersionUID = 1L;
    208 
    209     /**
    210      * Constructs a SimpleDateFormat using the default pattern for the default
    211      * locale.  <b>Note:</b> Not all locales support SimpleDateFormat; for full
    212      * generality, use the factory methods in the DateFormat class.
    213      *
    214      * @see DateFormat
    215      * @stable ICU 2.0
    216      */
    217     public SimpleDateFormat() {
    218         super(new java.text.SimpleDateFormat());
    219     }
    220 
    221     /**
    222      * Constructs a SimpleDateFormat using the given pattern in the default
    223      * locale.  <b>Note:</b> Not all locales support SimpleDateFormat; for full
    224      * generality, use the factory methods in the DateFormat class.
    225      * @stable ICU 2.0
    226      */
    227     public SimpleDateFormat(String pattern)
    228     {
    229         super(new java.text.SimpleDateFormat(pattern));
    230     }
    231 
    232     /**
    233      * Constructs a SimpleDateFormat using the given pattern and locale.
    234      * <b>Note:</b> Not all locales support SimpleDateFormat; for full
    235      * generality, use the factory methods in the DateFormat class.
    236      * @stable ICU 2.0
    237      */
    238     public SimpleDateFormat(String pattern, Locale loc)
    239     {
    240         super(new java.text.SimpleDateFormat(pattern, loc));
    241     }
    242 
    243     /**
    244      * Constructs a SimpleDateFormat using the given pattern and locale.
    245      * <b>Note:</b> Not all locales support SimpleDateFormat; for full
    246      * generality, use the factory methods in the DateFormat class.
    247      * @stable ICU 3.2
    248      */
    249     public SimpleDateFormat(String pattern, ULocale loc)
    250     {
    251         this(pattern, loc.toLocale());
    252     }
    253 
    254 //    /**
    255 //     * Constructs a SimpleDateFormat using the given pattern , override and locale.
    256 //     * @param pattern The pattern to be used
    257 //     * @param override The override string.  A numbering system override string can take one of the following forms:
    258 //     *     1). If just a numbering system name is specified, it applies to all numeric fields in the date format pattern.
    259 //     *     2). To specify an alternate numbering system on a field by field basis, use the field letters from the pattern
    260 //     *         followed by an = sign, followed by the numbering system name.  For example, to specify that just the year
    261 //     *         be formatted using Hebrew digits, use the override "y=hebr".  Multiple overrides can be specified in a single
    262 //     *         string by separating them with a semi-colon. For example, the override string "m=thai;y=deva" would format using
    263 //     *         Thai digits for the month and Devanagari digits for the year.
    264 //     * @param loc The locale to be used
    265 //     * @stable ICU 4.2
    266 //     */
    267 //    public SimpleDateFormat(String pattern, String override, ULocale loc)
    268 //    {
    269 //        throw new UnsupportedOperationException("Method not supported by com.ibm.icu.base");
    270 //    }
    271 
    272     /**
    273      * Constructs a SimpleDateFormat using the given pattern and
    274      * locale-specific symbol data.
    275      * Warning: uses default locale for digits!
    276      * @stable ICU 2.0
    277      */
    278     public SimpleDateFormat(String pattern, DateFormatSymbols formatData)
    279     {
    280         super(new java.text.SimpleDateFormat(pattern, formatData.dfs));
    281     }
    282 
    283     /**
    284      * Sets the 100-year period 2-digit years will be interpreted as being in
    285      * to begin on the date the user specifies.
    286      * @param startDate During parsing, two digit years will be placed in the range
    287      * <code>startDate</code> to <code>startDate + 100 years</code>.
    288      * @stable ICU 2.0
    289      */
    290     public void set2DigitYearStart(Date startDate) {
    291         ((java.text.SimpleDateFormat)dateFormat).set2DigitYearStart(startDate);
    292     }
    293 
    294     /**
    295      * Returns the beginning date of the 100-year period 2-digit years are interpreted
    296      * as being within.
    297      * @return the start of the 100-year period into which two digit years are
    298      * parsed
    299      * @stable ICU 2.0
    300      */
    301     public Date get2DigitYearStart() {
    302         return ((java.text.SimpleDateFormat)dateFormat).get2DigitYearStart();
    303     }
    304 
    305     /**
    306      * Formats a date or time, which is the standard millis
    307      * since January 1, 1970, 00:00:00 GMT.
    308      * <p>Example: using the US locale:
    309      * "yyyy.MM.dd G 'at' HH:mm:ss zzz" ->> 1996.07.10 AD at 15:08:56 PDT
    310      * @param cal the calendar whose date-time value is to be formatted into a date-time string
    311      * @param toAppendTo where the new date-time text is to be appended
    312      * @param pos the formatting position. On input: an alignment field,
    313      * if desired. On output: the offsets of the alignment field.
    314      * @return the formatted date-time string.
    315      * @see DateFormat
    316      * @stable ICU 2.0
    317      */
    318     public StringBuffer format(Calendar cal, StringBuffer toAppendTo,
    319                                FieldPosition pos) {
    320         StringBuffer result;
    321         FieldPosition jdkPos = toJDKFieldPosition(pos);
    322         synchronized(dateFormat) {
    323             java.util.Calendar oldCal = dateFormat.getCalendar();
    324             dateFormat.setCalendar(cal.calendar);
    325             result = dateFormat.format(cal.getTime(), toAppendTo, jdkPos);
    326             dateFormat.setCalendar(oldCal);
    327         }
    328         if (jdkPos != null) {
    329             pos.setBeginIndex(jdkPos.getBeginIndex());
    330             pos.setEndIndex(jdkPos.getEndIndex());
    331         }
    332         return result;
    333     }
    334 
    335     /**
    336      * Overrides superclass method
    337      * @stable ICU 2.0
    338      */
    339     public void setNumberFormat(NumberFormat newNumberFormat) {
    340         super.setNumberFormat(newNumberFormat);
    341     }
    342 
    343     /**
    344      * Overrides DateFormat
    345      * @see DateFormat
    346      * @stable ICU 2.0
    347      */
    348     public void parse(String text, Calendar cal, ParsePosition parsePos)
    349     {
    350         // Note: parsed time zone won't be set in the result calendar
    351         cal.setTime(dateFormat.parse(text, parsePos));
    352     }
    353 
    354     /**
    355      * Return a pattern string describing this date format.
    356      * @stable ICU 2.0
    357      */
    358     public String toPattern() {
    359         return ((java.text.SimpleDateFormat)dateFormat).toPattern();
    360     }
    361 
    362     /**
    363      * Return a localized pattern string describing this date format.
    364      * @stable ICU 2.0
    365      */
    366     public String toLocalizedPattern() {
    367         return ((java.text.SimpleDateFormat)dateFormat).toLocalizedPattern();
    368     }
    369 
    370     /**
    371      * Apply the given unlocalized pattern string to this date format.
    372      * @stable ICU 2.0
    373      */
    374     public void applyPattern(String pat) {
    375         ((java.text.SimpleDateFormat)dateFormat).applyPattern(pat);
    376     }
    377 
    378     /**
    379      * Apply the given localized pattern string to this date format.
    380      * @stable ICU 2.0
    381      */
    382     public void applyLocalizedPattern(String pat) {
    383         ((java.text.SimpleDateFormat)dateFormat).applyLocalizedPattern(pat);
    384     }
    385 
    386     /**
    387      * Gets the date/time formatting data.
    388      * @return a copy of the date-time formatting data associated
    389      * with this date-time formatter.
    390      * @stable ICU 2.0
    391      */
    392     public DateFormatSymbols getDateFormatSymbols() {
    393         return new DateFormatSymbols(((java.text.SimpleDateFormat)dateFormat).getDateFormatSymbols());
    394     }
    395 
    396     /**
    397      * Allows you to set the date/time formatting data.
    398      * @param newFormatSymbols the new symbols
    399      * @stable ICU 2.0
    400      */
    401     public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols) {
    402         ((java.text.SimpleDateFormat)dateFormat).setDateFormatSymbols(newFormatSymbols.dfs);
    403     }
    404 
    405 //    /**
    406 //     * {@icu} Gets the time zone formatter which this date/time
    407 //     * formatter uses to format and parse a time zone.
    408 //     *
    409 //     * @return the time zone formatter which this date/time
    410 //     * formatter uses.
    411 //     * @draft ICU 49
    412 //     * @provisional This API might change or be removed in a future release.
    413 //     */
    414 //    public TimeZoneFormat getTimeZoneFormat() {
    415 //        throw new UnsupportedOperationException("Method not supported by com.ibm.icu.base");
    416 //    }
    417 
    418 //    /**
    419 //     * {@icu} Allows you to set the time zone formatter.
    420 //     *
    421 //     * @param tzfmt the new time zone formatter
    422 //     * @draft ICU 49
    423 //     * @provisional This API might change or be removed in a future release.
    424 //     */
    425 //    public void setTimeZoneFormat(TimeZoneFormat tzfmt) {
    426 //        throw new UnsupportedOperationException("Method not supported by com.ibm.icu.base");
    427 //    }
    428 
    429     // For clone to use
    430     private SimpleDateFormat(java.text.SimpleDateFormat sdf) {
    431         super(sdf);
    432     }
    433 
    434     /**
    435      * Overrides Cloneable
    436      * @stable ICU 2.0
    437      */
    438     public Object clone() {
    439         return new SimpleDateFormat((java.text.SimpleDateFormat)dateFormat.clone());
    440     }
    441 
    442     /**
    443      * Override hashCode.
    444      * Generates the hash code for the SimpleDateFormat object
    445      * @stable ICU 2.0
    446      */
    447     public int hashCode()
    448     {
    449         return super.hashCode();
    450     }
    451 
    452     /**
    453      * Override equals.
    454      * @stable ICU 2.0
    455      */
    456     public boolean equals(Object obj)
    457     {
    458         return super.equals(obj);
    459     }
    460 
    461     /**
    462      * Format the object to an attributed string, and return the corresponding iterator
    463      * Overrides superclass method.
    464      *
    465      * @param obj The object to format
    466      * @return <code>AttributedCharacterIterator</code> describing the formatted value.
    467      *
    468      * @stable ICU 3.8
    469      */
    470     public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
    471         AttributedCharacterIterator it = dateFormat.formatToCharacterIterator(obj);
    472 
    473         // Extract formatted String first
    474         StringBuilder sb = new StringBuilder();
    475         for (char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
    476             sb.append(c);
    477         }
    478 
    479         // Create AttributedString
    480         AttributedString attrstr = new AttributedString(sb.toString());
    481 
    482         // Map JDK Field to ICU Field
    483         int idx = 0;
    484         it.first();
    485         while (idx < it.getEndIndex()) {
    486             int end = it.getRunLimit();
    487             Map<Attribute, Object> attributes = it.getAttributes();
    488             if (attributes != null) {
    489                 for (Entry<Attribute, Object> entry : attributes.entrySet()) {
    490                     Attribute attr = entry.getKey();
    491                     Object val = entry.getValue();
    492                     if (attr.equals(java.text.DateFormat.Field.AM_PM)) {
    493                         val = attr = Field.AM_PM;
    494                     } else if (attr.equals(java.text.DateFormat.Field.DAY_OF_MONTH)) {
    495                         val = attr = Field.DAY_OF_MONTH;
    496                     } else if (attr.equals(java.text.DateFormat.Field.DAY_OF_WEEK)) {
    497                         val = attr = Field.DAY_OF_WEEK ;
    498                     } else if (attr.equals(java.text.DateFormat.Field.DAY_OF_WEEK_IN_MONTH)) {
    499                         val = attr = Field.DAY_OF_WEEK_IN_MONTH ;
    500                     } else if (attr.equals(java.text.DateFormat.Field.DAY_OF_YEAR)) {
    501                         val = attr = Field.DAY_OF_YEAR;
    502                     } else if (attr.equals(java.text.DateFormat.Field.ERA)) {
    503                         val = attr = Field.ERA;
    504                     } else if (attr.equals(java.text.DateFormat.Field.HOUR_OF_DAY0)) {
    505                         val = attr = Field.HOUR_OF_DAY0;
    506                     } else if (attr.equals(java.text.DateFormat.Field.HOUR_OF_DAY1)) {
    507                         val = attr = Field.HOUR_OF_DAY1;
    508                     } else if (attr.equals(java.text.DateFormat.Field.HOUR0)) {
    509                         val = attr = Field.HOUR0;
    510                     } else if (attr.equals(java.text.DateFormat.Field.HOUR1)) {
    511                         val = attr = Field.HOUR1;
    512                     } else if (attr.equals(java.text.DateFormat.Field.MILLISECOND)) {
    513                         val = attr = Field.MILLISECOND;
    514                     } else if (attr.equals(java.text.DateFormat.Field.MINUTE)) {
    515                         val = attr = Field.MINUTE;
    516                     } else if (attr.equals(java.text.DateFormat.Field.MONTH)) {
    517                         val = attr = Field.MONTH;
    518                     } else if (attr.equals(java.text.DateFormat.Field.SECOND)) {
    519                         val = attr = Field.SECOND;
    520                     } else if (attr.equals(java.text.DateFormat.Field.TIME_ZONE)) {
    521                         val = attr = Field.TIME_ZONE;
    522                     } else if (attr.equals(java.text.DateFormat.Field.WEEK_OF_MONTH)) {
    523                         val = attr = Field.WEEK_OF_MONTH;
    524                     } else if (attr.equals(java.text.DateFormat.Field.WEEK_OF_YEAR)) {
    525                         val = attr = Field.WEEK_OF_YEAR;
    526                     } else if (attr.equals(java.text.DateFormat.Field.YEAR)) {
    527                         val = attr = Field.YEAR;
    528                     }
    529                     attrstr.addAttribute(attr, val, idx, end);
    530                 }
    531             }
    532             idx = end;
    533             while (it.getIndex() < idx) {
    534                 it.next();
    535             }
    536         }
    537 
    538         return attrstr.getIterator();
    539     }
    540 }
    541