blob: 7f49e5e4bcbe68e91f3b006c2cacf33df05eef46 [file] [log] [blame]
// Copyright 2016 The Bazel Authors. All rights reserved.
//
// 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.devtools.build.lib.rules.objc;
import static com.google.devtools.build.lib.rules.objc.AppleSkylarkCommon.BAD_SET_TYPE_ERROR;
import static com.google.devtools.build.lib.rules.objc.AppleSkylarkCommon.MISSING_KEY_ERROR;
import static com.google.devtools.build.lib.rules.objc.AppleSkylarkCommon.NOT_SET_ERROR;
import static com.google.devtools.build.lib.rules.objc.BundleableFile.BUNDLED_FIELD;
import static com.google.devtools.build.lib.rules.objc.BundleableFile.BUNDLE_PATH_FIELD;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.packages.StructImpl;
import com.google.devtools.build.lib.packages.StructProvider;
import com.google.devtools.build.lib.rules.objc.ObjcProvider.Key;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.EvalUtils;
import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
import com.google.devtools.build.lib.syntax.SkylarkType;
import com.google.devtools.build.lib.vfs.PathFragment;
/**
* A utility class for converting ObjcProvider values between java and skylark representation.
*/
public class ObjcProviderSkylarkConverters {
/**
* A map of possible NestedSet types to the converters that should define their treatment
* in translating between a java and skylark ObjcProvider.
*/
private static final ImmutableMap<Class<?>, Converter> CONVERTERS =
ImmutableMap.<Class<?>, Converter>builder()
.put(Artifact.class, new DirectConverter())
.put(String.class, new DirectConverter())
.put(PathFragment.class, new PathFragmentToStringConverter())
.put(SdkFramework.class, new SdkFrameworkToStringConverter())
.put(BundleableFile.class, new BundleableFileToStructConverter())
.build();
/**
* Returns a value for a skylark attribute given a java ObjcProvider key and value.
*/
public static Object convertToSkylark(Key<?> javaKey, NestedSet<?> javaValue) {
return CONVERTERS.get(javaKey.getType()).valueForSkylark(javaKey, javaValue);
}
/**
* Returns a value for a java ObjcProvider given a key and a corresponding skylark value.
*/
public static Iterable<?> convertToJava(Key<?> javaKey, Object skylarkValue) {
return CONVERTERS.get(javaKey.getType()).valueForJava(javaKey, skylarkValue);
}
/**
* Converts {@link PathFragment}s into a skylark-compatible nested set of path strings.
*/
public static SkylarkNestedSet convertPathFragmentsToSkylark(
Iterable<PathFragment> pathFragments) {
NestedSetBuilder<String> result = NestedSetBuilder.stableOrder();
for (PathFragment path : pathFragments) {
result.add(path.getSafePathString());
}
return SkylarkNestedSet.of(String.class, result.build());
}
/**
* A converter for ObjcProvider values.
*/
private static interface Converter {
/**
* Translates a java ObjcProvider value to a skylark ObjcProvider value.
*/
abstract Object valueForSkylark(Key<?> javaKey, NestedSet<?> javaValue);
/**
* Translates a skylark ObjcProvider value to a java ObjcProvider value.
*/
abstract Iterable<?> valueForJava(Key<?> javaKey, Object skylarkValue);
}
/**
* A converter that uses the same value for java and skylark.
*/
private static class DirectConverter implements Converter {
@Override
public Object valueForSkylark(Key<?> javaKey, NestedSet<?> javaValue) {
SkylarkType type = SkylarkType.of(javaKey.getType());
return SkylarkNestedSet.of(type, javaValue);
}
@Override
public Iterable<?> valueForJava(Key<?> javaKey, Object skylarkValue) {
validateTypes(skylarkValue, javaKey.getType(), javaKey.getSkylarkKeyName());
return ((SkylarkNestedSet) skylarkValue).toCollection();
}
}
/**
* A converter that that translates between a java PathFragment and a skylark string.
*/
private static class PathFragmentToStringConverter implements Converter {
@SuppressWarnings("unchecked")
@Override
public Object valueForSkylark(Key<?> javaKey, NestedSet<?> javaValue) {
return convertPathFragmentsToSkylark((NestedSet<PathFragment>) javaValue);
}
@SuppressWarnings("unchecked")
@Override
public Iterable<?> valueForJava(Key<?> javaKey, Object skylarkValue) {
validateTypes(skylarkValue, String.class, javaKey.getSkylarkKeyName());
NestedSetBuilder<PathFragment> result = NestedSetBuilder.stableOrder();
for (String path : ((SkylarkNestedSet) skylarkValue).toCollection(String.class)) {
result.add(PathFragment.create(path));
}
return result.build();
}
}
/**
* A converter that that translates between a java {@link SdkFramework} and a skylark string.
*/
private static class SdkFrameworkToStringConverter implements Converter {
@SuppressWarnings("unchecked")
@Override
public Object valueForSkylark(Key<?> javaKey, NestedSet<?> javaValue) {
NestedSetBuilder<String> result = NestedSetBuilder.stableOrder();
for (SdkFramework framework : (Iterable<SdkFramework>) javaValue) {
result.add(framework.getName());
}
return SkylarkNestedSet.of(String.class, result.build());
}
@SuppressWarnings("unchecked")
@Override
public Iterable<?> valueForJava(Key<?> javaKey, Object skylarkValue) {
validateTypes(skylarkValue, String.class, javaKey.getSkylarkKeyName());
NestedSetBuilder<SdkFramework> result = NestedSetBuilder.stableOrder();
for (String path : ((SkylarkNestedSet) skylarkValue).toCollection(String.class)) {
result.add(new SdkFramework(path));
}
return result.build();
}
}
/**
* A converter that that translates between a java BundleableFile and a skylark struct.
*/
private static class BundleableFileToStructConverter implements Converter {
@SuppressWarnings("unchecked")
@Override
public Object valueForSkylark(Key<?> javaKey, NestedSet<?> javaValue) {
NestedSetBuilder<StructImpl> result = NestedSetBuilder.stableOrder();
for (BundleableFile bundleableFile : (Iterable<BundleableFile>) javaValue) {
result.add(
StructProvider.STRUCT.create(
ImmutableMap.<String, Object>of(
BUNDLED_FIELD, bundleableFile.getBundled(),
BUNDLE_PATH_FIELD, bundleableFile.getBundlePath()),
"No such attribute '%s'"));
}
return SkylarkNestedSet.of(StructImpl.class, result.build());
}
@SuppressWarnings("unchecked")
@Override
public Iterable<?> valueForJava(Key<?> javaKey, Object skylarkValue) {
validateTypes(skylarkValue, StructImpl.class, javaKey.getSkylarkKeyName());
NestedSetBuilder<BundleableFile> result = NestedSetBuilder.stableOrder();
for (StructImpl struct : ((SkylarkNestedSet) skylarkValue).toCollection(StructImpl.class)) {
Artifact artifact;
String path;
try {
artifact = struct.getValue(BUNDLED_FIELD, Artifact.class);
path = struct.getValue(BUNDLE_PATH_FIELD, String.class);
} catch (EvalException e) {
throw new IllegalArgumentException(e.getMessage());
}
if (artifact == null) {
throw new IllegalArgumentException(String.format(MISSING_KEY_ERROR, BUNDLED_FIELD));
}
if (path == null) {
throw new IllegalArgumentException(String.format(MISSING_KEY_ERROR, BUNDLE_PATH_FIELD));
}
result.add(new BundleableFile(artifact, path));
}
return result.build();
}
}
/**
* Throws an error if the given object is not a nested set of the given type.
*/
private static void validateTypes(Object toCheck, Class<?> expectedSetType, String keyName) {
if (!(toCheck instanceof SkylarkNestedSet)) {
throw new IllegalArgumentException(
String.format(NOT_SET_ERROR, keyName, EvalUtils.getDataTypeName(toCheck)));
} else if (!((SkylarkNestedSet) toCheck).getContentType().canBeCastTo(expectedSetType)) {
throw new IllegalArgumentException(
String.format(
BAD_SET_TYPE_ERROR,
keyName,
EvalUtils.getDataTypeNameFromClass(expectedSetType),
EvalUtils.getDataTypeNameFromClass(
((SkylarkNestedSet) toCheck).getContentType().getType())));
}
}
}