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