Java tutorial
/** * 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); } }