All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.api.tools.framework.aspects.Feature Maven / Gradle / Ivy

There is a newer version: 0.0.8
Show newest version
/*
 * Copyright (C) 2016 Google 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 com.google.api.tools.framework.aspects;

import com.google.api.tools.framework.model.Diag;
import com.google.api.tools.framework.model.DiagCollector;
import com.google.api.tools.framework.model.Location;
import com.google.common.base.Preconditions;
import com.google.common.collect.DiscreteDomain;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Range;

import java.util.List;
import java.util.Set;

/**
 * Feature represents a platform behavior that may not be suitable for all services.  For example,
 * a feature may control whether an API proxy should automatically serve API description (discovery)
 * documents.
 *
 * A feature:
 * 
    *
  • has a name composed of '/' separated parts, starting with a namespace identifier
  • *
  • may be enabled or disabled by default at a particular config version
  • *
  • only be available at a limited range of config versions
  • *
  • may have child features that are enabled when the parent is enabled
  • *
*/ public class Feature { private final String featureName; private final List childBindings; private final Range supportedVersionRange; private final Range defaultVersionRange; private Feature(Builder builder) { Preconditions.checkArgument( !builder.supportedVersionRange.canonical(DiscreteDomain.integers()).isEmpty(), "supportedVersionRange must not be empty"); featureName = builder.featureName; childBindings = builder.childBindings.build(); supportedVersionRange = builder.supportedVersionRange; defaultVersionRange = builder.defaultVersionRange; } /** * Returns true if the feature is enabled by default in the given config version. */ boolean isDefaultIn(int configVersion) { return defaultVersionRange.contains(configVersion); } /** * Returns true if the feature is supported in the given config version. */ boolean isSupportedIn(int configVersion) { return supportedVersionRange.contains(configVersion); } /** * Adds or removes this feature from the given set of enabled features. Validates that this * feature is available in the given config version. * *

If this feature has children bound at the given config version, then those are accumulated * as well. */ void accumulate(Set enabled, boolean remove, int configVersion, DiagCollector diags, Location location) { if (!remove && !isSupportedIn(configVersion)) { diags.addDiag(Diag.error(location, "Feature %s not available in config version %s.", this, configVersion)); } if (!remove) { if (!enabled.add(featureName)) { diags.addDiag(Diag.warning(location, "Enabling feature %s had no effect because the feature was already enabled.", this)); } } else { if (!enabled.remove(featureName)) { diags.addDiag(Diag.warning(location, "Disabling feature %s had no effect because the feature was already disabled.", this)); } } for (ChildBinding binding : childBindings) { if (binding.range.contains(configVersion)) { binding.child.accumulate(enabled, remove, configVersion, diags, location); } } } /** * Retrieve all child names. (For validation in Features). */ Iterable getChildNames() { ImmutableList.Builder names = ImmutableList.builder(); for (ChildBinding binding : childBindings) { names.add(binding.child.getName()); } return names.build(); } void addSelfAndChildren(ImmutableMap.Builder featuresByNameBuilder) { featuresByNameBuilder.put(featureName, this); for (ChildBinding binding : childBindings) { binding.child.addSelfAndChildren(featuresByNameBuilder); } } /** * Gets the name of the feature. */ String getName() { return featureName; } @Override public String toString() { return featureName; } /** * Creates a new Builder with the given feature name. */ public static Builder builder(String featureName) { return new Builder(featureName); } private static class ChildBinding { private final Feature child; private final Range range; ChildBinding(Feature child, Range range) { this.child = child; this.range = range; } } /** * Builder for {@link Feature}s. */ public static class Builder { private final String featureName; private Range supportedVersionRange = Range.all(); private Range defaultVersionRange = Range.openClosed(0, 0); // Empty private ImmutableList.Builder childBindings = ImmutableList.builder(); private Builder(String featureName) { Preconditions.checkNotNull(featureName); this.featureName = featureName; } /** * Sets the range of versions where this feature is supported. */ public Builder withSupportedRange(Range versionRange) { Preconditions.checkNotNull(versionRange); supportedVersionRange = versionRange; return this; } /** * Sets the range of versions where this feature is enabled by default. */ public Builder withDefaultRange(Range versionRange) { Preconditions.checkNotNull(versionRange); defaultVersionRange = versionRange; return this; } /** * Automatically includes the given child when the config version is in the given range. */ public Builder withChild(Feature childFeature, Range versionRange) { Preconditions.checkNotNull(childFeature); Preconditions.checkNotNull(versionRange); Preconditions.checkArgument(childFeature.getName().startsWith(featureName + "/"), "Child feature name %s must start with parent feature name %s + '/'", childFeature.getName(), featureName); String childPart = childFeature.getName().substring(featureName.length() + 1); Preconditions.checkArgument( childPart.indexOf('/') == -1, "Child feature name must not contain '/'"); childBindings.add(new ChildBinding(childFeature, versionRange)); return this; } private static void checkEncloses( Range outer, Range inner, String format, Object... additionalArgs) { Object[] formatArgs = new Object[additionalArgs.length + 2]; formatArgs[0] = outer; formatArgs[1] = inner; System.arraycopy(additionalArgs, 0, formatArgs, 2, additionalArgs.length); Preconditions.checkArgument(outer.encloses(inner) || inner.isEmpty(), format, formatArgs); } /** * Returns the feature with the previously given configuration. */ public Feature build() { checkEncloses(supportedVersionRange, defaultVersionRange, "supportedVersionRange %s does not enclose defaultVersionRange %s on %s", featureName); for (ChildBinding binding : childBindings.build()) { checkEncloses(supportedVersionRange, binding.range, "parent supportedVersionRange %s does not include binding range %s for child %s", binding.child.featureName); checkEncloses(binding.child.supportedVersionRange, binding.range, "child supportedVersionRange %s does not include binding range %s for child %s", binding.child.featureName); } return new Feature(this); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy