All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeBits Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.jackrabbit.oak.spi.security.privilege;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.jackrabbit.guava.common.primitives.Longs;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import static org.apache.jackrabbit.guava.common.base.Preconditions.checkArgument;

/**
 * Internal representation of JCR privileges.
 */
public final class PrivilegeBits implements PrivilegeConstants {

    private static final long NO_PRIVILEGE = 0;
    private static final long READ_NODES = 1;
    private static final long READ_PROPERTIES = READ_NODES << 1;
    private static final long ADD_PROPERTIES = READ_PROPERTIES << 1;
    private static final long ALTER_PROPERTIES = ADD_PROPERTIES << 1;
    private static final long REMOVE_PROPERTIES = ALTER_PROPERTIES << 1;
    private static final long ADD_CHILD_NODES = REMOVE_PROPERTIES << 1;
    private static final long REMOVE_CHILD_NODES = ADD_CHILD_NODES << 1;
    private static final long REMOVE_NODE = REMOVE_CHILD_NODES << 1;
    private static final long READ_AC = REMOVE_NODE << 1;
    private static final long MODIFY_AC = READ_AC << 1;
    private static final long NODE_TYPE_MNGMT = MODIFY_AC << 1;
    private static final long VERSION_MNGMT = NODE_TYPE_MNGMT << 1;
    private static final long LOCK_MNGMT = VERSION_MNGMT << 1;
    private static final long LIFECYCLE_MNGMT = LOCK_MNGMT << 1;
    private static final long RETENTION_MNGMT = LIFECYCLE_MNGMT << 1;
    private static final long WORKSPACE_MNGMT = RETENTION_MNGMT << 1;
    private static final long NODE_TYPE_DEF_MNGMT = WORKSPACE_MNGMT << 1;
    private static final long NAMESPACE_MNGMT = NODE_TYPE_DEF_MNGMT << 1;
    private static final long PRIVILEGE_MNGMT = NAMESPACE_MNGMT << 1;
    private static final long USER_MNGMT = PRIVILEGE_MNGMT << 1;
    private static final long INDEX_DEFINITION_MNGMT = USER_MNGMT << 1;

    private static final long READ = READ_NODES | READ_PROPERTIES;
    private static final long MODIFY_PROPERTIES = ADD_PROPERTIES | ALTER_PROPERTIES | REMOVE_PROPERTIES;
    private static final long WRITE = MODIFY_PROPERTIES | ADD_CHILD_NODES | REMOVE_CHILD_NODES | REMOVE_NODE;
    private static final long WRITE2 = WRITE | NODE_TYPE_MNGMT;

    public static final PrivilegeBits EMPTY = new PrivilegeBits(UnmodifiableData.EMPTY);

    public static final Map BUILT_IN;
    private static final Map BUILT_IN_BITS;

    static {
        Map privs = new HashMap<>();
        privs.put(REP_READ_NODES, getInstance(READ_NODES));
        privs.put(REP_READ_PROPERTIES, getInstance(READ_PROPERTIES));
        privs.put(REP_ADD_PROPERTIES, getInstance(ADD_PROPERTIES));
        privs.put(REP_ALTER_PROPERTIES, getInstance(ALTER_PROPERTIES));
        privs.put(REP_REMOVE_PROPERTIES, getInstance(REMOVE_PROPERTIES));
        privs.put(JCR_ADD_CHILD_NODES, getInstance(ADD_CHILD_NODES));
        privs.put(JCR_REMOVE_CHILD_NODES, getInstance(REMOVE_CHILD_NODES));
        privs.put(JCR_REMOVE_NODE, getInstance(REMOVE_NODE));
        privs.put(JCR_READ_ACCESS_CONTROL, getInstance(READ_AC));
        privs.put(JCR_MODIFY_ACCESS_CONTROL, getInstance(MODIFY_AC));
        privs.put(JCR_NODE_TYPE_MANAGEMENT, getInstance(NODE_TYPE_MNGMT));
        privs.put(JCR_VERSION_MANAGEMENT, getInstance(VERSION_MNGMT));
        privs.put(JCR_LOCK_MANAGEMENT, getInstance(LOCK_MNGMT));
        privs.put(JCR_LIFECYCLE_MANAGEMENT, getInstance(LIFECYCLE_MNGMT));
        privs.put(JCR_RETENTION_MANAGEMENT, getInstance(RETENTION_MNGMT));
        privs.put(JCR_WORKSPACE_MANAGEMENT, getInstance(WORKSPACE_MNGMT));
        privs.put(JCR_NODE_TYPE_DEFINITION_MANAGEMENT, getInstance(NODE_TYPE_DEF_MNGMT));
        privs.put(JCR_NAMESPACE_MANAGEMENT, getInstance(NAMESPACE_MNGMT));
        privs.put(REP_PRIVILEGE_MANAGEMENT, getInstance(PRIVILEGE_MNGMT));
        privs.put(REP_USER_MANAGEMENT, getInstance(USER_MNGMT));
        privs.put(REP_INDEX_DEFINITION_MANAGEMENT, getInstance(INDEX_DEFINITION_MNGMT));

        privs.put(JCR_READ, getInstance(READ));
        privs.put(JCR_MODIFY_PROPERTIES, getInstance(MODIFY_PROPERTIES));
        privs.put(JCR_WRITE, getInstance(WRITE));
        privs.put(REP_WRITE, getInstance(WRITE2));

        BUILT_IN = Collections.unmodifiableMap(privs);

        Map bits = new HashMap<>();
        bits.put(READ_NODES, REP_READ_NODES);
        bits.put(READ_PROPERTIES, REP_READ_PROPERTIES);
        bits.put(ADD_PROPERTIES, REP_ADD_PROPERTIES);
        bits.put(ALTER_PROPERTIES, REP_ALTER_PROPERTIES);
        bits.put(REMOVE_PROPERTIES, REP_REMOVE_PROPERTIES);
        bits.put(ADD_CHILD_NODES, JCR_ADD_CHILD_NODES);
        bits.put(REMOVE_CHILD_NODES, JCR_REMOVE_CHILD_NODES);
        bits.put(REMOVE_NODE, JCR_REMOVE_NODE);
        bits.put(READ_AC, JCR_READ_ACCESS_CONTROL);
        bits.put(MODIFY_AC, JCR_MODIFY_ACCESS_CONTROL);
        bits.put(NODE_TYPE_MNGMT, JCR_NODE_TYPE_MANAGEMENT);
        bits.put(VERSION_MNGMT, JCR_VERSION_MANAGEMENT);
        bits.put(LOCK_MNGMT, JCR_LOCK_MANAGEMENT);
        bits.put(LIFECYCLE_MNGMT, JCR_LIFECYCLE_MANAGEMENT);
        bits.put(RETENTION_MNGMT, JCR_RETENTION_MANAGEMENT);
        bits.put(WORKSPACE_MNGMT, JCR_WORKSPACE_MANAGEMENT);
        bits.put(NODE_TYPE_DEF_MNGMT, JCR_NODE_TYPE_DEFINITION_MANAGEMENT);
        bits.put(NAMESPACE_MNGMT, JCR_NAMESPACE_MANAGEMENT);
        bits.put(PRIVILEGE_MNGMT, REP_PRIVILEGE_MANAGEMENT);
        bits.put(USER_MNGMT, REP_USER_MANAGEMENT);
        bits.put(INDEX_DEFINITION_MNGMT, REP_INDEX_DEFINITION_MANAGEMENT);

        bits.put(READ, JCR_READ);
        bits.put(MODIFY_PROPERTIES, JCR_MODIFY_PROPERTIES);
        bits.put(WRITE, JCR_WRITE);
        bits.put(WRITE2, REP_WRITE);

        BUILT_IN_BITS = Collections.unmodifiableMap(bits);
    }

    public static final PrivilegeBits NEXT_AFTER_BUILT_INS = getInstance(INDEX_DEFINITION_MNGMT).nextBits();

    private final Data d;

    /**
     * Private constructor.
     *
     * @param d The data that backs this instance.
     */
    private PrivilegeBits(Data d) {
        this.d = d;
    }

    /**
     * Creates a mutable instance of privilege bits.
     *
     * @return a new instance of privilege bits.
     */
    public static PrivilegeBits getInstance() {
        return new PrivilegeBits(new ModifiableData());
    }

    /**
     * Creates a mutable instance of privilege bits.
     *
     * @param base The base for this mutable instance.
     * @return a new instance of privilege bits.
     */
    @NotNull
    public static PrivilegeBits getInstance(@NotNull PrivilegeBits... base) {
        PrivilegeBits bts = getInstance();
        for (PrivilegeBits baseBits : base) {
            bts.add(baseBits);
        }
        return bts;
    }

    /**
     * Get or create an instance of privilege bits for a specific property that
     * stores privileges.
     *
     * @param property The property state storing privilege bits information.
     * @return an instance of {@code PrivilegeBits}
     */
    @NotNull
    public static PrivilegeBits getInstance(@Nullable PropertyState property) {
        if (property == null) {
            return EMPTY;
        }

        int size = property.count();
        if (size == 1) {
            return getInstance(property.getValue(Type.LONG, 0));
        } else {
            long[] longs = new long[size];
            for (int i = 0; i < longs.length; i++) {
                longs[i] = property.getValue(Type.LONG, i);
            }
            return getInstance(longs);
        }
    }

    /**
     * Get or create an instance of privilege bits for a privilege definition.
     *
     * @param tree A privilege definition tree or the privileges root.
     * @return an instance of {@code PrivilegeBits}
     */
    @NotNull
    public static PrivilegeBits getInstance(@Nullable Tree tree) {
        if (tree == null) {
            return EMPTY;
        }
        String privName = tree.getName();
        if (BUILT_IN.containsKey(privName)) {
            return BUILT_IN.get(privName);
        } else if (REP_PRIVILEGES.equals(privName)) {
            return getInstance(tree.getProperty(REP_NEXT));
        } else {
            return getInstance(tree.getProperty(REP_BITS));
        }
    }

    /**
     * Internal method to get or create an instance of privilege bits for the
     * specified long value.
     *
     * @param bits A long value.
     * @return an instance of {@code PrivilegeBits}
     */
    @NotNull
    private static PrivilegeBits getInstance(long bits) {
        if (bits == NO_PRIVILEGE) {
            return EMPTY;
        } else {
            checkArgument(bits > NO_PRIVILEGE);
            if (BUILT_IN_BITS != null) {
                String key = BUILT_IN_BITS.get(bits);
                if (key != null) {
                    return BUILT_IN.get(key);
                }
            }
            return new PrivilegeBits(new UnmodifiableData(bits));
        }
    }

    /**
     * Internal method to create a new instance of {@code PrivilegeBits}.
     *
     * @param bits A long array.
     * @return an instance of {@code PrivilegeBits}
     */
    @NotNull
    private static PrivilegeBits getInstance(long[] bits) {
        return new PrivilegeBits(new UnmodifiableData(bits));
    }

    /**
     * Calculate the granted permissions by evaluating the given privileges. Note,
     * that only built-in privileges can be mapped to permissions. Any other
     * privileges will be ignored.
     *
     * @param bits The set of privileges present at given tree.
     * @param parentBits The privileges present on the parent tree. These are
     * required in order to determine permissions that include a modification
     * of the parent tree (add_child_nodes, remove_child_nodes).
     * @param isAllow {@code true} if the privileges are granted; {@code false}
     * otherwise.
     * @return the resulting permissions.
     */
    public static long calculatePermissions(@NotNull PrivilegeBits bits,
                                            @NotNull PrivilegeBits parentBits,
                                            boolean isAllow) {
        long privs = bits.d.longValue();
        long perm = Permissions.NO_PERMISSION;
        if ((privs & READ) == READ) {
            perm |= Permissions.READ;
        } else {
            if ((privs & READ_NODES) == READ_NODES) {
                perm |= Permissions.READ_NODE;
            } else if (((privs & READ_PROPERTIES) == READ_PROPERTIES)) {
                perm |= Permissions.READ_PROPERTY;
            }
        }
        if ((privs & MODIFY_PROPERTIES) == MODIFY_PROPERTIES) {
            perm |= Permissions.SET_PROPERTY;
        } else {
            if ((privs & ADD_PROPERTIES) == ADD_PROPERTIES) {
                perm |= Permissions.ADD_PROPERTY;
            }
            if ((privs & ALTER_PROPERTIES) == ALTER_PROPERTIES) {
                perm |= Permissions.MODIFY_PROPERTY;
            }
            if ((privs & REMOVE_PROPERTIES) == REMOVE_PROPERTIES) {
                perm |= Permissions.REMOVE_PROPERTY;
            }
        }
        perm |= calculateWritePermissions(privs, parentBits.d.longValue(), isAllow);

        // the remaining (special) permissions are simply defined on the node
        perm |= calculateOtherPermissions(privs);
        return perm;
    }
    
    private static long calculateWritePermissions(long privs, long parentPrivs, boolean isAllow) {
        long perm = Permissions.NO_PERMISSION;
        
        // add_node permission is granted through privilege on the parent.
        if ((parentPrivs & ADD_CHILD_NODES) == ADD_CHILD_NODES) {
            perm |= Permissions.ADD_NODE;
        }

        /*
         remove_node is
         allowed: only if remove_child_nodes privilege is present on
                  the parent AND remove_node is present on the node itself
         denied : if either remove_child_nodes is denied on the parent
                  OR remove_node is denied on the node itself.
        */
        if (isAllow) {
            if ((parentPrivs & REMOVE_CHILD_NODES) == REMOVE_CHILD_NODES &&
                    (privs & REMOVE_NODE) == REMOVE_NODE) {
                perm |= Permissions.REMOVE_NODE;
            }
        } else {
            if ((parentPrivs & REMOVE_CHILD_NODES) == REMOVE_CHILD_NODES ||
                    (privs & REMOVE_NODE) == REMOVE_NODE) {
                perm |= Permissions.REMOVE_NODE;
            }
        }

        // modify_child_node_collection permission
        if ((privs & ADD_CHILD_NODES) == ADD_CHILD_NODES &&
                (privs & REMOVE_CHILD_NODES) == REMOVE_CHILD_NODES) {
            perm |= Permissions.MODIFY_CHILD_NODE_COLLECTION;
        }
        return perm;
    }
    
    private static long calculateOtherPermissions(long privs) {
        long perm = Permissions.NO_PERMISSION;
        if ((privs & READ_AC) == READ_AC) {
            perm |= Permissions.READ_ACCESS_CONTROL;
        }
        if ((privs & MODIFY_AC) == MODIFY_AC) {
            perm |= Permissions.MODIFY_ACCESS_CONTROL;
        }
        if ((privs & LIFECYCLE_MNGMT) == LIFECYCLE_MNGMT) {
            perm |= Permissions.LIFECYCLE_MANAGEMENT;
        }
        if ((privs & LOCK_MNGMT) == LOCK_MNGMT) {
            perm |= Permissions.LOCK_MANAGEMENT;
        }
        if ((privs & NODE_TYPE_MNGMT) == NODE_TYPE_MNGMT) {
            perm |= Permissions.NODE_TYPE_MANAGEMENT;
        }
        if ((privs & RETENTION_MNGMT) == RETENTION_MNGMT) {
            perm |= Permissions.RETENTION_MANAGEMENT;
        }
        if ((privs & VERSION_MNGMT) == VERSION_MNGMT) {
            perm |= Permissions.VERSION_MANAGEMENT;
        }
        if ((privs & WORKSPACE_MNGMT) == WORKSPACE_MNGMT) {
            perm |= Permissions.WORKSPACE_MANAGEMENT;
        }
        if ((privs & NODE_TYPE_DEF_MNGMT) == NODE_TYPE_DEF_MNGMT) {
            perm |= Permissions.NODE_TYPE_DEFINITION_MANAGEMENT;
        }
        if ((privs & NAMESPACE_MNGMT) == NAMESPACE_MNGMT) {
            perm |= Permissions.NAMESPACE_MANAGEMENT;
        }
        if ((privs & PRIVILEGE_MNGMT) == PRIVILEGE_MNGMT) {
            perm |= Permissions.PRIVILEGE_MANAGEMENT;
        }
        if ((privs & USER_MNGMT) == USER_MNGMT) {
            perm |= Permissions.USER_MANAGEMENT;
        }
        if ((privs & INDEX_DEFINITION_MNGMT) == INDEX_DEFINITION_MNGMT) {
            perm |= Permissions.INDEX_DEFINITION_MANAGEMENT;
        }
        return perm;
    }

