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

com.microsoft.azure.documentdb.internal.ResourceId Maven / Gradle / Ivy

package com.microsoft.azure.documentdb.internal;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.microsoft.azure.documentdb.DocumentClientException;
import com.microsoft.azure.documentdb.Resource;

/**
 * Used internally to represents a Resource ID in the Azure Cosmos DB database service.
 */
public class ResourceId {
    private static final Logger LOGGER = LoggerFactory.getLogger(ResourceId.class);

    static final short Length = 20;
    static final short OFFER_ID_LENGTH = 3;

    private int database;
    private int documentCollection;
    private long storedProcedure;
    private long trigger;
    private long userDefinedFunction;
    private long conflict;
    private long document;
    private long partitionKeyRange;
    private int user;
    private long permission;
    private int attachment;
    private long offer;

    private ResourceId() {
        this.offer = 0;
        this.database = 0;
        this.documentCollection = 0;
        this.storedProcedure = 0;
        this.trigger = 0;
        this.userDefinedFunction = 0;
        this.document = 0;
        this.partitionKeyRange = 0;
        this.user = 0;
        this.conflict = 0;
        this.permission = 0;
        this.attachment = 0;
    }

    public static ResourceId parse(String id) throws IllegalArgumentException {
        Pair pair = ResourceId.tryParse(id);

        if (!pair.getKey()) {
            throw new IllegalArgumentException(String.format(
                    "Invalid resource id %s", id));
        }
        return pair.getValue();
    }

    public static ResourceId newDatabaseId(int dbid) {
        ResourceId resourceId = new ResourceId();
        resourceId.database = dbid;
        return resourceId;
    }

    public static ResourceId newDocumentCollectionId(String databaseId, int collectionId)
            throws DocumentClientException {
        ResourceId dbId = ResourceId.parse(databaseId);

        ResourceId collectionResourceId = new ResourceId();
        collectionResourceId.database = dbId.database;
        collectionResourceId.documentCollection = collectionId;

        return collectionResourceId;
    }

    public static ResourceId newUserId(String databaseId, int userId) throws DocumentClientException {
        ResourceId dbId = ResourceId.parse(databaseId);

        ResourceId userResourceId = new ResourceId();
        userResourceId.database = dbId.database;
        userResourceId.user = userId;

        return userResourceId;
    }

    public static ResourceId newPermissionId(String userId, long permissionId) throws DocumentClientException {
        ResourceId usrId = ResourceId.parse(userId);

        ResourceId permissionResourceId = new ResourceId();
        permissionResourceId.database = usrId.database;
        permissionResourceId.user = usrId.user;
        permissionResourceId.permission = permissionId;
        return permissionResourceId;
    }

    public static ResourceId newAttachmentId(String documentId, int attachmentId) throws DocumentClientException {
        ResourceId docId = ResourceId.parse(documentId);

        ResourceId attachmentResourceId = new ResourceId();
        attachmentResourceId.database = docId.database;
        attachmentResourceId.documentCollection = docId.documentCollection;
        attachmentResourceId.document = docId.document;
        attachmentResourceId.attachment = attachmentId;

        return attachmentResourceId;
    }

    public static Pair tryParse(String id) {
        ResourceId rid = null;

        try {
            if (StringUtils.isEmpty(id))
                return Pair.of(false, null);

            if (id.length() % 4 != 0) {
                // our ResourceId string is always padded
                return Pair.of(false, null);
            }

            byte[] buffer = null;

            Pair pair = ResourceId.verify(id);

            if (!pair.getKey())
                return Pair.of(false, null);

            buffer = pair.getValue();

            if (buffer.length % 4 != 0 && buffer.length != ResourceId.OFFER_ID_LENGTH) {
                return Pair.of(false, null);
            }

            rid = new ResourceId();

            if (buffer.length == ResourceId.OFFER_ID_LENGTH) {
                rid.offer = 0;
                for (int index = 0; index < ResourceId.OFFER_ID_LENGTH; index++)
                {
                    rid.offer |= (long)(buffer[index] << (index * 8));
                }
                return Pair.of(true, rid);
            }

            if (buffer.length >= 4)
                rid.database = ByteBuffer.wrap(buffer).getInt();

            if (buffer.length >= 8) {
                byte[] temp = new byte[4];
                ResourceId.blockCopy(buffer, 4, temp, 0, 4);

                boolean isCollection = (temp[0] & (128)) > 0;

                if (isCollection) {
                    rid.documentCollection = ByteBuffer.wrap(temp).getInt();

                    if (buffer.length >= 16) {
                        byte[] subCollRes = new byte[8];
                        ResourceId.blockCopy(buffer, 8, subCollRes, 0, 8);

                        long subCollectionResource = ByteBuffer.wrap(buffer, 8, 8).getLong();
                        if ((subCollRes[7] >> 4) == (byte) CollectionChildResourceType.Document) {
                            rid.document = subCollectionResource;

                            if (buffer.length == 20) {
                                rid.attachment = ByteBuffer.wrap(buffer, 16, 4).getInt();
                            }
                        } else if (Math.abs(subCollRes[7] >> 4) == (byte) CollectionChildResourceType.StoredProcedure) {
                            rid.storedProcedure = subCollectionResource;
                        } else if ((subCollRes[7] >> 4) == (byte) CollectionChildResourceType.Trigger) {
                            rid.trigger = subCollectionResource;
                        } else if ((subCollRes[7] >> 4) == (byte) CollectionChildResourceType.UserDefinedFunction) {
                            rid.userDefinedFunction = subCollectionResource;
                        } else if ((subCollRes[7] >> 4) == (byte) CollectionChildResourceType.Conflict) {
                            rid.conflict = subCollectionResource;
                        } else if ((subCollRes[7] >> 4) == (byte) CollectionChildResourceType.PartitionKeyRange) {
                            rid.partitionKeyRange = subCollectionResource;
                        } else {
                            return Pair.of(false, rid);
                        }
                    } else if (buffer.length != 8) {
                        return Pair.of(false, rid);
                    }
                } else {
                    rid.user = ByteBuffer.wrap(temp).getInt();

                    if (buffer.length == 16) {
                        rid.permission = ByteBuffer.wrap(buffer, 8, 8).getLong();
                    } else if (buffer.length != 8) {
                        return Pair.of(false, rid);
                    }
                }
            }

            return Pair.of(true, rid);
        } catch (Exception e) {
            LOGGER.warn("Unable to parse ResourceId {}: {}", id, e.getMessage(), e);
            return Pair.of(false, null);
        }
    }

    public static Pair verify(String id) {
        if (StringUtils.isEmpty(id))
            throw new IllegalArgumentException("id");

        byte[] buffer = null;

        try {
            buffer = ResourceId.fromBase64String(id);
        } catch (Exception e) {
            LOGGER.warn("Unable to parse ResourceId from id {}", id, e);
        }

        if (buffer == null || buffer.length > ResourceId.Length) {
            buffer = null;
            return Pair.of(false, buffer);
        }

        return Pair.of(true, buffer);
    }

    public static boolean verifyBool(String id) {
        return verify(id).getKey();
    }

    static byte[] fromBase64String(String s) {
        return Base64.decodeBase64(s.replace('-', '/'));
    }

    static String toBase64String(byte[] buffer) {
        return ResourceId.toBase64String(buffer, 0, buffer.length);
    }

    static String toBase64String(byte[] buffer, int offset, int length) {
        byte[] subBuffer = Arrays.copyOfRange(buffer, offset, length);

        return Utils.encodeBase64String(subBuffer).replace('/', '-');
    }

    // Copy the bytes provided with a for loop, faster when there are only a few
    // bytes to copy
    static void blockCopy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) {
        int stop = srcOffset + count;
        for (int i = srcOffset; i < stop; i++)
            dst[dstOffset++] = src[i];
    }

    private static byte[] convertToBytesUsingByteBuffer(int value) {
        ByteOrder order = ByteOrder.BIG_ENDIAN;
        ByteBuffer buffer = ByteBuffer.allocate(4);
        buffer.order(order);
        return buffer.putInt(value).array();
    }

    private static byte[] convertToBytesUsingByteBuffer(long value) {
        ByteOrder order = ByteOrder.BIG_ENDIAN;
        ByteBuffer buffer = ByteBuffer.allocate(8);
        buffer.order(order);
        return buffer.putLong(value).array();
    }

    public boolean isDatabaseId() {
        return this.getDatabase() != 0 && (this.getDocumentCollection() == 0 && this.getUser() == 0);
    }

    public int getDatabase() {
        return this.database;
    }

    public ResourceId getDatabaseId() {
        ResourceId rid = new ResourceId();
        rid.database = this.database;
        return rid;
    }

    public int getDocumentCollection() {
        return this.documentCollection;
    }

    public ResourceId getDocumentCollectionId() {
        ResourceId rid = new ResourceId();
        rid.database = this.database;
        rid.documentCollection = this.documentCollection;
        return rid;
    }

    /**
     * Unique (across all databases) Id for the DocumentCollection.
     * First 4 bytes are DatabaseId and next 4 bytes are CollectionId.
     *
     * @return the unique collectionId
     */
    public long getUniqueDocumentCollectionId() {
        return (long) this.database << 32 | this.documentCollection;
    }

    public long getStoredProcedure() {
        return this.storedProcedure;
    }

    public ResourceId getStoredProcedureId() {
        ResourceId rid = new ResourceId();
        rid.database = this.database;
        rid.documentCollection = this.documentCollection;
        rid.storedProcedure = this.storedProcedure;
        return rid;
    }

    public long getTrigger() {
        return this.trigger;
    }

    public ResourceId getTriggerId() {
        ResourceId rid = new ResourceId();
        rid.database = this.database;
        rid.documentCollection = this.documentCollection;
        rid.trigger = this.trigger;
        return rid;
    }

    public long getUserDefinedFunction() {
        return this.userDefinedFunction;
    }

    public ResourceId getUserDefinedFunctionId() {
        ResourceId rid = new ResourceId();
        rid.database = this.database;
        rid.documentCollection = this.documentCollection;
        rid.userDefinedFunction = this.userDefinedFunction;
        return rid;
    }

    public long getConflict() {
        return this.conflict;
    }

    public ResourceId getConflictId() {
        ResourceId rid = new ResourceId();
        rid.database = this.database;
        rid.documentCollection = this.documentCollection;
        rid.conflict = this.conflict;
        return rid;
    }

    public long getDocument() {
        return this.document;
    }

    public ResourceId getDocumentId() {
        ResourceId rid = new ResourceId();
        rid.database = this.database;
        rid.documentCollection = this.documentCollection;
        rid.document = this.document;
        return rid;
    }
    
    public long getPartitionKeyRange() {
        return this.partitionKeyRange;
    }

    public ResourceId getPartitionKeyRangeId() {
        ResourceId rid = new ResourceId();
        rid.database = this.database;
        rid.documentCollection = this.documentCollection;
        rid.partitionKeyRange = this.partitionKeyRange;
        return rid;
    }

    public int getUser() {
        return this.user;
    }

    public ResourceId getUserId() {
        ResourceId rid = new ResourceId();
        rid.database = this.database;
        rid.user = this.user;
        return rid;
    }

    public long getPermission() {
        return this.permission;
    }

    public ResourceId getPermissionId() {
        ResourceId rid = new ResourceId();
        rid.database = this.database;
        rid.user = this.user;
        rid.permission = this.permission;
        return rid;
    }

    public int getAttachment() {
        return this.attachment;
    }

    public ResourceId getAttachmentId() {
        ResourceId rid = new ResourceId();
        rid.database = this.database;
        rid.documentCollection = this.documentCollection;
        rid.document = this.document;
        rid.attachment = this.attachment;
        return rid;
    }

    public long getOffer() { return this.offer; }

    public ResourceId getOfferId() {
        ResourceId rid = new ResourceId();
        rid.offer = this.offer;
        return rid;
    }

    public byte[] getValue() {
        int len = 0;
        if (this.offer != 0)
            len += ResourceId.OFFER_ID_LENGTH;
        else if (this.database != 0)
            len += 4;
        if (this.documentCollection != 0 || this.user != 0)
            len += 4;
        if (this.document != 0 || this.permission != 0
                || this.storedProcedure != 0 || this.trigger != 0
                || this.userDefinedFunction != 0 || this.conflict != 0
                || this.partitionKeyRange != 0)
            len += 8;
        if (this.attachment != 0)
            len += 4;

        byte[] val = new byte[len];

        if (this.offer != 0)
            ResourceId.blockCopy(convertToBytesUsingByteBuffer(this.offer),
                    0, val, 0, ResourceId.OFFER_ID_LENGTH);
        else if (this.database != 0)
            ResourceId.blockCopy(convertToBytesUsingByteBuffer(this.database),
                    0, val, 0, 4);

        if (this.documentCollection != 0)
            ResourceId.blockCopy(
                    convertToBytesUsingByteBuffer(this.documentCollection), 
					0, val, 4, 4);
        else if (this.user != 0)
            ResourceId.blockCopy(convertToBytesUsingByteBuffer(this.user), 
			        0, val, 4, 4);

        if (this.storedProcedure != 0)
            ResourceId.blockCopy(
                    convertToBytesUsingByteBuffer(this.storedProcedure), 
					0, val, 8, 8);
        else if (this.trigger != 0)
            ResourceId.blockCopy(convertToBytesUsingByteBuffer(this.trigger),
                    0, val, 8, 8);
        else if (this.userDefinedFunction != 0)
            ResourceId.blockCopy(
                    convertToBytesUsingByteBuffer(this.userDefinedFunction), 
					0, val, 8, 8);
        else if (this.conflict != 0)
            ResourceId.blockCopy(convertToBytesUsingByteBuffer(this.conflict),
                    0, val, 8, 8);
        else if (this.document != 0)
            ResourceId.blockCopy(convertToBytesUsingByteBuffer(this.document),
                    0, val, 8, 8);
        else if (this.permission != 0)
            ResourceId.blockCopy(
                    convertToBytesUsingByteBuffer(this.permission), 
					0, val, 8, 8);
        else if (this.partitionKeyRange != 0)
            ResourceId.blockCopy(
			        convertToBytesUsingByteBuffer(this.partitionKeyRange), 
					0, val, 8, 8);

        if (this.attachment != 0)
            ResourceId.blockCopy(
			        convertToBytesUsingByteBuffer(this.attachment), 
			        0, val, 16, 4);

        return val;
    }

    public String toString() {
        return ResourceId.toBase64String(this.getValue());
    }

    // Using a byte however, we only need nibble here.
    private static class CollectionChildResourceType {
        public static final byte Document = 0x0;
        public static final byte StoredProcedure = 0x08;
        public static final byte Trigger = 0x07;
        public static final byte UserDefinedFunction = 0x06;
        public static final byte Conflict = 0x04;
        public static final byte PartitionKeyRange = 0x05;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy