Home | History | Annotate | Download | only in util
      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) 2007, International Business Machines Corporation and         *
      6  * others. All Rights Reserved.                                                *
      7  *******************************************************************************
      8  */
      9 package com.ibm.icu.dev.test.util;
     10 
     11 import com.ibm.icu.impl.Utility;
     12 
     13 /**
     14  * @author srl
     15  *
     16  * analog of FieldsSet in C++
     17  */
     18 public class FieldsSet {
     19     public static final int NO_ENUM = -1;
     20 
     21     protected FieldsSet(int whichEnum, int fieldsCount) {
     22         if (fieldsCount <= 0 && whichEnum != NO_ENUM) {
     23             fieldsCount = DebugUtilities.enumCount(whichEnum);
     24         }
     25         fEnum = whichEnum;
     26         fFieldsCount = fieldsCount;
     27         if(fieldsCount<0) {
     28             throw new InternalError("Preposterous field count " + fieldsCount);
     29         }
     30         fValues = new int[fFieldsCount];
     31         fIsSet = new boolean[fFieldsCount];
     32         clear();
     33     }
     34 
     35     protected int fEnum = NO_ENUM;
     36 
     37     protected int fFieldsCount = 0;
     38 
     39     protected int fValues[] = null;
     40 
     41     protected boolean fIsSet[] = null;
     42 
     43     public void clear() {
     44         for (int i = 0; i < fFieldsCount; i++) {
     45             clear(i);
     46         }
     47     }
     48 
     49     public void clear(int field) {
     50         fValues[field] = -1;
     51         fIsSet[field] = false;
     52     }
     53 
     54     public void set(int field, int amount) {
     55         fValues[field] = amount;
     56         fIsSet[field] = true;
     57     }
     58 
     59     public boolean isSet(int field) {
     60         return fIsSet[field];
     61     }
     62 
     63     public int get(int field) {
     64         if (fIsSet[field]) {
     65             return fValues[field];
     66         } else {
     67             return -1;
     68         }
     69     }
     70 
     71     public boolean isSameType(FieldsSet other) {
     72         return ((other.fEnum == fEnum) && (other.fFieldsCount == fFieldsCount));
     73     }
     74 
     75     public int fieldCount() {
     76         return fFieldsCount;
     77     }
     78 
     79     /**
     80      * @param other  "expected" set to match against
     81      * @return a formatted string listing which fields are set in this, with the
     82      *         comparison made agaainst those fields in other, or, 'null' if there is no difference.
     83      */
     84     public String diffFrom(FieldsSet other) {
     85         StringBuffer str = new StringBuffer();
     86         if(!isSameType(other)) {
     87             throw new IllegalArgumentException("U_ILLEGAL_ARGUMENT_ERROR: FieldsSet of a different type!");
     88         }
     89         for (int i=0; i<fieldCount(); i++) {
     90             if (isSet(i)) {
     91                 int myVal = get(i);
     92                 int theirVal = other.get(i);
     93 
     94                 if(fEnum != NO_ENUM) {
     95                     String fieldName = DebugUtilities.enumString(fEnum, i);
     96 
     97                     String aval = Integer.toString(myVal);
     98                     String bval = Integer.toString(theirVal);
     99 
    100                     str.append(fieldName +"="+aval+" not "+bval+", ");
    101                 } else {
    102                     str.append(Integer.toString(i) + "=" + myVal+" not " + theirVal+", ");
    103                 }
    104             }
    105         }
    106         if(str.length()==0) {
    107             return null;
    108         }
    109         return str.toString();
    110     }
    111 
    112     /**
    113      * @param str string to parse
    114      * @param status formatted string for status
    115      */
    116     public int parseFrom(String str) {
    117         return parseFrom(str, null);
    118     }
    119 
    120     public int parseFrom(String str, FieldsSet inheritFrom) {
    121         int goodFields = 0;
    122 
    123         String[] fields = Utility.split(str, ',');
    124         for(int i=0;i<fields.length;i++) {
    125             String fieldStr = fields[i];
    126             String kv[] = Utility.split(fieldStr, '=');
    127             if(kv.length < 1 || kv.length > 2) {
    128                 throw new InternalError("split around '=' failed: " + fieldStr);
    129             }
    130             String key = kv[0];
    131             String value = "";
    132             if(kv.length>1) {
    133                 value = kv[1];
    134             }
    135 
    136             int field = handleParseName(inheritFrom, key, value);
    137             if(field != -1) {
    138                 handleParseValue(inheritFrom, field, value);
    139                 goodFields++;
    140             }
    141         }
    142 
    143         return goodFields;
    144     }
    145 
    146     /**
    147      * Callback interface for subclass. This function is called when parsing a
    148      * field name, such as "MONTH" in "MONTH=4". Base implementation is to
    149      * lookup the enum value using udbg_* utilities, or else as an integer if
    150      * enum is not available.
    151      *
    152      * If there is a special directive, the implementer can catch it here and
    153      * return -1 after special processing completes.
    154      *
    155      * @param inheritFrom  the set inheriting from - may be null.
    156      * @param name  the field name (key side)
    157      * @param substr  the string in question (value side)
    158      * @param status  error status - set to error for failure.
    159      * @return field number, or negative if field should be skipped.
    160      */
    161     protected int handleParseName(FieldsSet inheritFrom, String name,
    162             String substr) {
    163         int field = -1;
    164         if(fEnum != NO_ENUM) {
    165             field = DebugUtilities.enumByString(fEnum, name);
    166         }
    167         if(field < 0) {
    168             field = Integer.parseInt(name);
    169         }
    170         return field;
    171     }
    172 
    173     /**
    174      * Callback interface for subclass. Base implementation is to call
    175      * parseValueDefault(...)
    176      *
    177      * @param inheritFrom  the set inheriting from - may be null.
    178      * @param field   which field is being parsed
    179      * @param substr  the string in question (value side)
    180      * @param status  error status - set to error for failure.
    181      * @see parseValueDefault
    182      */
    183     protected void handleParseValue(FieldsSet inheritFrom, int field,
    184             String substr) {
    185         parseValueDefault(inheritFrom, field, substr);
    186     }
    187 
    188     /**
    189      * the default implementation for handleParseValue. Base implementation is
    190      * to parse a decimal integer value, or inherit from inheritFrom if the
    191      * string is 0-length. Implementations of this function should call
    192      * set(field,...) on successful parse.
    193      *
    194      * @see handleParseValue
    195      */
    196     protected void parseValueDefault(FieldsSet inheritFrom, int field,
    197             String substr) {
    198         if(substr.length()==0) {
    199             if(inheritFrom == null) {
    200                 throw new InternalError("Trying to inherit from field " + field + " but inheritFrom is null");
    201             }
    202             if(!inheritFrom.isSet(field)) {
    203                 throw new InternalError("Trying to inherit from field " + field + " but inheritFrom["+field+"] is  not set");
    204             }
    205             set(field,inheritFrom.get(field));
    206         } else {
    207             int value = Integer.parseInt(substr);
    208             set(field, value);
    209         }
    210     }
    211 
    212     /**
    213      * convenience implementation for handleParseValue attempt to load a value
    214      * from an enum value using udbg_enumByString() if fails, will call
    215      * parseValueDefault()
    216      *
    217      * @see handleParseValue
    218      */
    219     protected void parseValueEnum(int type, FieldsSet inheritFrom, int field,
    220             String substr) {
    221         int value = DebugUtilities.enumByString(type, substr);
    222         if(value>=0) {
    223             set(field,value);
    224             return;
    225         }
    226         parseValueDefault(inheritFrom, field, substr);
    227     }
    228 
    229     public String fieldName(int field) {
    230         return (fEnum!=NO_ENUM)?DebugUtilities.enumString(fEnum, field):Integer.toString(field);
    231     }
    232 
    233     public String toString() {
    234         String str = getClass().getName()+" ["+fFieldsCount+","
    235         +(fEnum!=NO_ENUM?DebugUtilities.typeString(fEnum):Integer.toString(fEnum))+"]: ";
    236         for(int i=0;i<fFieldsCount;i++) {
    237             if(isSet(i)) {
    238                 str = str + fieldName(i)+"="+get(i)+",";
    239             }
    240         }
    241         return str;
    242     }
    243 }
    244