    /**
     * Returns {@code true} if this privilege bits includes no privileges
     * at all.
     *
     * @return {@code true} if this privilege bits includes no privileges
     *         at all; {@code false} otherwise.
     * @see org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions#NO_PERMISSION
     */
    public boolean isEmpty() {
        return d.isEmpty();
    }

    /**
     * Returns an unmodifiable instance.
     *
     * @return an unmodifiable {@code PrivilegeBits} instance.
     */
    @NotNull
    public PrivilegeBits unmodifiable() {
        if (d instanceof ModifiableData) {
            if (d.isSimple()) {
                return getInstance(d.longValue());
            } else {
                long[] bits = d.longValues();
                long[] copy = new long[bits.length];
                System.arraycopy(bits, 0, copy, 0, bits.length);
                return getInstance(copy);
            }
        } else {
            return this;
        }
    }

    @NotNull
    public PrivilegeBits modifiable() {
        if (d instanceof ModifiableData) {
            return this;
        } else {
            return getInstance(this);
        }
    }

    /**
     * Returns {@code true} if all privileges defined by the specified
     * {@code otherBits} are present in this instance.
     *
     * @param otherBits
     * @return {@code true} if all privileges defined by the specified
     *         {@code otherBits} are included in this instance; {@code false}
     *         otherwise.
     */
    public boolean includes(@NotNull PrivilegeBits otherBits) {
        return d.includes(otherBits.d);
    }

    /**
     * @return {@code true} if this instance represents one of the built-in privilege
     * @see #BUILT_IN
     */
    public boolean isBuiltin() {
        return d.isSimple() && BUILT_IN_BITS.containsKey(d.longValue());
    }

    /**
     * Adds the other privilege bits to this instance.
     *
     * @param other The other privilege bits to be added.
     * @return The updated instance.
     * @throws UnsupportedOperationException if this instance is immutable.
     */
    @NotNull
    public PrivilegeBits add(@NotNull PrivilegeBits other) {
        if (d instanceof ModifiableData) {
            ((ModifiableData) d).add(other.d);
            return this;
        } else {
            throw unsupported();
        }
    }

    /**
     * Subtracts the other PrivilegeBits from the this.
* If the specified bits do not intersect with this, it isn't modified.
* If {@code this} is included in {@code other} {@link #EMPTY empty} * privilege bits is returned. * * @param other The other privilege bits to be subtracted from this instance. * @return The updated instance. * @throws UnsupportedOperationException if this instance is immutable. */ @NotNull public PrivilegeBits diff(@NotNull PrivilegeBits other) { if (d instanceof ModifiableData) { ((ModifiableData) d).diff(other.d); return this; } else { throw unsupported(); } } /** * Subtracts the {@code b} from {@code a} and adds the result (diff) * to this instance. * * @param a An instance of privilege bits. * @param b An instance of privilege bits. * @return The updated instance. * @throws UnsupportedOperationException if this instance is immutable. */ @NotNull public PrivilegeBits addDifference(@NotNull PrivilegeBits a, @NotNull PrivilegeBits b) { if (d instanceof ModifiableData) { ((ModifiableData) d).addDifference(a.d, b.d); return this; } else { throw unsupported(); } } /** * Retains the elements in this {@code PrivilegeBits} that are contained in * the specified other {@code PrivilegeBits}. * * @param other Other privilege bits. * @return This modifiable instance of privilege bits modified such it contains * only privileges that were also contained in the {@code other} instance. */ @NotNull public PrivilegeBits retain(@NotNull PrivilegeBits other) { if (d instanceof ModifiableData) { ((ModifiableData) d).retain(other.d); return this; } else { throw unsupported(); } } @NotNull public PropertyState asPropertyState(@NotNull String name) { return PropertyStates.createProperty(name, Longs.asList(d.longValues()), Type.LONGS); } /** * Method to calculate the next privilege bits associated with this instance. * * @return an new instance of {@code PrivilegeBits} */ @NotNull public PrivilegeBits nextBits() { if (this == EMPTY) { return EMPTY; } else { return new PrivilegeBits(d.next()); } } /** * Write this instance as property to the specified tree. * * @param tree The target tree. */ public void writeTo(@NotNull Tree tree) { String name = (REP_PRIVILEGES.equals(tree.getName())) ? REP_NEXT : REP_BITS; tree.setProperty(asPropertyState(name)); } private static UnsupportedOperationException unsupported() { return new UnsupportedOperationException("immutable privilege bits"); } //-------------------------------------------------------------< Object >--- @Override public int hashCode() { return d.hashCode(); } @Override public boolean equals(Object o) { if (o == this) { return true; } else if (o instanceof PrivilegeBits) { return d.equals(((PrivilegeBits) o).d); } else { return false; } } @Override public String toString() { StringBuilder sb = new StringBuilder("PrivilegeBits: "); if (d.isSimple()) { sb.append(d.longValue()); } else { sb.append(Arrays.toString(d.longValues())); } return sb.toString(); } //------------------------------------------------------< inner classes >--- /** * Base class for the internal privilege bits representation and handling. */ private abstract static class Data { abstract boolean isEmpty(); abstract long longValue(); abstract long[] longValues(); abstract boolean isSimple(); abstract Data next(); abstract boolean includes(Data other); /** * Checks if all {@code otherBits} is already included in {@code bits}. *

* Truth table: *

         * | b\o | 0 | 1 |
         * |  0  | 1 | 0 |
         * |  1 |  1 | 1 |
         * 
* @param bits the super set of bits * @param otherBits the bits to check against * @return {@code true} if all other bits are included in bits. */ static boolean includes(long bits, long otherBits) { return (bits | ~otherBits) == -1; } /** * Checks if all {@code otherBits} is already included in {@code bits}. *

* Truth table: *

         * | b\o | 0 | 1 |
         * |  0  | 1 | 0 |
         * |  1 |  1 | 1 |
         * 
* @param bits the super set of bits * @param otherBits the bits to check against * @return {@code true} if all other bits are included in bits. */ static boolean includes(long[] bits, long[] otherBits) { if (otherBits.length <= bits.length) { // test for each long if is included for (int i = 0; i < otherBits.length; i++) { if ((bits[i] | ~otherBits[i]) != -1) { return false; } } return true; } else { // otherbits array is longer > cannot be included in bits return false; } } } /** * Immutable Data object */ private static final class UnmodifiableData extends Data { private static final long MAX = Long.MAX_VALUE / 2; private static final UnmodifiableData EMPTY = new UnmodifiableData(NO_PRIVILEGE); private final long bits; private final long[] bitsArr; private final boolean isSimple; private final int hashcode; private UnmodifiableData(long bits) { this.bits = bits; bitsArr = null; isSimple = true; hashcode = Long.valueOf(this.bits).hashCode(); } private UnmodifiableData(long[] bitsArr) { bits = NO_PRIVILEGE; this.bitsArr = bitsArr; isSimple = false; hashcode = Arrays.hashCode(this.bitsArr); } @Override boolean isEmpty() { return this == EMPTY; } @Override long longValue() { return bits; } @Override long[] longValues() { if (isSimple) { return new long[] { bits }; } return bitsArr; } @Override boolean isSimple() { return isSimple; } @Override Data next() { if (this == EMPTY) { return EMPTY; } else if (isSimple) { if (bits < MAX) { long b = bits << 1; return new UnmodifiableData(b); } else { return new UnmodifiableData(new long[]{bits}).next(); } } else { long[] bts; long last = bitsArr[bitsArr.length - 1]; if (last < MAX) { bts = new long[bitsArr.length]; System.arraycopy(bitsArr, 0, bts, 0, bitsArr.length); bts[bts.length - 1] = last << 1; } else { bts = new long[bitsArr.length + 1]; bts[bts.length - 1] = 1; } return new UnmodifiableData(bts); } } @Override boolean includes(Data other) { if (isSimple) { return (other.isSimple()) && includes(bits, other.longValue()); } else { return includes(bitsArr, other.longValues()); } } //---------------------------------------------------------< Object >--- @Override public int hashCode() { return hashcode; } @Override public boolean equals(Object o) { if (o == this) { return true; } else if (o instanceof UnmodifiableData) { UnmodifiableData d = (UnmodifiableData) o; if (isSimple != d.isSimple) { return false; } if (isSimple) { return bits == d.bits; } else { return Arrays.equals(bitsArr, d.bitsArr); } } else { return false; } } } /** * Mutable implementation of the Data base class. */ private static final class ModifiableData extends Data { private long[] bits; private ModifiableData() { bits = new long[]{NO_PRIVILEGE}; } @Override boolean isEmpty() { return bits.length == 1 && bits[0] == NO_PRIVILEGE; } @Override long longValue() { return (bits.length == 1) ? bits[0] : NO_PRIVILEGE; } @Override long[] longValues() { return bits; } @Override boolean isSimple() { return bits.length == 1; } @Override Data next() { throw new UnsupportedOperationException("Not implemented."); } @Override boolean includes(Data other) { if (bits.length == 1) { return other.isSimple() && includes(bits[0], other.longValue()); } else { return includes(bits, other.longValues()); } } /** * Add the other Data to this instance. * * @param other */ private void add(Data other) { if (other != this) { if (bits.length == 1 && other.isSimple()) { bits[0] |= other.longValue(); } else { or(other.longValues()); } } } /** * Subtract the other Data from this instance. * * @param other */ private void diff(Data other) { if (bits.length == 1 && other.isSimple()) { bits[0] = bits[0] & ~other.longValue(); } else { bits = diff(bits, other.longValues()); } } /** * Add the diff between the specified Data a and b. * * @param a * @param b */ private void addDifference(Data a, Data b) { if (a.isSimple() && b.isSimple()) { bits[0] |= a.longValue() & ~b.longValue(); } else { long[] diff = diff(a.longValues(), b.longValues()); or(diff); } } private void or(long[] b) { if (b.length > bits.length) { // enlarge the array long[] res = new long[b.length]; System.arraycopy(bits, 0, res, 0, bits.length); bits = res; } for (int i = 0; i < b.length; i++) { bits[i] |= b[i]; } } private void retain(Data other) { if (isSimple()) { bits[0] &= other.longValue(); } else { long[] lvs = longValues(); long[] bLvs = other.longValues(); long[] res = (lvs.length <= bLvs.length) ? new long[lvs.length] : new long[bLvs.length]; int compactSize = -1; for (int i = 0; i < res.length; i++) { res[i] = (lvs[i] & bLvs[i]); if (res[i] == 0) { if (compactSize == -1) { compactSize = i+1; } } else { compactSize = -1; } } if (compactSize != -1 && res.length > compactSize) { bits = Arrays.copyOfRange(res, 0, compactSize); } else { bits = res; } } } private static long[] diff(long[] a, long[] b) { int index = -1; long[] res = new long[(Math.max(a.length, b.length))]; for (int i = 0; i < res.length; i++) { if (i < a.length && i < b.length) { res[i] = a[i] & ~b[i]; } else { res[i] = (i < a.length) ? a[i] : 0; } // remember start of trailing 0 array entries if (res[i] != 0) { index = -1; } else if (index == -1) { index = i; } } switch (index) { case -1: // no need to remove trailing 0-long from the array return res; case 0: // array consisting of one or multiple 0 return new long[]{NO_PRIVILEGE}; default: // remove trailing 0-long entries from the array long[] r2 = new long[index]; System.arraycopy(res, 0, r2, 0, index); return r2; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy