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

com.ebuddy.cassandra.structure.Decomposer 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 static org.apache.commons.lang3.ObjectUtils.NULL;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.ebuddy.cassandra.Path;

/**
 * Support for decomposing complex objects into paths to simple objects.
 * Only the basic JSON structures are currently supported, i.e. Maps, Lists, Strings, Numbers, Booleans, and null.
 *
 * @author Eric Zoerner [email protected]
 */
public class Decomposer {
    private static final Decomposer INSTANCE = new Decomposer();

    private Decomposer() { }

    public static Decomposer get() {
        return INSTANCE;
    }

    /**
     * Decompose a map of arbitrarily complex structured objects into a map of
     * simple objects keyed by paths.
     *
     * @param structures the input map of paths to objects
     * @return a map of simple object keyed by paths
     * @throws IllegalArgumentException if there is an object of unsupported type in the structures
     * or if structures is null
     */
    public Map decompose(Map structures) {
        if (structures == null) {
            throw new IllegalArgumentException("structures is null");
        }
        Map decomposed = new HashMap(structures.size());

        for (Map.Entry entry : structures.entrySet()) {

            Object structure = entry.getValue();
            Path path = entry.getKey();

            // handle null specially by replacing with a Null token
            if (structure == null) {
                decomposed.put(path, NULL);
                continue;
            }

            if (Types.isSimple(structure)) {
                decomposed.put(path, structure);
                continue;
            }

            Map decomposedMap = decomposeStructure(structure);

            for (Map.Entry decomposedEntry : decomposedMap.entrySet()) {
                decomposed.put(path.concat(decomposedEntry.getKey()), decomposedEntry.getValue());
            }
        }
        return decomposed;
    }

    //////// Private Methods //////////

    @SuppressWarnings("ChainOfInstanceofChecks")
    private Map decomposeStructure(Object structure) {
        Map decomposedMap;
        if (structure instanceof Map) {
            decomposedMap = normalizeMap((Map)structure);
        } else if (structure instanceof List) {
            decomposedMap = normalizeList((List)structure);
        } else {
            throw new IllegalArgumentException("Unsupported data type: " + structure.getClass().getSimpleName());
        }
        return decompose(decomposedMap);
    }

    private Map normalizeMap(Map map) {
        Map normalized = new HashMap(map.size());
        for (Map.Entry entry : map.entrySet()) {

            Object key = entry.getKey();
            if (!Types.isSimple(key)) {
                throw new IllegalArgumentException(String.format("map key of type %s not supported",
                                                                 key.getClass().getSimpleName()));
            }
            Path keyPath = key instanceof Path ? (Path)key : DefaultPath.fromStrings(key.toString());

            Object value = entry.getValue();
            normalized.put(keyPath, value);
        }
        return normalized;
    }

    private Map normalizeList(List list) {
        // get type info for list
        // TODO: if this is a set of simple types, then encode the set into the keys using #
        String type = (String)list.get(0);
        /// get list itself
        List listItself = (List)list.get(1);

        Map normalized = new HashMap(listItself.size());
        for (int i = 0; i < listItself.size(); i++) {
            normalized.put(DefaultPath.fromIndex(i), listItself.get(i));
        }
        // add terminator column, issue #20
        normalized.put(DefaultPath.fromIndex(listItself.size()), Types.LIST_TERMINATOR_VALUE);

        return normalized;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy