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

com.ebuddy.cassandra.structure.DefaultPath Maven / Gradle / Ivy

/*
 * Copyright 2013 eBuddy B.V.
 *
 *    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 com.ebuddy.cassandra.structure;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import javax.annotation.Nullable;

import org.apache.commons.lang3.StringUtils;

import com.ebuddy.cassandra.Path;
import com.google.common.base.Function;
import com.google.common.collect.Lists;

/**
 * Implementation of Path used as column names in Cassandra for encoding structures and for querying elements
 * of a structured object.
 *
 * @author Eric Zoerner [email protected]
 */
public class DefaultPath implements Path {
    private static final char PATH_DELIMITER_CHAR = '/';
    private static final String LIST_INDEX_PREFIX = "@";

    private static final Function urlEncodeFunction = new UrlEncode();

    private final List pathElements;


    /** Create a DefaultPath from a list of encoded path element strings. */
    private DefaultPath(List encodedPathElements) {
        pathElements = encodedPathElements;
    }

    /** Create a DefaultPath from an array of encoded path element strings. */
    private DefaultPath(String... encodedPathElements) {
        this(Arrays.asList(encodedPathElements));
    }

    /** Create a DefaultPath from un-encoded string elements. */
    public static DefaultPath fromStrings(String... elements) {
        return new DefaultPath(Lists.transform(Arrays.asList(elements), urlEncodeFunction));
    }

    @Override
    public Path concat(Path other) {
        List newPathElements = new LinkedList();
        newPathElements.addAll(pathElements);
        newPathElements.addAll(other.getElements());
        return new DefaultPath(newPathElements);
    }

    /**
     * Paths always end with the delimiter character in order to facilitate
     * start/finish slice queries in Cassandra.
     * @return the (encoded) String representation of a path.
     */
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        for (String pathElement : pathElements) {
            builder.append(pathElement);
            builder.append(PATH_DELIMITER_CHAR);
        }
        return builder.toString();
    }

    public static DefaultPath fromIndex(int i) {
        return new DefaultPath(Arrays.asList(LIST_INDEX_PREFIX + i));
    }

    @Override
    public String head() {
        return pathElements.size() == 0 ? null : pathElements.get(0);
    }

    @Override
    public DefaultPath tail() {
        return tail(1);
    }

    @Override
    public DefaultPath tail(int startIndex) {
        return new DefaultPath(pathElements.subList(startIndex, pathElements.size()));
    }

    @Override
    public boolean isEmpty() {
        return pathElements.size() == 0;
    }

    @Override
    public boolean startsWith(Path path) {
        return pathElements.subList(0, path.size()).equals(path.getElements());
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        return pathElements.equals(((DefaultPath)o).pathElements);

    }

    @Override
    public int hashCode() {
        return pathElements.hashCode();
    }

    @Override
    public int size() {
        return pathElements.size();
    }

    @Override
    public List getElements() {
        return Collections.unmodifiableList(pathElements);
    }

    @Override
    public Path withIndices(int... indices) {
        List newPathElements = new ArrayList(pathElements.size() + indices.length);
        newPathElements.addAll(pathElements);
        for (int index : indices) {
            newPathElements.add(LIST_INDEX_PREFIX + index);
        }
        return new DefaultPath(newPathElements);
    }

    @Override
    public Path withElements(String... elements) {
        List newPathElements = new ArrayList(pathElements.size() + elements.length);
        newPathElements.addAll(pathElements);
        newPathElements.addAll(Lists.transform(Arrays.asList(elements), urlEncodeFunction));
        return new DefaultPath(newPathElements);
    }

    /** Create a Path object from a String produced by the Path#toString method. */

    public static Path fromEncodedPathString(String pathString) {
        String[] parts = StringUtils.split(pathString, PATH_DELIMITER_CHAR);
        return new DefaultPath(parts);
    }

    public static int getListIndex(String pathElement) {
        if (pathElement.isEmpty()) {
            throw new IllegalStateException("empty path");
        }

        if (!pathElement.startsWith(LIST_INDEX_PREFIX)) {
            throw new IllegalStateException("not a list index");
        }

        String rest = pathElement.substring(LIST_INDEX_PREFIX.length());
        try {
            return Integer.parseInt(rest);
        } catch (NumberFormatException ignored) {
            throw new IllegalStateException("bad format for list index");
        }
    }

    /**
     * Return true if all the given encoded path elements are list indexes.
     * @throws IllegalArgumentException if an empty path is found
     */
    public static boolean isList(Iterable encodedElements) {
        for (String element : encodedElements)  {
            if (!isListIndex(element)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Return true if the first element in this path is a list index.
     */
    private static boolean isListIndex(String pathElement) {
        if (!pathElement.startsWith(LIST_INDEX_PREFIX)) {
            return false;
        }
        String rest = pathElement.substring(LIST_INDEX_PREFIX.length());
        int index;
        try {
            index = Integer.parseInt(rest);
        } catch (NumberFormatException ignored) {
            return false;
        }
        return index >= 0;
    }

    private static class UrlEncode implements Function {
        @Nullable
        @Override
        public String apply(@Nullable String s) {
            if (s == null) {
                return null;
            }
            String encodedString;
            try {
                encodedString = URLEncoder.encode(s, "UTF-8");
            } catch (UnsupportedEncodingException ignored) {
                throw new AssertionError("UTF-8 is unknown?");
            }
            return encodedString;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy