Package org.lilyproject.repository.impl.compat

Source Code of org.lilyproject.repository.impl.compat.Lily11RecordIdDecoder

/*
* Copyright 2012 NGDATA nv
*
* Licensed 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.lilyproject.repository.impl.compat;

import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;

import org.lilyproject.bytes.api.DataInput;
import org.lilyproject.bytes.impl.DataInputImpl;
import org.lilyproject.repository.api.IdGenerator;
import org.lilyproject.repository.api.Link;
import org.lilyproject.repository.api.RecordId;
import org.lilyproject.repository.impl.id.IdGeneratorImpl;
import org.lilyproject.repository.impl.id.UUIDRecordId;
import org.lilyproject.repository.impl.id.UserRecordId;
import org.lilyproject.repository.impl.id.VariantRecordId;

/**
* This class contains code to parse Lily <= 1.1 style record id's encoded as bytes.
*/
public class Lily11RecordIdDecoder {
    private static final Constructor UUID_CONSTRUCTOR;
    static {
        try {
            UUID_CONSTRUCTOR = UUIDRecordId.class.getDeclaredConstructor(UUID.class, IdGeneratorImpl.class);
            UUID_CONSTRUCTOR.setAccessible(true);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    private static final Constructor USER_CONSTRUCTOR;
    static {
        try {
            USER_CONSTRUCTOR = UserRecordId.class.getDeclaredConstructor(String.class, IdGeneratorImpl.class);
            USER_CONSTRUCTOR.setAccessible(true);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    private static final Constructor VARIANT_CONSTRUCTOR;
    static {
        try {
            VARIANT_CONSTRUCTOR = VariantRecordId.class.getDeclaredConstructor(RecordId.class, Map.class, IdGeneratorImpl.class);
            VARIANT_CONSTRUCTOR.setAccessible(true);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    private Lily11RecordIdDecoder(IdGeneratorImpl idGenerator) {
    }

    private Lily11RecordIdDecoder() {
    }

    private static enum IdIdentifier {USER((byte)0), UUID((byte)1), VARIANT((byte)2);

        private final byte identifierByte;

        IdIdentifier(byte identifierByte) {
            this.identifierByte = identifierByte;
        }

        public byte getIdentifierByte() {
            return identifierByte;
        }
    }

    private static final IdIdentifier[] IDENTIFIERS;
    static {
        IDENTIFIERS = new IdIdentifier[3];
        IDENTIFIERS[0] = IdIdentifier.USER;
        IDENTIFIERS[1] = IdIdentifier.UUID;
        IDENTIFIERS[2] = IdIdentifier.VARIANT;
    }

    public static RecordId decode(DataInput dataInput, IdGenerator idGenerator) {
        try {
            // Read the identifier byte at the end of the input
            int pos = dataInput.getPosition();
            int size = dataInput.getSize();
            dataInput.setPosition(size - 1);
            byte identifierByte = dataInput.readByte();
            dataInput.setPosition(pos);

            // Virtually remove the identifier byte from the input
            dataInput.setSize(size - 1);

            IdIdentifier idIdentifier = IDENTIFIERS[identifierByte];
            RecordId recordId = null;
            switch (idIdentifier) {
                case UUID:
                    UUID uuid = new UUID(dataInput.readLong(), dataInput.readLong());
                    recordId = (RecordId)UUID_CONSTRUCTOR.newInstance(uuid, idGenerator);
                    break;
                case USER:
                    String basicRecordIdString = dataInput.readUTF();
                    checkIdString(basicRecordIdString, "record id");
                    recordId = (RecordId)USER_CONSTRUCTOR.newInstance(basicRecordIdString, idGenerator);
                    break;
                case VARIANT:
                    int position = dataInput.getPosition();
                    int length = dataInput.getSize();
                    dataInput.setPosition(length - 8);
                    int nrOfVariants = dataInput.readInt();
                    int masterRecordIdLength = dataInput.readInt();

                    DataInput masterRecordIdInput = new DataInputImpl((DataInputImpl)dataInput, position, masterRecordIdLength);
                    RecordId masterRecordId = decode(masterRecordIdInput, idGenerator);
                    dataInput.setPosition(masterRecordIdLength);

                    SortedMap<String, String> varProps = new TreeMap<String, String>();
                    for (int i = 0; i < nrOfVariants; i++) {
                        String dimension = dataInput.readUTF();
                        String dimensionValue = dataInput.readUTF();

                        checkVariantPropertyNameValue(dimension);
                        checkVariantPropertyNameValue(dimensionValue);
                        varProps.put(dimension, dimensionValue);
                    }

                    recordId = (RecordId)VARIANT_CONSTRUCTOR.newInstance(masterRecordId, varProps, idGenerator);
                    break;
                default:
                    // will already have failed on the IDENTIFIERS array lookup above
                    break;
            }
            return recordId;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    protected static void checkVariantPropertyNameValue(String text) {
        checkIdString(text, "variant property name or value");
    }

    protected static void checkIdString(String text, String what) {
        if (text.length() == 0) {
            throw new IllegalArgumentException("Zero-length " + what + " is not allowed.");
        }

        if (Character.isWhitespace(text.charAt(0)) || Character.isWhitespace(text.charAt(text.length() - 1))) {
            throw new IllegalArgumentException(what + " should not start or end with whitespace: \"" + text + "\".");
        }

        checkReservedCharacters(text);
    }

    protected static void checkReservedCharacters(String text) {
        for (int i = 0; i < text.length(); i++) {
            switch (text.charAt(i)) {
                case '.':
                case '=':
                case ',':
                    throw new IllegalArgumentException("Reserved record id character (one of: . , =) in \"" +
                            text + "\".");
            }
        }
    }

    public static Link decodeLink(DataInput dataInput, IdGenerator idGenerator) {
        // Format: see toBytes.

        int recordIdLength = dataInput.readInt();
        byte[] recordIdBytes = null;
        if (recordIdLength > 0) {
            recordIdBytes = dataInput.readBytes(recordIdLength);
        }
        String args = dataInput.readUTF();

        if (recordIdLength == 0 && args == null) {
            return new Link();
        }

        Link.LinkBuilder builder = Link.newBuilder();
        if (recordIdLength > 0) {
            RecordId id = decode(new DataInputImpl(recordIdBytes), idGenerator);
            builder.recordId(id);
        }

        if (args != null && args.length() > 0) {
            argsFromString(args, builder, args /* does not matter, should never be invalid */);
        }

        return builder.create();
    }

    private static void argsFromString(String args, Link.LinkBuilder builder, String link) {
        String[] variantStringParts = args.split(",");
        for (String part : variantStringParts) {
            int eqPos = part.indexOf('=');
            if (eqPos == -1) {
                String thing = part.trim();
                if (thing.equals("*")) {
                    // this is the default, but if users want to make explicit, allow them
                    builder.copyAll(true);
                } else if (thing.equals("!*")) {
                    builder.copyAll(false);
                } else if (thing.startsWith("+") && thing.length() > 1) {
                    builder.copy(thing.substring(1));
                } else if  (thing.startsWith("-") && thing.length() > 1) {
                    builder.remove(thing.substring(1));
                } else {
                    throw new IllegalArgumentException("Invalid link: " + link);
                }
            } else {
                String name = part.substring(0, eqPos).trim();
                String value = part.substring(eqPos + 1).trim();
                if (name.length() == 0 || value.length() == 0) {
                    throw new IllegalArgumentException("Invalid link: " + link);
                }
                builder.set(name, value);
            }
        }
    }
}
TOP

Related Classes of org.lilyproject.repository.impl.compat.Lily11RecordIdDecoder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.