org.apache.hadoop.fs.impl.FlagSet Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.hadoop.fs.impl;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.StreamCapabilities;
import org.apache.hadoop.util.ConfigurationHelper;
import org.apache.hadoop.util.Preconditions;
import static java.util.Objects.requireNonNull;
import static org.apache.hadoop.util.ConfigurationHelper.mapEnumNamesToValues;
/**
* A set of flags, constructed from a configuration option or from a string,
* with the semantics of
* {@link ConfigurationHelper#parseEnumSet(String, String, Class, boolean)}
* and implementing {@link StreamCapabilities}.
*
* Thread safety: there is no synchronization on a mutable {@code FlagSet}.
* Once declared immutable, flags cannot be changed, so they
* becomes implicitly thread-safe.
*/
public final class FlagSet> implements StreamCapabilities {
/**
* Class of the enum.
* Used for duplicating the flags as java type erasure
* loses this information otherwise.
*/
private final Class enumClass;
/**
* Prefix for path capabilities probe.
*/
private final String prefix;
/**
* Set of flags.
*/
private final EnumSet flags;
/**
* Is the set immutable?
*/
private final AtomicBoolean immutable = new AtomicBoolean(false);
/**
* Mapping of prefixed flag names to enum values.
*/
private final Map namesToValues;
/**
* Create a FlagSet.
* @param enumClass class of enum
* @param prefix prefix (with trailing ".") for path capabilities probe
* @param flags flags. A copy of these are made.
*/
private FlagSet(final Class enumClass,
final String prefix,
@Nullable final EnumSet flags) {
this.enumClass = requireNonNull(enumClass, "null enumClass");
this.prefix = requireNonNull(prefix, "null prefix");
this.flags = flags != null
? EnumSet.copyOf(flags)
: EnumSet.noneOf(enumClass);
this.namesToValues = mapEnumNamesToValues(prefix, enumClass);
}
/**
* Get a copy of the flags.
*
* This is immutable.
* @return the flags.
*/
public EnumSet flags() {
return EnumSet.copyOf(flags);
}
/**
* Probe for the FlagSet being empty.
* @return true if there are no flags set.
*/
public boolean isEmpty() {
return flags.isEmpty();
}
/**
* Is a flag enabled?
* @param flag flag to check
* @return true if it is in the set of enabled flags.
*/
public boolean enabled(final E flag) {
return flags.contains(flag);
}
/**
* Check for mutability before any mutating operation.
* @throws IllegalStateException if the set is still mutable
*/
private void checkMutable() {
Preconditions.checkState(!immutable.get(),
"FlagSet is immutable");
}
/**
* Enable a flag.
* @param flag flag to enable.
*/
public void enable(final E flag) {
checkMutable();
flags.add(flag);
}
/**
* Disable a flag.
* @param flag flag to disable
*/
public void disable(final E flag) {
checkMutable();
flags.remove(flag);
}
/**
* Set a flag to the chosen value.
* @param flag flag
* @param state true to enable, false to disable.
*/
public void set(final E flag, boolean state) {
if (state) {
enable(flag);
} else {
disable(flag);
}
}
/**
* Is a flag enabled?
* @param capability string to query the stream support for.
* @return true if the capability maps to an enum value and
* that value is set.
*/
@Override
public boolean hasCapability(final String capability) {
final E e = namesToValues.get(capability);
return e != null && enabled(e);
}
/**
* Make immutable; no-op if already set.
*/
public void makeImmutable() {
immutable.set(true);
}
/**
* Is the FlagSet immutable?
* @return true iff the FlagSet is immutable.
*/
public boolean isImmutable() {
return immutable.get();
}
/**
* Get the enum class.
* @return the enum class.
*/
public Class getEnumClass() {
return enumClass;
}
@Override
public String toString() {
return "{" +
(flags.stream()
.map(Enum::name)
.collect(Collectors.joining(", ")))
+ "}";
}
/**
* Generate the list of capabilities.
* @return a possibly empty list.
*/
public List pathCapabilities() {
return namesToValues.keySet().stream()
.filter(this::hasCapability)
.collect(Collectors.toList());
}
/**
* Equality is based on the value of {@link #enumClass} and
* {@link #prefix} and the contents of the set, which must match.
*
* The immutability flag is not considered, nor is the
* {@link #namesToValues} map, though as that is generated from
* the enumeration and prefix, it is implicitly equal if the prefix
* and enumClass fields are equal.
* @param o other object
* @return true iff the equality condition is met.
*/
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
FlagSet> flagSet = (FlagSet>) o;
return Objects.equals(enumClass, flagSet.enumClass)
&& Objects.equals(prefix, flagSet.prefix)
&& Objects.equals(flags, flagSet.flags);
}
/**
* Hash code is based on the flags.
* @return a hash code.
*/
@Override
public int hashCode() {
return Objects.hashCode(flags);
}
/**
* Create a copy of the FlagSet.
* @return a new mutable instance with a separate copy of the flags
*/
public FlagSet copy() {
return new FlagSet<>(enumClass, prefix, flags);
}
/**
* Convert to a string which can be then set in a configuration.
* This is effectively a marshalled form of the flags.
* @return a comma separated list of flag names.
*/
public String toConfigurationString() {
return flags.stream()
.map(Enum::name)
.collect(Collectors.joining(", "));
}
/**
* Create a FlagSet.
* @param enumClass class of enum
* @param prefix prefix (with trailing ".") for path capabilities probe
* @param flags flags
* @param enum type
* @return a mutable FlagSet
*/
public static > FlagSet createFlagSet(
final Class enumClass,
final String prefix,
final EnumSet flags) {
return new FlagSet<>(enumClass, prefix, flags);
}
/**
* Create a FlagSet from a list of enum values.
* @param enumClass class of enum
* @param prefix prefix (with trailing ".") for path capabilities probe
* @param enabled varags list of flags to enable.
* @param enum type
* @return a mutable FlagSet
*/
@SafeVarargs
public static > FlagSet createFlagSet(
final Class enumClass,
final String prefix,
final E... enabled) {
final FlagSet flagSet = new FlagSet<>(enumClass, prefix, null);
Arrays.stream(enabled).forEach(flag -> {
if (flag != null) {
flagSet.enable(flag);
}
});
return flagSet;
}
/**
* Build a FlagSet from a comma separated list of values.
* Case independent.
* Special handling of "*" meaning: all values.
* @param enumClass class of enum
* @param conf configuration
* @param key key to look for
* @param ignoreUnknown should unknown values raise an exception?
* @param enumeration type
* @return a mutable FlagSet
* @throws IllegalArgumentException if one of the entries was unknown and ignoreUnknown is false,
* or there are two entries in the enum which differ only by case.
*/
public static > FlagSet buildFlagSet(
final Class enumClass,
final Configuration conf,
final String key,
final boolean ignoreUnknown) {
final EnumSet flags = conf.getEnumSet(key, enumClass, ignoreUnknown);
return createFlagSet(enumClass, key + ".", flags);
}
}