001: /*
002: Copyright (c) 2004, Dennis M. Sosnoski
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without modification,
006: are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright notice, this
009: list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: * Neither the name of JiBX nor the names of its contributors may be used
014: to endorse or promote products derived from this software without specific
015: prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028:
029: package org.jibx.runtime;
030:
031: import java.util.Arrays;
032: import java.util.Comparator;
033:
034: /**
035: * Named value set support class. This provides convenience methods to support
036: * working with a set of named <code>static final int</code> values, including
037: * translating them to and from <code>String</code> representations. It's
038: * intended for use with relatively small nonnegative int values.
039: *
040: * @author Dennis M. Sosnoski
041: */
042: public class EnumSet {
043: /** Maximum <code>int</code> value supported for enumerations. */
044: public static final int VALUE_LIMIT = 512;
045:
046: /** Actual item definitions (used for extensions). */
047: private final EnumItem[] m_items;
048:
049: /** Enumeration names in index number order. */
050: private final String[] m_indexedNames;
051:
052: /** Enumeration names in sort order. */
053: private final String[] m_orderedNames;
054:
055: /** Index values corresponding to sorted names. */
056: private final int[] m_orderedIndexes;
057:
058: /**
059: * Constructor from array of enumeration items. The supplied items can be in
060: * any order, and the numeric values do not need to be contiguous (but must
061: * be unique, nonnegative, and should be fairly small). Note that this
062: * constructor will reorder the items in the supplied array as a side
063: * effect.
064: *
065: * @param items array of enumeration items (will be reordered)
066: */
067: public EnumSet(EnumItem[] items) {
068: m_items = items;
069: if (items.length > 0) {
070:
071: // first sort items in ascending name order
072: Arrays.sort(items, new Comparator() {
073: public int compare(Object a, Object b) {
074: return ((EnumItem) a).m_name
075: .compareTo(((EnumItem) b).m_name);
076: }
077: });
078:
079: // populate arrays for name lookup
080: m_orderedNames = new String[items.length];
081: m_orderedIndexes = new int[items.length];
082: int high = -1;
083: for (int i = 0; i < items.length; i++) {
084: EnumItem item = items[i];
085: if (item.m_value < 0) {
086: throw new IllegalArgumentException(
087: "Negative item value " + item.m_value
088: + " not allowed");
089: } else if (item.m_value > high) {
090: high = item.m_value;
091: if (high >= VALUE_LIMIT) {
092: throw new IllegalArgumentException(
093: "Enumeration with value " + high
094: + " too large to be used.");
095: }
096: }
097: m_orderedNames[i] = item.m_name;
098: m_orderedIndexes[i] = item.m_value;
099: }
100:
101: // populate array for indexed lookup of names
102: m_indexedNames = new String[high + 1];
103: for (int i = 0; i < items.length; i++) {
104: EnumItem item = items[i];
105: if (m_indexedNames[item.m_value] == null) {
106: m_indexedNames[item.m_value] = item.m_name;
107: } else {
108: throw new IllegalArgumentException(
109: "Duplicate index value " + item.m_value);
110: }
111: }
112:
113: } else {
114: m_indexedNames = new String[0];
115: m_orderedNames = new String[0];
116: m_orderedIndexes = new int[0];
117: }
118: }
119:
120: /**
121: * Constructor from array of names. The value associated with each name is
122: * just the position index in the array added to the start value.
123: *
124: * @param start item value for first added name
125: * @param names array of names (no <code>null</code> entries allowed)
126: */
127: public EnumSet(int start, String[] names) {
128: this (buildItems(start, names));
129: }
130:
131: /**
132: * Constructor from existing enumeration with added names. The value
133: * associated with each name is just the position index in the array added
134: * to the start value.
135: *
136: * @param base base enumeration to be extended
137: * @param start item value for first added name
138: * @param names array of names (no <code>null</code> entries allowed)
139: */
140: public EnumSet(EnumSet base, int start, String[] names) {
141: this (mergeItems(base, start, names));
142: }
143:
144: /**
145: * Generate array of enumeration items from array of names. The value
146: * associated with each name is just the position index in the array added
147: * to the start value.
148: *
149: * @param start item value for first added name
150: * @param names array of names (no <code>null</code> entries allowed)
151: */
152: private static EnumItem[] buildItems(int start, String[] names) {
153: EnumItem[] items = new EnumItem[names.length];
154: for (int i = 0; i < items.length; i++) {
155: items[i] = new EnumItem(start + i, names[i]);
156: }
157: return items;
158: }
159:
160: /**
161: * Generate array of enumeration items from base enumeration and array of
162: * names. The value associated with each name is just the position index in
163: * the array added to the start value.
164: *
165: * @param base base enumeration to be extended
166: * @param start item value for first added name
167: * @param names array of names (no <code>null</code> entries allowed)
168: */
169: private static EnumItem[] mergeItems(EnumSet base, int start,
170: String[] names) {
171: int prior = base.m_items.length;
172: EnumItem[] merges = new EnumItem[prior + names.length];
173: System.arraycopy(base.m_items, 0, merges, 0, prior);
174: for (int i = 0; i < names.length; i++) {
175: merges[prior + i] = new EnumItem(start + i, names[i]);
176: }
177: return merges;
178: }
179:
180: /**
181: * Get name for value if defined.
182: *
183: * @param value enumeration value
184: * @return name for value, or <code>null</code> if not defined
185: */
186: public String getName(int value) {
187: if (value >= 0 && value < m_indexedNames.length) {
188: return m_indexedNames[value];
189: } else {
190: return null;
191: }
192: }
193:
194: /**
195: * Get name for value. If the supplied value is not defined in the
196: * enumeration this throws an exception.
197: *
198: * @param value enumeration value
199: * @return name for value
200: */
201: public String getNameChecked(int value) {
202: if (value >= 0 && value < m_indexedNames.length) {
203: String name = m_indexedNames[value];
204: if (name != null) {
205: return name;
206: }
207: }
208: throw new IllegalArgumentException("Value " + value
209: + " not defined");
210: }
211:
212: /**
213: * Get value for name if defined.
214: *
215: * @param name possible enumeration name
216: * @return value for name, or <code>-1</code> if not found in enumeration
217: */
218: public int getValue(String name) {
219: int base = 0;
220: int limit = m_orderedNames.length - 1;
221: while (base <= limit) {
222: int cur = (base + limit) >> 1;
223: int diff = name.compareTo(m_orderedNames[cur]);
224: if (diff < 0) {
225: limit = cur - 1;
226: } else if (diff > 0) {
227: base = cur + 1;
228: } else if (m_orderedIndexes != null) {
229: return m_orderedIndexes[cur];
230: } else {
231: return cur;
232: }
233: }
234: return -1;
235: }
236:
237: /**
238: * Get value for name. If the supplied name is not present in the
239: * enumeration this throws an exception.
240: *
241: * @param name enumeration name
242: * @return value for name
243: */
244: public int getValueChecked(String name) {
245: int index = getValue(name);
246: if (index >= 0) {
247: return index;
248: } else {
249: throw new IllegalArgumentException("Name " + name
250: + " not defined");
251: }
252: }
253:
254: /**
255: * Check value with exception. Throws an exception if the supplied value is
256: * not defined by this enumeration.
257: *
258: * @param value
259: */
260: public void checkValue(int value) {
261: if (value < 0 || value >= m_indexedNames.length
262: || m_indexedNames[value] == null) {
263: throw new IllegalArgumentException("Value " + value
264: + " not defined");
265: }
266: }
267:
268: /**
269: * Get maximum index value in enumeration set.
270: *
271: * @return maximum
272: */
273: public int maxIndex() {
274: return m_indexedNames.length - 1;
275: }
276:
277: /**
278: * Enumeration pair information. This gives an <code>int</code> value along
279: * with the associated <code>String</code> representation.
280: */
281: public static class EnumItem {
282: public final int m_value;
283: public final String m_name;
284:
285: public EnumItem(int value, String name) {
286: m_value = value;
287: m_name = name;
288: }
289: }
290: }
|