001/* 002 * BridJ - Dynamic and blazing-fast native interop for Java. 003 * http://bridj.googlecode.com/ 004 * 005 * Copyright (c) 2010-2013, Olivier Chafik (http://ochafik.com/) 006 * All rights reserved. 007 * 008 * Redistribution and use in source and binary forms, with or without 009 * modification, are permitted provided that the following conditions are met: 010 * 011 * * Redistributions of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * * Redistributions in binary form must reproduce the above copyright 014 * notice, this list of conditions and the following disclaimer in the 015 * documentation and/or other materials provided with the distribution. 016 * * Neither the name of Olivier Chafik nor the 017 * names of its contributors may be used to endorse or promote products 018 * derived from this software without specific prior written permission. 019 * 020 * THIS SOFTWARE IS PROVIDED BY OLIVIER CHAFIK AND CONTRIBUTORS ``AS IS'' AND ANY 021 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 022 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 023 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 024 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 025 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 026 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 027 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 028 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 029 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 030 */ 031package org.bridj; 032 033import java.lang.reflect.Array; 034import java.lang.reflect.Method; 035import java.util.ArrayList; 036import java.util.Collections; 037import java.util.Iterator; 038import java.util.List; 039import java.util.Map; 040import java.util.NoSuchElementException; 041import java.util.WeakHashMap; 042 043/** 044 * Set of int-valued enum values that is itself int-valued (bitwise OR of all 045 * the values).<br> 046 * This helps use Java enums (that implement {@link ValuedEnum}) as combinable C 047 * flags (see {@link FlagSet#fromValues(Enum[]) fromValues(E...) }). 048 * 049 * @author ochafik 050 */ 051public class FlagSet<E extends Enum<E>> implements ValuedEnum<E> { 052 053 private final long value; 054 private final Class<E> enumClass; 055 private E[] enumClassValues; 056 057 protected FlagSet(long value, Class<E> enumClass, E[] enumClassValues) { 058 this.enumClass = enumClass; 059 this.value = value; 060 this.enumClassValues = enumClassValues; 061 } 062 private static Map<Class<?>, Object[]> enumsCache = new WeakHashMap<Class<?>, Object[]>(); 063 064 @SuppressWarnings("unchecked") 065 private static synchronized <EE extends Enum<EE>> EE[] getValues(Class<EE> enumClass) { 066 EE[] values = (EE[]) enumsCache.get(enumClass); 067 if (values == null) { 068 try { 069 Method valuesMethod = enumClass.getMethod("values"); 070 Class<?> valuesType = valuesMethod.getReturnType(); 071 if (!valuesType.isArray() || !ValuedEnum.class.isAssignableFrom(valuesType.getComponentType())) { 072 throw new RuntimeException(); 073 } 074 enumsCache.put(enumClass, values = (EE[]) valuesMethod.invoke(null)); 075 } catch (Exception ex) { 076 throw new IllegalArgumentException("Class " + enumClass + " does not have a public static " + ValuedEnum.class.getName() + "[] values() method.", ex); 077 } 078 } 079 return (EE[]) values; 080 } 081 082 @Override 083 public boolean equals(Object o) { 084 if (!(o instanceof ValuedEnum)) { 085 return false; 086 } 087 return value() == ((ValuedEnum) o).value(); 088 } 089 090 @Override 091 public int hashCode() { 092 return ((Long) value()).hashCode(); 093 } 094 095 //@Override 096 public Iterator<E> iterator() { 097 return getMatchingEnums().iterator(); 098 } 099 100 public E toEnum() { 101 E nullMatch = null; 102 E match = null; 103 for (E e : getMatchingEnums()) { 104 if (((ValuedEnum) e).value() == 0) { 105 nullMatch = e; 106 } else if (match == null) { 107 match = e; 108 } else { 109 throw new NoSuchElementException("More than one enum value corresponding to " + this + " : " + e + " and " + match + "..."); 110 } 111 } 112 if (match != null) { 113 return match; 114 } 115 116 if (value() == 0) { 117 return nullMatch; 118 } 119 120 throw new NoSuchElementException("No enum value corresponding to " + this); 121 } 122 123 @Override 124 public String toString() { 125 StringBuilder b = new StringBuilder(); 126 b.append(enumClass.getSimpleName()).append("(").append(value()).append(" = "); 127 try { 128 boolean first = true; 129 for (E e : this.getMatchingEnums()) { 130 if (first) { 131 first = false; 132 } else { 133 b.append(" | "); 134 } 135 b.append(e); 136 } 137 } catch (Throwable th) { 138 b.append("?"); 139 } 140 b.append(")"); 141 return b.toString(); 142 } 143 144 public static <EE extends Enum<EE>> FlagSet<EE> createFlagSet(long value, Class<EE> enumClass) { 145 return new FlagSet<EE>(value, enumClass, null); 146 } 147 148 public static class IntFlagSet<E extends Enum<E>> extends FlagSet<E> implements IntValuedEnum<E> { 149 150 protected IntFlagSet(long value, Class<E> enumClass, E[] enumClassValues) { 151 super(value, enumClass, enumClassValues); 152 } 153 } 154 155 public static <EE extends Enum<EE>> IntFlagSet<EE> createFlagSet(int value, Class<EE> enumClass) { 156 return new IntFlagSet<EE>(value, enumClass, null); 157 } 158 159 public static <EE extends Enum<EE>> FlagSet<EE> fromValue(ValuedEnum<EE> value) { 160 if (value instanceof Enum) { 161 return FlagSet.createFlagSet(value.value(), (EE) value); 162 } else { 163 return (FlagSet<EE>) value; 164 } 165 } 166 167 public static <EE extends Enum<EE>> FlagSet<EE> createFlagSet(long value, EE... enumValue) { 168 if (enumValue == null) { 169 throw new IllegalArgumentException("Expected at least one enum value"); 170 } 171 Class<EE> enumClass = (Class) enumValue[0].getClass(); 172 if (IntValuedEnum.class.isAssignableFrom(enumClass)) { 173 return new IntFlagSet<EE>(value, enumClass, enumValue); 174 } else { 175 return new FlagSet<EE>(value, enumClass, enumValue); 176 } 177 } 178// public static <EE extends Enum<EE>> IntFlagSet<EE> createFlagSet(int value, EE... enumValue) { 179// return (IntFlagSet<EE>)createFlagSet((long)value, enumValue); 180// } 181 182 public static <EE extends Enum<EE>> IntValuedEnum<EE> fromValue(int value, Class<EE> enumClass) { 183 return (IntValuedEnum<EE>) fromValue((long) value, enumClass, enumClass.getEnumConstants()); 184 } 185 186 public static <EE extends Enum<EE>> IntValuedEnum<EE> fromValue(int value, EE... enumValues) { 187 return (IntValuedEnum<EE>) fromValue((long) value, enumValues); 188 } 189 190 public static <EE extends Enum<EE>> ValuedEnum<EE> fromValue(long value, EE... enumValues) { 191 if (enumValues == null || enumValues.length == 0) { 192 throw new IllegalArgumentException("Expected at least one enum value"); 193 } 194 Class<EE> enumClass = (Class) enumValues[0].getClass(); 195 return fromValue(value, enumClass, enumValues); 196 } 197 198 protected static <EE extends Enum<EE>> ValuedEnum<EE> fromValue(long value, Class<EE> enumClass, EE... enumValue) { 199 List<EE> enums = getMatchingEnums(value, enumClass.getEnumConstants()); 200 if (enums.size() == 1) { 201 return (ValuedEnum<EE>) enums.get(0); 202 } 203 if (IntValuedEnum.class.isAssignableFrom(enumClass)) { 204 return new IntFlagSet<EE>(value, enumClass, enums.toArray((EE[]) Array.newInstance(enumClass, enums.size()))); 205 } else { 206 return new FlagSet<EE>(value, enumClass, enums.toArray((EE[]) Array.newInstance(enumClass, enums.size()))); 207 } 208 } 209 210 /** 211 * Isolate bits that are set in the value.<br> 212 * For instance, {@code getBits(0xf)} yields {@literal 0x1, 0x2, 0x4, 0x8} 213 * 214 * @param value 215 * @return split bits, which give the value back if OR-ed all together. 216 */ 217 public static List<Long> getBits(final long value) { 218 List<Long> list = new ArrayList<Long>(); 219 for (int i = 0; i < 64; i++) { 220 long bit = 1L << i; 221 if ((value & bit) != 0) { 222 list.add(bit); 223 } 224 } 225 return list; 226 } 227 228 /** 229 * Get the integral value of this FlagSet. 230 * 231 * @return value of the flag set 232 */ 233 //@Override 234 public long value() { 235 return value; 236 } 237 238 public Class<E> getEnumClass() { 239 return enumClass; 240 } 241 242 protected E[] getEnumClassValues() { 243 return enumClassValues == null ? enumClassValues = getValues(enumClass) : enumClassValues; 244 } 245 246 /** 247 * Tests if the flagset value is equal to the OR combination of all the 248 * given values combined with bitwise OR operations.<br> 249 * The following C code : 250 * <pre>{@code 251 * E v = ...; // E is an enum type 252 * if (v == (E_V1 | E_V2)) { ... } 253 * }</pre> Can be translated to the following Java + BridJ code : 254 * <pre>{@code 255 * FlagSet<E> v = ...; 256 * if (v.is(E_V1, E_V2)) { ... } 257 * }</pre> 258 */ 259 public boolean is(E... valuesToBeCombinedWithOR) { 260 return value() == orValue(valuesToBeCombinedWithOR); 261 } 262 263 /** 264 * Tests if the flagset value is contains the OR combination of all the 265 * given values combined with bitwise OR operations.<br> 266 * The following C code : 267 * <pre>{@code 268 * E v = ...; // E is an enum type 269 * if (v & (E_V1 | E_V2)) { ... } 270 * }</pre> Can be translated to the following Java + BridJ code : 271 * <pre>{@code 272 * FlagSet<E> v = ...; 273 * if (v.has(E_V1, E_V2)) { ... } 274 * }</pre> 275 */ 276 public boolean has(E... valuesToBeCombinedWithOR) { 277 return (value() & orValue(valuesToBeCombinedWithOR)) != 0; 278 } 279 280 public FlagSet<E> or(E... valuesToBeCombinedWithOR) { 281 return new FlagSet(value() | orValue(valuesToBeCombinedWithOR), enumClass, null); 282 } 283 284 static <E extends Enum<E>> long orValue(E... valuesToBeCombinedWithOR) { 285 long value = 0; 286 for (E v : valuesToBeCombinedWithOR) { 287 value |= ((ValuedEnum) v).value(); 288 } 289 return value; 290 } 291 292 public FlagSet<E> without(E... valuesToBeCombinedWithOR) { 293 return new FlagSet(value() & ~orValue(valuesToBeCombinedWithOR), enumClass, null); 294 } 295 296 public FlagSet<E> and(E... valuesToBeCombinedWithOR) { 297 return new FlagSet(value() & orValue(valuesToBeCombinedWithOR), enumClass, null); 298 } 299 300 protected List<E> getMatchingEnums() { 301 return enumClass == null ? Collections.EMPTY_LIST : getMatchingEnums(value, getEnumClassValues()); 302 } 303 304 protected static <EE extends Enum<EE>> List<EE> getMatchingEnums(long value, EE[] enums) { 305 List<EE> ret = new ArrayList<EE>(); 306 for (EE e : enums) { 307 long eMask = ((ValuedEnum<?>) e).value(); 308 if ((eMask == 0 && value == 0) || (eMask != 0 && (value & eMask) == eMask)) { 309 ret.add((EE) e); 310 } 311 } 312 return ret; 313 } 314 315 public static <E extends Enum<E>> FlagSet<E> fromValues(E... enumValues) { 316 long value = 0; 317 Class cl = null; 318 for (E enumValue : enumValues) { 319 if (enumValue == null) { 320 continue; 321 } 322 if (cl == null) { 323 cl = enumValue.getClass(); 324 } 325 value |= ((ValuedEnum) enumValue).value(); 326 } 327 return new FlagSet<E>(value, cl, enumValues); 328 } 329}