/* * Copyright © 2015 Cask Data, Inc. * * 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 co.cask.cdap.api.dataset.lib; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import javax.annotation.Nonnull; /** * This class describes how a dataset is partitioned, by means of the fields of a partition key and their types. * The ordering of fields in the partitioning matters: It is the order in which partition keys are indexed in the * meta data. As a best practice, a Partitioning should name the fields in the order of how frequently they are * used in partition filters, because partition filters that contain a condition for the first field in the * Partitioning perform best. */ public class Partitioning { /** * Describes the type of a partitioning field. */ public enum FieldType { STRING { @Override public String parse(String value) { return value; } }, LONG { @Override public Long parse(String value) { return Long.parseLong(value); } }, INT { @Override public Integer parse(String value) { return Integer.parseInt(value); } }; /** * Parse a string into a value of this field type. For example, {@link FieldType#INT} delegates this * to {@link Integer#parseInt}. * @param value the string to parse */ public abstract Comparable parse(String value); } private final Map<String, FieldType> fields; /** * Private constructor to force the use of the builder. */ private Partitioning(LinkedHashMap<String, FieldType> fields) { this.fields = Collections.unmodifiableMap(new LinkedHashMap<>(fields)); } /** * @return the type of a field, or null if that field is not declared for the partitioning */ public FieldType getFieldType(String fieldName) { return fields.get(fieldName); } /** * This returns a map associating all the fields of this partitioning with their respective types. Iterators * over the key set or the entry set of this map will yield the same order in which the fields were added to * the partitioning. * * @return all fields and their types */ public Map<String, FieldType> getFields() { return fields; } /** * @return a builder for a partitioning */ public static Builder builder() { return new Builder(); } /** * A builder for partitioning objects. */ public static class Builder { private final LinkedHashMap<String, FieldType> fields = new LinkedHashMap<>(); private Builder() { } /** * Add a field with a given name and type. * * @param name the field name * @param type the type of the field * * @throws java.lang.IllegalArgumentException if the field name is null, empty, or already exists, * or if the type is null. */ public Builder addField(@Nonnull String name, @Nonnull FieldType type) { if (name == null || name.isEmpty()) { throw new IllegalArgumentException("Field name cannot be null or empty."); } if (type == null) { throw new IllegalArgumentException("Field type cannot be null."); } if (fields.containsKey(name)) { throw new IllegalArgumentException(String.format("Field '%s' already exists in partitioning.", name)); } fields.put(name, type); return this; } /** * Add field of type STRING. * * @param name the field name * * @throws java.lang.IllegalArgumentException if the field name is null, empty, or already exists. */ public Builder addStringField(String name) { return addField(name, FieldType.STRING); } /** * Add field of type INT. * * @param name the field name * * @throws java.lang.IllegalArgumentException if the field name is null, empty, or already exists. */ public Builder addIntField(String name) { return addField(name, FieldType.INT); } /** * Add field of type LONG. * * @param name the field name * * @throws java.lang.IllegalArgumentException if the field name is null, empty, or already exists. */ public Builder addLongField(String name) { return addField(name, FieldType.LONG); } /** * Create the partitioning. * * @throws java.lang.IllegalStateException if no fields have been added */ public Partitioning build() { if (fields.isEmpty()) { throw new IllegalStateException("Partitioning cannot be empty."); } return new Partitioning(fields); } } }