org.diqube.metadata.inspect.TableMetadataInspector.java Source code

Java tutorial

Introduction

Here is the source code for org.diqube.metadata.inspect.TableMetadataInspector.java

Source

/**
 * diqube: Distributed Query Base.
 *
 * Copyright (C) 2015 Bastian Gloeckle
 *
 * This file is part of diqube.
 *
 * diqube is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.diqube.metadata.inspect;

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

import org.diqube.metadata.create.FieldUtil;
import org.diqube.metadata.inspect.exception.ColumnNameInvalidException;
import org.diqube.name.RepeatedColumnNameGenerator;
import org.diqube.thrift.base.thrift.FieldMetadata;
import org.diqube.thrift.base.thrift.TableMetadata;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

/**
 * Utility class to help inspect {@link TableMetadata}.
 *
 * @author Bastian Gloeckle
 */
public class TableMetadataInspector {
    private RepeatedColumnNameGenerator repeatedColumnNameGenerator;
    private Map<String, FieldMetadata> fieldMetadata;

    /* package */ TableMetadataInspector(TableMetadata metadata,
            RepeatedColumnNameGenerator repeatedColumnNameGenerator) {
        this.repeatedColumnNameGenerator = repeatedColumnNameGenerator;

        fieldMetadata = new HashMap<>();
        for (FieldMetadata f : metadata.getFields())
            fieldMetadata.put(f.getFieldName(), f);
    }

    /**
     * Identifies all interesting {@link FieldMetadata} for a given columnName, as it might be used in a diql query.
     * 
     * <p>
     * The interesting {@link FieldMetadata}s are the ones of the field corresponding to the column itself and all its
     * parent fields.
     * 
     * @param columnName
     *          The column name as it might be used in a diql query. It might contain
     *          {@link RepeatedColumnNameGenerator#allEntriesIdentifyingSubstr()} etc.
     * @return A list of the {@link FieldMetadata} of all fields that are interesting. The list is ordered from the most
     *         generic (parent) to the most specific field.
     * @throws ColumnNameInvalidException
     *           If the columnName is not valid according to the metadata.
     */
    public List<FieldMetadata> findAllFieldMetadata(String columnName) throws ColumnNameInvalidException {
        List<FieldMetadata> m = new ArrayList<>();
        String fieldName = FieldUtil.toFieldName(columnName);
        if (columnName.indexOf("..") > -1 || columnName.startsWith(".") || columnName.endsWith("."))
            throw new ColumnNameInvalidException(
                    "Column name contains dots at at least one invalid position: " + columnName);

        String firstColName = columnName;
        boolean shouldBeLengthColumn = false;
        if (FieldUtil.isLengthColumn(fieldName)) {
            firstColName = columnName.substring(0, columnName.length() - FieldUtil.LENGTH.length());
            fieldName = fieldName.substring(0, fieldName.length() - FieldUtil.LENGTH.length());
            shouldBeLengthColumn = true;
        }

        findCurrentFieldMetadataAndParents(columnName, firstColName, fieldName, shouldBeLengthColumn, m);
        return Lists.reverse(m);
    }

    /**
     * Identifies the most specific interesting {@link FieldMetadata} for a given columnName, as it might be used in a
     * diql query.
     * 
     * @return {@link FieldMetadata} or <code>null</code>.
     */
    public FieldMetadata findFieldMetadata(String columnName) throws ColumnNameInvalidException {
        List<FieldMetadata> allMetadata = findAllFieldMetadata(columnName);
        if (allMetadata.isEmpty())
            return null;
        return Iterables.getLast(allMetadata);
    }

    /**
     * Processes the column name recursively and finds {@link FieldMetadata} of each field traversed.
     * 
     * @param fullColumnName
     *          The full column name passed to {@link #findAllFieldMetadata(String)}. For error reporting.
     * @param columnName
     *          The current column Name. This method "walks up" the hierarchy of fields.
     * @param fieldName
     *          The current field name as processed by {@link FieldUtil#toFieldName(String)} of the column.
     * @param shouldBeLengthColumn
     *          <code>true</code> if the current field is expected to be a [length] column, <code>false</code> if this
     *          would be an error. This is typically true for the full column = the [length] must only appear at the very
     *          end of a full column name
     * @param res
     *          The list to which to add {@link FieldMetadata}. Note that the order is from most specific field to most
     *          generic, as this method "walks up" the field hierarchy.
     * @throws ColumnNameInvalidException
     *           If column name is invalid.
     */
    private void findCurrentFieldMetadataAndParents(String fullColumnName, String columnName, String fieldName,
            boolean shouldBeLengthColumn, List<FieldMetadata> res) throws ColumnNameInvalidException {

        if (!shouldBeLengthColumn && FieldUtil.isLengthColumn(columnName))
            throw new ColumnNameInvalidException(
                    "Usage of " + FieldUtil.LENGTH + " at an illegal position in '" + fullColumnName + "'.");

        if (!fieldMetadata.containsKey(fieldName))
            throw new ColumnNameInvalidException("Field '" + fieldName + "' referenced in column name '"
                    + fullColumnName + "' is not available.");

        FieldMetadata expectedField = fieldMetadata.get(fieldName);

        boolean fieldIsRepeated = columnName.endsWith(repeatedColumnNameGenerator.repeatedColumnNameEndsWith())
                || shouldBeLengthColumn;
        if (expectedField.isRepeated() && !fieldIsRepeated)
            throw new ColumnNameInvalidException("Field '" + fieldName + "' is repeated, but '" + fullColumnName
                    + "' does not use it that way.");
        else if (!expectedField.isRepeated() && fieldIsRepeated)
            throw new ColumnNameInvalidException(
                    "Field '" + fieldName + "' is not repeated, but '" + fullColumnName + "' expects it to be.");

        res.add(expectedField);

        int lastDotColumn = columnName.lastIndexOf('.');
        int lastDotField = fieldName.lastIndexOf('.');
        if (lastDotColumn == -1 ^ lastDotField == -1)
            throw new ColumnNameInvalidException("Internal error while processing '" + fullColumnName + "'.");

        if (lastDotColumn == -1)
            return;

        String nextColumn = columnName.substring(0, lastDotColumn);
        String nextField = fieldName.substring(0, lastDotField);
        findCurrentFieldMetadataAndParents(fullColumnName, nextColumn, nextField, false, res);
    }
}