001 /*
002 * To change this template, choose Tools | Templates
003 * and open the template in the editor.
004 */
005
006 package org.bridj;
007
008 import java.lang.reflect.Method;
009 import java.util.ArrayList;
010 import java.util.Iterator;
011 import java.util.List;
012 import java.util.Map;
013 import java.util.NoSuchElementException;
014 import java.util.WeakHashMap;
015
016 /**
017 * Set of int-valued enum values that is itself int-valued (bitwise OR of all the values).<br>
018 * This helps use Java enums (that implement {@link ValuedEnum}) as combinable C flags (see {@link FlagSet#fromValues(Enum[]) fromValues(E...) }).
019 * @author ochafik
020 */
021 public class FlagSet<E extends Enum<E>> implements ValuedEnum<E> {
022 private final long value;
023 private final Class<E> enumClass;
024 private E[] enumClassValues;
025
026 protected FlagSet(long value, Class<E> enumClass, E[] enumClassValues) {
027 this.enumClass = enumClass;
028 this.value = value;
029 this.enumClassValues = enumClassValues;
030 }
031
032 private static Map<Class<?>, Object[]> enumsCache = new WeakHashMap<Class<?>, Object[]>();
033
034 @SuppressWarnings("unchecked")
035 private static synchronized <EE extends Enum<EE>> EE[] getValues(Class<EE> enumClass) {
036 EE[] values = (EE[])enumsCache.get(enumClass);
037 if (values == null) {
038 try {
039 Method valuesMethod = enumClass.getMethod("values");
040 Class<?> valuesType = valuesMethod.getReturnType();
041 if (!valuesType.isArray() || !ValuedEnum.class.isAssignableFrom(valuesType.getComponentType()))
042 throw new RuntimeException();
043 enumsCache.put(enumClass, values = (EE[])valuesMethod.invoke(null));
044 } catch (Exception ex) {
045 throw new IllegalArgumentException("Class " + enumClass + " does not have a public static " + ValuedEnum.class.getName() + "[] values() method.", ex);
046 }
047 }
048 return (EE[])values;
049 }
050
051 @Override
052 public boolean equals(Object o) {
053 if (!(o instanceof ValuedEnum))
054 return false;
055 return value() == ((ValuedEnum)o).value();
056 }
057
058 @Override
059 public int hashCode() {
060 return ((Long)value()).hashCode();
061 }
062
063 //@Override
064 public Iterator<E> iterator() {
065 return getMatchingEnums().iterator();
066 }
067 public E toEnum() {
068 E nullMatch = null;
069 E match = null;
070 for (E e : getMatchingEnums()) {
071 if (((ValuedEnum)e).value() == 0)
072 nullMatch = e;
073 else if (match == null)
074 match = e;
075 else
076 throw new NoSuchElementException("More than one enum value corresponding to " + this + " : " + e + " and " + match + "...");
077 }
078 if (match != null)
079 return match;
080
081 if (value() == 0)
082 return nullMatch;
083
084 throw new NoSuchElementException("No enum value corresponding to " + this);
085 }
086
087 @Override
088 public String toString() {
089 StringBuilder b = new StringBuilder();
090 b.append(enumClass.getSimpleName()).append("(").append(value()).append(" = ");
091 try {
092 boolean first = true;
093 for (E e : this.getMatchingEnums()) {
094 if (first)
095 first = false;
096 else
097 b.append(" | ");
098 b.append(e);
099 }
100 } catch (Throwable th) {
101 b.append("?");
102 }
103 b.append(")");
104 return b.toString();
105 }
106
107 public static <EE extends Enum<EE>> FlagSet<EE> fromValue(long value, Class<EE> enumClass) {
108 return new FlagSet<EE>(value, enumClass, null);
109 }
110 public static class IntFlagSet<E extends Enum<E>> extends FlagSet<E> implements IntValuedEnum<E> {
111 protected IntFlagSet(long value, Class<E> enumClass, E[] enumClassValues) {
112 super(value, enumClass, enumClassValues);
113 }
114 }
115 public static <EE extends Enum<EE>> IntFlagSet<EE> fromValue(int value, Class<EE> enumClass) {
116 return new IntFlagSet<EE>(value, enumClass, null);
117 }
118 public static <EE extends Enum<EE>> FlagSet<EE> fromValue(ValuedEnum<EE> value) {
119 if (value instanceof Enum)
120 return FlagSet.fromValue(value.value(), (EE)value);
121 else
122 return (FlagSet<EE>)value;
123 }
124 public static <EE extends Enum<EE>> FlagSet<EE> fromValue(long value, EE... enumValue) {
125 return new FlagSet<EE>(value, null, enumValue);
126 }
127 public static <EE extends Enum<EE>> IntFlagSet<EE> fromValue(int value, EE... enumValue) {
128 return new IntFlagSet<EE>(value, null, enumValue);
129 }
130 /**
131 * Isolate bits that are set in the value.<br>
132 * For instance, {@code getBits(0xf)} yields {@literal 0x1, 0x2, 0x4, 0x8}
133 * @param value
134 * @return split bits, which give the value back if OR-ed all together.
135 */
136 public static List<Long> getBits(final long value) {
137 List<Long> list = new ArrayList<Long>();
138 for (int i = 0; i < 64; i++) {
139 long bit = 1L << i;
140 if ((value & bit) != 0)
141 list.add(bit);
142 }
143 return list;
144 }
145
146 /**
147 * Get the integral value of this FlagSet.
148 * @return value of the flag set
149 */
150 //@Override
151 public long value() {
152 return value;
153 }
154
155 public Class<E> getEnumClass() {
156 return enumClass;
157 }
158
159 protected E[] getEnumClassValues() {
160 return enumClassValues == null ? enumClassValues = getValues(enumClass) : enumClassValues;
161 }
162
163 /**
164 * Tests if the flagset value is equal to the OR combination of all the given values combined with bitwise OR operations.<br>
165 * The following C code :
166 * <pre>{@code
167 * E v = ...; // E is an enum type
168 * if (v == (E_V1 | E_V2)) { ... }
169 * }</pre>
170 * Can be translated to the following Java + BridJ code :
171 * <pre>{@code
172 * FlagSet<E> v = ...;
173 * if (v.is(E_V1, E_V2)) { ... }
174 * }</pre>
175 */
176 public boolean is(E... valuesToBeCombinedWithOR) {
177 return value() == orValue(valuesToBeCombinedWithOR);
178 }
179
180 /**
181 * Tests if the flagset value is contains the OR combination of all the given values combined with bitwise OR operations.<br>
182 * The following C code :
183 * <pre>{@code
184 * E v = ...; // E is an enum type
185 * if (v & (E_V1 | E_V2)) { ... }
186 * }</pre>
187 * Can be translated to the following Java + BridJ code :
188 * <pre>{@code
189 * FlagSet<E> v = ...;
190 * if (v.has(E_V1, E_V2)) { ... }
191 * }</pre>
192 */
193 public boolean has(E... valuesToBeCombinedWithOR) {
194 return (value() & orValue(valuesToBeCombinedWithOR)) != 0;
195 }
196
197 public FlagSet<E> or(E... valuesToBeCombinedWithOR) {
198 return new FlagSet(value() | orValue(valuesToBeCombinedWithOR), enumClass, null);
199 }
200
201 static <E extends Enum<E>> long orValue(E... valuesToBeCombinedWithOR) {
202 long value = 0;
203 for (E v : valuesToBeCombinedWithOR)
204 value |= ((ValuedEnum)v).value();
205 return value;
206 }
207 public FlagSet<E> without(E... valuesToBeCombinedWithOR) {
208 return new FlagSet(value() & ~orValue(valuesToBeCombinedWithOR), enumClass, null);
209 }
210 public FlagSet<E> and(E... valuesToBeCombinedWithOR) {
211 return new FlagSet(value() & orValue(valuesToBeCombinedWithOR), enumClass, null);
212 }
213
214 protected List<E> getMatchingEnums() {
215 List<E> ret = new ArrayList<E>();
216 if (enumClass != null) {
217 for (E e : getEnumClassValues()) {
218 long eMask = ((ValuedEnum<?>)e).value();
219 if ((value & eMask) == eMask)
220 ret.add((E)e);
221 }
222 }
223
224 return ret;
225 }
226
227 public static <E extends Enum<E>> FlagSet<E> fromValues(E... enumValues) {
228 long value = 0;
229 Class cl = null;
230 for (E enumValue : enumValues) {
231 if (enumValue == null)
232 continue;
233 if (cl == null)
234 cl = enumValue.getClass();
235 value |= ((ValuedEnum)enumValue).value();
236 }
237 return new FlagSet<E>(value, cl, enumValues);
238 }
239
240 }