org.kiji.schema.layout.impl.ShortColumnNameTranslator.java Source code

Java tutorial

Introduction

Here is the source code for org.kiji.schema.layout.impl.ShortColumnNameTranslator.java

Source

/**
 * (c) Copyright 2014 WibiData, Inc.
 *
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * 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.kiji.schema.layout.impl;

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

import com.google.common.base.Objects;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.hbase.util.Bytes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.kiji.annotations.ApiAudience;
import org.kiji.schema.KijiColumnName;
import org.kiji.schema.NoSuchColumnException;
import org.kiji.schema.hbase.HBaseColumnName;
import org.kiji.schema.layout.KijiColumnNameTranslator;
import org.kiji.schema.layout.KijiTableLayout;
import org.kiji.schema.layout.KijiTableLayout.LocalityGroupLayout;
import org.kiji.schema.layout.KijiTableLayout.LocalityGroupLayout.FamilyLayout;
import org.kiji.schema.layout.KijiTableLayout.LocalityGroupLayout.FamilyLayout.ColumnLayout;

/**
 * Translates between HTable and Kiji table column names.
 * <p/>
 * <p>This class defines a mapping between names of HBase HTable families/qualifiers and
 * Kiji table family/qualifiers using a custom base-64 encoding based on the ids of each
 * individual column.</p>
 */
@ApiAudience.Private
public class ShortColumnNameTranslator extends KijiColumnNameTranslator {
    private static final Logger LOG = LoggerFactory.getLogger(ShortColumnNameTranslator.class);

    /** Used to separate the Kiji family from the Kiji qualifier in an HBase qualifier. */
    public static final String SEPARATOR = ":";

    /** The table to translate names for. */
    protected final KijiTableLayout mTableLayout;

    /** A map from ColumnId to its locality group. */
    protected final Map<ColumnId, LocalityGroupLayout> mLocalityGroups;

    /**
     * Creates a new <code>ShortColumnNameTranslator</code> instance.
     *
     * @param tableLayout The layout of the table to translate column names for.
     */
    public ShortColumnNameTranslator(KijiTableLayout tableLayout) {
        mTableLayout = tableLayout;

        // Index the locality groups by their ColumnIds.
        mLocalityGroups = new HashMap<ColumnId, LocalityGroupLayout>();
        for (Map.Entry<ColumnId, String> entry : mTableLayout.getLocalityGroupIdNameMap().entrySet()) {
            final ColumnId lgId = entry.getKey();
            final String lgName = entry.getValue();
            mLocalityGroups.put(lgId, mTableLayout.getLocalityGroupMap().get(lgName));
        }
    }

