/* * Copyright (C) 2014 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package lombok.core; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Map; import java.util.WeakHashMap; /** * Augments a instance of a type with a boolean field. *

* If the type already declares a boolean field, that field is used. Otherwise the field will be augmented. * * @param the type to augment. */ public abstract class BooleanFieldAugment { /** * Augments a instance of a type with a boolean field. *

* If the type already declares a boolean instance field, that field might be used. Otherwise the field will be augmented. *

* This code assumes that for any combination of {@code type} and {@code name} this method is only called once. * Otherwise, whether state is shared is undefined. * * @param type to augment * @param name of the field * @throws NullPointerException if {@code type} or {@code name} is {@code null} */ public static BooleanFieldAugment augment(Class type, String name) { checkNotNull(type, "type"); checkNotNull(name, "name"); Field booleanField = getBooleanField(type, name); if (booleanField == null) { return new MapFieldAugment(); } return new ExistingFieldAugment(booleanField); } private BooleanFieldAugment() { // prevent external instantiation } private static Field getBooleanField(Class type, String name) { try { Field result = type.getDeclaredField(name); if (Modifier.isStatic(result.getModifiers()) || result.getType() != boolean.class) { return null; } result.setAccessible(true); return result; } catch (Throwable t) { return null; } } /** * Sets the field to {@code true}. * @returns the previous value * @throws NullPointerException if {@code object} is {@code null} */ public abstract boolean set(T object); /** * Sets the field to {@code false}. * @returns the previous value * @throws NullPointerException if {@code object} is {@code null} */ public abstract boolean clear(T object); /** * @eturn {code true} if the field is set, otherwise {@code false}. * @throws NullPointerException if {@code object} is {@code null} */ public abstract boolean get(T object); private static class MapFieldAugment extends BooleanFieldAugment { private static final Object MARKER = new Object(); private final Map values = new WeakHashMap(); public boolean set(T object) { checkNotNull(object, "object"); synchronized (values) { return values.put(object, MARKER) != null; } } public boolean clear(T object) { checkNotNull(object, "object"); synchronized (values) { return values.remove(object) != null; } } public boolean get(T object) { checkNotNull(object, "object"); synchronized (values) { return values.get(object) != null; } } } private static class ExistingFieldAugment extends BooleanFieldAugment { private final Object lock = new Object(); private final Field booleanField; private ExistingFieldAugment(Field booleanField) { this.booleanField = booleanField; } @Override public boolean set(T object) { checkNotNull(object, "object"); try { synchronized (lock) { boolean result = booleanField.getBoolean(object); booleanField.setBoolean(object, true); return result; } } catch (IllegalAccessException e) { throw sneakyThrow(e); } } @Override public boolean clear(T object) { checkNotNull(object, "object"); try { synchronized (lock) { boolean result = booleanField.getBoolean(object); booleanField.setBoolean(object, false); return result; } } catch (IllegalAccessException e) { throw sneakyThrow(e); } } @Override public boolean get(T object) { checkNotNull(object, "object"); try { synchronized (lock) { return booleanField.getBoolean(object); } } catch (IllegalAccessException e) { throw sneakyThrow(e); } } } private static T checkNotNull(T object, String name) { if (object == null) throw new NullPointerException(name); return object; } private static RuntimeException sneakyThrow(Throwable t) { if (t == null) throw new NullPointerException("t"); BooleanFieldAugment.sneakyThrow0(t); return null; } @SuppressWarnings("unchecked") private static void sneakyThrow0(Throwable t) throws T { throw (T)t; } }