blob: 28df7909a35ba6750995a96310f382c8dae63edf [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.NOT_SET_ERROR;
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.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())
.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 NestedSet<?> convertToJava(Key<?> javaKey, Object skylarkValue)
throws EvalException {
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 interface Converter {
/** Translates a java ObjcProvider value to a skylark ObjcProvider value. */
Object valueForSkylark(Key<?> javaKey, NestedSet<?> javaValue);
/** Translates a skylark ObjcProvider value to a java ObjcProvider value. */
NestedSet<?> valueForJava(Key<?> javaKey, Object skylarkValue) throws EvalException;
}
/**
* 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 NestedSet<?> valueForJava(Key<?> javaKey, Object skylarkValue) throws EvalException {
return nestedSetWithType(skylarkValue, javaKey.getType(), javaKey.getSkylarkKeyName());
}
}
/**
* 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);
}
@Override
public NestedSet<?> valueForJava(Key<?> javaKey, Object skylarkValue) throws EvalException {
NestedSet<String> nestedSet =
nestedSetWithType(skylarkValue, String.class, javaKey.getSkylarkKeyName());
NestedSetBuilder<PathFragment> result = NestedSetBuilder.stableOrder();
for (String path : nestedSet.toList()) {
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());
}
@Override
public NestedSet<?> valueForJava(Key<?> javaKey, Object skylarkValue) throws EvalException {
NestedSet<String> nestedSet =
nestedSetWithType(skylarkValue, String.class, javaKey.getSkylarkKeyName());
NestedSetBuilder<SdkFramework> result = NestedSetBuilder.stableOrder();
for (String path : nestedSet.toList()) {
result.add(new SdkFramework(path));
}
return result.build();
}
}
/** Throws an error if the given object is not a nested set of the given type. */
private static <T> NestedSet<T> nestedSetWithType(
Object toCheck, Class<T> expectedSetType, String keyName) throws EvalException {
if (toCheck instanceof SkylarkNestedSet) {
SkylarkNestedSet sns = (SkylarkNestedSet) toCheck;
try {
return sns.getSet(expectedSetType);
} catch (SkylarkNestedSet.TypeException exception) {
throw new EvalException(
null,
String.format(
BAD_SET_TYPE_ERROR,
keyName,
EvalUtils.getDataTypeNameFromClass(expectedSetType),
EvalUtils.getDataTypeName(toCheck, /*fullDetails=*/ true)),
exception);
}
} else {
throw new EvalException(
null, String.format(NOT_SET_ERROR, keyName, EvalUtils.getDataTypeName(toCheck)));
}
}
}