    /** {@inheritDoc} */
    @Override
    public KijiColumnName toKijiColumnName(HBaseColumnName hbaseColumnName) throws NoSuchColumnException {
        LOG.debug("Translating HBase column name '{}' to Kiji column name...", hbaseColumnName);
        final ColumnId lgId = ColumnId.fromByteArray(hbaseColumnName.getFamily());
        final LocalityGroupLayout localityGroup = mLocalityGroups.get(lgId);
        if (null == localityGroup) {
            throw new NoSuchColumnException(String.format("No locality group with ID/HBase family: '%s'.",
                    hbaseColumnName.getFamilyAsString()));
        }

        final String[] parts = StringUtils
                .splitByWholeSeparatorPreserveAllTokens(hbaseColumnName.getQualifierAsString(), SEPARATOR, 2);
        if (2 != parts.length) {
            throw new NoSuchColumnException("Missing separator (" + SEPARATOR + ") from HBase qualifier ("
                    + hbaseColumnName.getQualifierAsString() + "). Unable to parse Kiji family/qualifier pair.");
        }

        final ColumnId familyId = ColumnId.fromString(parts[0]);
        final FamilyLayout kijiFamily = getKijiFamilyById(localityGroup, familyId);
        if (null == kijiFamily) {
            throw new NoSuchColumnException(String.format("No family with ColumnId '%s' in locality group '%s'.",
                    familyId, localityGroup.getDesc().getName()));
        }

        if (kijiFamily.isGroupType()) {
            // Group type family.
            final ColumnId columnId = ColumnId.fromString(parts[1]);
            final ColumnLayout kijiColumn = getKijiColumnById(kijiFamily, columnId);
            if (null == kijiColumn) {
                throw new NoSuchColumnException(String.format("No column with ColumnId '%s' in family '%s'.",
                        columnId, kijiFamily.getDesc().getName()));
            }
            final KijiColumnName result = new KijiColumnName(kijiFamily.getDesc().getName(),
                    kijiColumn.getDesc().getName());
            LOG.debug("Translated to Kiji group column '{}'.", result);
            return result;
        }

        // Map type family.
        assert kijiFamily.isMapType();
        final KijiColumnName result = new KijiColumnName(kijiFamily.getDesc().getName(), parts[1]);
        LOG.debug("Translated to Kiji map column '{}'.", result);
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public HBaseColumnName toHBaseColumnName(KijiColumnName kijiColumnName) throws NoSuchColumnException {
        final String familyName = kijiColumnName.getFamily();
        final FamilyLayout fLayout = mTableLayout.getFamilyMap().get(familyName);
        if (null == fLayout) {
            throw new NoSuchColumnException(kijiColumnName.toString());
        }
        final ColumnId lgId = fLayout.getLocalityGroup().getId();
        final ColumnId familyId = fLayout.getId();

        final String qualifier = kijiColumnName.getQualifier();
        if (fLayout.isGroupType()) {
            // Group type family.
            if (null != qualifier) {
                final ColumnId columnId = fLayout.getColumnIdNameMap().inverse().get(qualifier);
                if (null == columnId) {
                    throw new NoSuchColumnException(kijiColumnName.toString());
                }

                return new HBaseColumnName(toHBaseFamily(lgId), toHBaseQualifier(familyId, columnId));
            }

            // The caller is attempting to translate a Kiji column name that has only a family,
            // no qualifier.  This is okay.  We'll just return an HBaseColumnName with an empty
            // qualifier suffix.
            return new HBaseColumnName(toHBaseFamily(lgId), toHBaseQualifier(familyId, (String) null));
        } else {
            // Map type family.
            assert fLayout.isMapType();
            return new HBaseColumnName(toHBaseFamily(lgId), toHBaseQualifier(familyId, qualifier));
        }
    }

    /** {@inheritDoc} */
    @Override
    public byte[] toHBaseFamilyName(LocalityGroupLayout localityGroupLayout) {
        return localityGroupLayout.getId().toByteArray();
    }

    /**
     * Gets a Kiji column family from within a locality group by ID.
     *
     * @param localityGroup The locality group to look in.
     * @param familyId The ColumnId of the family to look for.
     * @return The family, or null if no family with the ID can be found within the locality group.
     */
    FamilyLayout getKijiFamilyById(LocalityGroupLayout localityGroup, ColumnId familyId) {
        final String familyName = localityGroup.getFamilyIdNameMap().get(familyId);
        return localityGroup.getFamilyMap().get(familyName);
    }

    /**
     * Gets a Kiji column from within a group-type family by ID.
     *
     * @param family The group-type family to look in.
     * @param columnId The ColumnId of the column to look for.
     * @return The column, or null if no column with the ID can be found within the family.
     */
    ColumnLayout getKijiColumnById(FamilyLayout family, ColumnId columnId) {
        final String columnName = family.getColumnIdNameMap().get(columnId);
        return family.getColumnMap().get(columnName);
    }

    /**
     * Turns a Kiji locality group into an HBase family.
     *
     * @param localityGroupId The ColumnID of the locality group.
     * @return The HBase family that should store data in this locality group.
     */
    private static byte[] toHBaseFamily(ColumnId localityGroupId) {
        return localityGroupId.toByteArray();
    }

    /**
     * Turns a group-type family and column into an HBase qualifier.
     *
     * @param familyId The ColumnId of the group-type family.
     * @param columnId The ColumnId of the Kiji column family.
     * @return The HBase qualifier that should store this Kiji column's data.
     */
    private static byte[] toHBaseQualifier(ColumnId familyId, ColumnId columnId) {
        StringBuilder hbaseQualifier = new StringBuilder().append(familyId.toString()).append(SEPARATOR)
                .append(columnId.toString());
        return Bytes.toBytes(hbaseQualifier.toString());
    }

    /**
     * Turns a map-type family and key into an HBase qualifier.
     *
     * @param familyId The ColumnId of the map-type family.
     * @param key The key into the map-type family.
     * @return The HBase qualifier that should store this Kiji column's data.
     */
    private static byte[] toHBaseQualifier(ColumnId familyId, String key) {
        StringBuilder hbaseQualifier = new StringBuilder().append(familyId.toString()).append(SEPARATOR);
        if (null != key) {
            hbaseQualifier.append(key);
        }
        return Bytes.toBytes(hbaseQualifier.toString());
    }

    /** {@inheritDoc} */
    @Override
    public KijiTableLayout getTableLayout() {
        return mTableLayout;
    }

    /** {@inheritDoc} */
    @Override
    public String toString() {
        return Objects.toStringHelper(this).add("table", mTableLayout.getName())
                .add("columnNameTranslator", mTableLayout.getDesc().getColumnNameTranslator().toString())
                .toString();
    }
}