1/*
2 *******************************************************************************
3 * Copyright (C) 2007, International Business Machines Corporation and         *
4 * others. All Rights Reserved.                                                *
5 *******************************************************************************
6 */
7package com.ibm.icu.dev.test.util;
8
9import com.ibm.icu.impl.Utility;
10
11/**
12 * @author srl
13 *
14 * analog of FieldsSet in C++
15 */
16public class FieldsSet {
17    public static final int NO_ENUM = -1;
18
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
41    public void clear() {
42        for (int i = 0; i < fFieldsCount; i++) {
43            clear(i);
44        }
45    }
46
47    public void clear(int field) {
48        fValues[field] = -1;
49        fIsSet[field] = false;
50    }
51
52    public void set(int field, int amount) {
53        fValues[field] = amount;
54        fIsSet[field] = true;
55    }
56
57    public boolean isSet(int field) {
58        return fIsSet[field];
59    }
60
61    public int get(int field) {
62        if (fIsSet[field]) {
63            return fValues[field];
64        } else {
65            return -1;
66        }
67    }
68
69    public boolean isSameType(FieldsSet other) {
70        return ((other.fEnum == fEnum) && (other.fFieldsCount == fFieldsCount));
71    }
72
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     */
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     */
114    public int parseFrom(String str) {
115        return parseFrom(str, null);
116    }
117
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     */
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     */
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     */
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     */
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
227    public String fieldName(int field) {
228        return (fEnum!=NO_ENUM)?DebugUtilities.enumString(fEnum, field):Integer.toString(field);
229    }
230
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