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