/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.security.authorization; import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.NoSuchElementException; /** * Optimized set of {@link Right}. * * @version $Id: 54d4e62cc0742a69f09dac73c1859f31d7572a43 $ * @since 4.0M2 */ public class RightSet extends AbstractSet<Right> implements Cloneable, java.io.Serializable { /** Serialization identifier. */ private static final long serialVersionUID = 1L; /** Bit vector representation to store the set. */ private long rights; /** Default constructor. */ public RightSet() { if (Right.size() > 64) { throw new IllegalStateException(); } } /** * Create a new initialized set. * * @param rights a collection of {@code Right} object to initialize the set */ public RightSet(Collection<? extends Right> rights) { if (Right.size() > 64) { throw new IllegalStateException(); } this.addAll(rights); } /** * Create a new initialized set. * @param rights the rights you want in the set */ public RightSet(Right... rights) { if (Right.size() > 64) { throw new IllegalStateException(); } Collections.addAll(this, rights); } @Override public Iterator<Right> iterator() { return new RightIterator(); } /** * Private iterator for this set. */ private class RightIterator implements Iterator<Right> { /** Current index in the set, using a bit mask of remaining rights. */ private long index; /** Last element returned, using a single bit mask of current element. */ private long lastIndex; /** Default constructor. */ RightIterator() { index = rights; } @Override public boolean hasNext() { return index != 0; } @Override public Right next() { if (index == 0) { throw new NoSuchElementException(); } lastIndex = index & -index; index -= lastIndex; return Right.get(Long.numberOfTrailingZeros(lastIndex)); } @Override public void remove() { if (lastIndex == 0) { throw new IllegalStateException(); } rights -= lastIndex; lastIndex = 0; } } @Override public boolean equals(Object o) { if (!(o instanceof RightSet)) { return super.equals(o); } return ((RightSet) o).rights == rights; } @Override public int hashCode() { return Long.valueOf(rights).hashCode(); } @Override public boolean removeAll(Collection<?> objects) { if (!(objects instanceof RightSet)) { return super.removeAll(objects); } long old = rights; rights &= ~((RightSet) objects).rights; return rights != old; } @Override public boolean add(Right right) { long old = rights; rights |= (1L << right.ordinal()); return rights != old; } @Override public boolean addAll(Collection<? extends Right> rights) { if (!(rights instanceof RightSet)) { return super.addAll(rights); } long old = this.rights; this.rights |= ((RightSet) rights).rights; return this.rights != old; } @Override public void clear() { rights = 0; } @Override public boolean contains(Object o) { return o != null && o instanceof Right && (rights & (1L << ((Right) o).ordinal())) != 0; } @Override public boolean containsAll(Collection<?> objects) { if (!(objects instanceof RightSet)) { return super.containsAll(objects); } return (((RightSet) objects).rights & ~rights) == 0; } @Override public boolean remove(Object o) { if (o == null || !(o instanceof Right)) { return false; } long old = rights; rights &= ~(1L << ((Right) o).ordinal()); return rights != old; } @Override public boolean isEmpty() { return rights == 0; } @Override public boolean retainAll(Collection<?> objects) { if (!(objects instanceof RightSet)) { return super.retainAll(objects); } long old = rights; rights &= ((RightSet) objects).rights; return rights != old; } @Override public int size() { // return Long.bitCount(rights); // // Would be easier and probably faster, but some versions of the Oracle/Sun implementation may have an issue // with Long.bitCount(), see: // [Java 6] Wrong results from basic comparisons after calls to Long.bitCount(long) (pmd : XPathRule_1339015068) // See Bug ID : 7063674 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7063674 // // So we have reimplemented it based on public domain code snippets published in Bit Twiddling Hacks // by Sean Eron Anderson (see http://www-graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallelw) long v = rights - ((rights >>> 1) & 0x5555555555555555L); v = (v & 0x3333333333333333L) + ((v >>> 2) & 0x3333333333333333L); return (int) (((v + (v >> 4) & 0x0F0F0F0F0F0F0F0FL) * 0x0101010101010101L) >>> 56); } @Override public Object[] toArray() { return fillArray(new Object[size()]); } @Override @SuppressWarnings("unchecked") public <T> T[] toArray(T[] ts) { T[] a = ts; int size = size(); if (a.length < size) { a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size); } if (a.length > size) { a[size] = null; } return (T[]) fillArray(a); } /** * Fill array ts that should have the appropriate size with the {@code Right} in this set. * @param ts an array properly sized to receive this set * @return an array representing this set */ private Object[] fillArray(Object[] ts) { int j = 0; for (int i = 0; i < Right.size(); i++) { if ((rights & (1 << i)) > 0) { ts[j++] = Right.get(i); } } return ts; } @Override public String toString() { StringBuilder sb = new StringBuilder("["); boolean first = true; for (int i = 0; i < Right.size(); i++) { if ((rights & (1 << i)) > 0) { if (first) { first = false; } else { sb.append(", "); } sb.append(Right.get(i).getName()); } } sb.append("]"); return sb.toString(); } @Override public RightSet clone() throws CloneNotSupportedException { RightSet clone = (RightSet) super.clone(); clone.rights = rights; return clone; } }