| // © 2016 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| /* |
| ******************************************************************************* |
| * Copyright (C) 2007, International Business Machines Corporation and * |
| * others. All Rights Reserved. * |
| ******************************************************************************* |
| */ |
| package com.ibm.icu.dev.test.util; |
| |
| import com.ibm.icu.impl.Utility; |
| |
| /** |
| * @author srl |
| * |
| * analog of FieldsSet in C++ |
| */ |
| public class FieldsSet { |
| public static final int NO_ENUM = -1; |
| |
| protected FieldsSet(int whichEnum, int fieldsCount) { |
| if (fieldsCount <= 0 && whichEnum != NO_ENUM) { |
| fieldsCount = DebugUtilities.enumCount(whichEnum); |
| } |
| fEnum = whichEnum; |
| fFieldsCount = fieldsCount; |
| if(fieldsCount<0) { |
| throw new InternalError("Preposterous field count " + fieldsCount); |
| } |
| fValues = new int[fFieldsCount]; |
| fIsSet = new boolean[fFieldsCount]; |
| clear(); |
| } |
| |
| protected int fEnum = NO_ENUM; |
| |
| protected int fFieldsCount = 0; |
| |
| protected int fValues[] = null; |
| |
| protected boolean fIsSet[] = null; |
| |
| public void clear() { |
| for (int i = 0; i < fFieldsCount; i++) { |
| clear(i); |
| } |
| } |
| |
| public void clear(int field) { |
| fValues[field] = -1; |
| fIsSet[field] = false; |
| } |
| |
| public void set(int field, int amount) { |
| fValues[field] = amount; |
| fIsSet[field] = true; |
| } |
| |
| public boolean isSet(int field) { |
| return fIsSet[field]; |
| } |
| |
| public int get(int field) { |
| if (fIsSet[field]) { |
| return fValues[field]; |
| } else { |
| return -1; |
| } |
| } |
| |
| public boolean isSameType(FieldsSet other) { |
| return ((other.fEnum == fEnum) && (other.fFieldsCount == fFieldsCount)); |
| } |
| |
| public int fieldCount() { |
| return fFieldsCount; |
| } |
| |
| /** |
| * @param other "expected" set to match against |
| * @return a formatted string listing which fields are set in this, with the |
| * comparison made agaainst those fields in other, or, 'null' if there is no difference. |
| */ |
| public String diffFrom(FieldsSet other) { |
| StringBuffer str = new StringBuffer(); |
| if(!isSameType(other)) { |
| throw new IllegalArgumentException("U_ILLEGAL_ARGUMENT_ERROR: FieldsSet of a different type!"); |
| } |
| for (int i=0; i<fieldCount(); i++) { |
| if (isSet(i)) { |
| int myVal = get(i); |
| int theirVal = other.get(i); |
| |
| if(fEnum != NO_ENUM) { |
| String fieldName = DebugUtilities.enumString(fEnum, i); |
| |
| String aval = Integer.toString(myVal); |
| String bval = Integer.toString(theirVal); |
| |
| str.append(fieldName +"="+aval+" not "+bval+", "); |
| } else { |
| str.append(Integer.toString(i) + "=" + myVal+" not " + theirVal+", "); |
| } |
| } |
| } |
| if(str.length()==0) { |
| return null; |
| } |
| return str.toString(); |
| } |
| |
| /** |
| * @param str string to parse |
| * @param status formatted string for status |
| */ |
| public int parseFrom(String str) { |
| return parseFrom(str, null); |
| } |
| |
| public int parseFrom(String str, FieldsSet inheritFrom) { |
| int goodFields = 0; |
| |
| String[] fields = Utility.split(str, ','); |
| for(int i=0;i<fields.length;i++) { |
| String fieldStr = fields[i]; |
| String kv[] = Utility.split(fieldStr, '='); |
| if(kv.length < 1 || kv.length > 2) { |
| throw new InternalError("split around '=' failed: " + fieldStr); |
| } |
| String key = kv[0]; |
| String value = ""; |
| if(kv.length>1) { |
| value = kv[1]; |
| } |
| |
| int field = handleParseName(inheritFrom, key, value); |
| if(field != -1) { |
| handleParseValue(inheritFrom, field, value); |
| goodFields++; |
| } |
| } |
| |
| return goodFields; |
| } |
| |
| /** |
| * Callback interface for subclass. This function is called when parsing a |
| * field name, such as "MONTH" in "MONTH=4". Base implementation is to |
| * lookup the enum value using udbg_* utilities, or else as an integer if |
| * enum is not available. |
| * |
| * If there is a special directive, the implementer can catch it here and |
| * return -1 after special processing completes. |
| * |
| * @param inheritFrom the set inheriting from - may be null. |
| * @param name the field name (key side) |
| * @param substr the string in question (value side) |
| * @param status error status - set to error for failure. |
| * @return field number, or negative if field should be skipped. |
| */ |
| protected int handleParseName(FieldsSet inheritFrom, String name, |
| String substr) { |
| int field = -1; |
| if(fEnum != NO_ENUM) { |
| field = DebugUtilities.enumByString(fEnum, name); |
| } |
| if(field < 0) { |
| field = Integer.parseInt(name); |
| } |
| return field; |
| } |
| |
| /** |
| * Callback interface for subclass. Base implementation is to call |
| * parseValueDefault(...) |
| * |
| * @param inheritFrom the set inheriting from - may be null. |
| * @param field which field is being parsed |
| * @param substr the string in question (value side) |
| * @param status error status - set to error for failure. |
| * @see parseValueDefault |
| */ |
| protected void handleParseValue(FieldsSet inheritFrom, int field, |
| String substr) { |
| parseValueDefault(inheritFrom, field, substr); |
| } |
| |
| /** |
| * the default implementation for handleParseValue. Base implementation is |
| * to parse a decimal integer value, or inherit from inheritFrom if the |
| * string is 0-length. Implementations of this function should call |
| * set(field,...) on successful parse. |
| * |
| * @see handleParseValue |
| */ |
| protected void parseValueDefault(FieldsSet inheritFrom, int field, |
| String substr) { |
| if(substr.length()==0) { |
| if(inheritFrom == null) { |
| throw new InternalError("Trying to inherit from field " + field + " but inheritFrom is null"); |
| } |
| if(!inheritFrom.isSet(field)) { |
| throw new InternalError("Trying to inherit from field " + field + " but inheritFrom["+field+"] is not set"); |
| } |
| set(field,inheritFrom.get(field)); |
| } else { |
| int value = Integer.parseInt(substr); |
| set(field, value); |
| } |
| } |
| |
| /** |
| * convenience implementation for handleParseValue attempt to load a value |
| * from an enum value using udbg_enumByString() if fails, will call |
| * parseValueDefault() |
| * |
| * @see handleParseValue |
| */ |
| protected void parseValueEnum(int type, FieldsSet inheritFrom, int field, |
| String substr) { |
| int value = DebugUtilities.enumByString(type, substr); |
| if(value>=0) { |
| set(field,value); |
| return; |
| } |
| parseValueDefault(inheritFrom, field, substr); |
| } |
| |
| public String fieldName(int field) { |
| return (fEnum!=NO_ENUM)?DebugUtilities.enumString(fEnum, field):Integer.toString(field); |
| } |
| |
| public String toString() { |
| String str = getClass().getName()+" ["+fFieldsCount+"," |
| +(fEnum!=NO_ENUM?DebugUtilities.typeString(fEnum):Integer.toString(fEnum))+"]: "; |
| for(int i=0;i<fFieldsCount;i++) { |
| if(isSet(i)) { |
| str = str + fieldName(i)+"="+get(i)+","; |
| } |
| } |
| return str; |
| } |
| } |