/*
* Copyright 2017-present Facebook, 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.facebook.buck.rules;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.rules.coercer.CoercedTypeCache;
import com.facebook.buck.rules.coercer.ParamInfo;
import com.facebook.buck.rules.coercer.ParamInfoException;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.hash.HashCode;
import java.lang.reflect.Field;
public class AbstractNodeBuilderWithMutableArg<
TArg, TDescription extends Description<TArg>, TBuildRule extends BuildRule>
extends AbstractNodeBuilder<TArg, TDescription, TBuildRule> {
protected final TArg arg;
protected AbstractNodeBuilderWithMutableArg(TDescription description, BuildTarget target) {
super(description, target);
this.arg = makeArgWithDefaultValues(description);
}
protected AbstractNodeBuilderWithMutableArg(
TDescription description, BuildTarget target, ProjectFilesystem projectFilesystem) {
super(description, target, projectFilesystem);
this.arg = makeArgWithDefaultValues(description);
}
protected AbstractNodeBuilderWithMutableArg(
TDescription description,
BuildTarget target,
ProjectFilesystem projectFilesystem,
HashCode hashCode) {
super(description, target, projectFilesystem, hashCode);
this.arg = makeArgWithDefaultValues(description);
}
@Override
protected TArg getArgForPopulating() {
return arg;
}
@Override
protected TArg getPopulatedArg() {
return arg;
}
/**
* Populate optional fields of this constructor arg with their default values.
*
* <p>TODO(dwh): Remove this when all constructor args are immutables.
*/
private TArg makeArgWithDefaultValues(TDescription description) {
TArg arg;
try {
arg = description.getConstructorArgType().newInstance();
} catch (IllegalAccessException | InstantiationException e) {
throw new IllegalStateException(
String.format(
"Could not instantiate constructor arg %s for %s",
description.getConstructorArgType().getClass(), description.getClass()),
e);
}
try {
for (ParamInfo info :
CoercedTypeCache.INSTANCE
.getAllParamInfo(TYPE_COERCER_FACTORY, arg.getClass())
.values()) {
if (info.isOptional()) {
info.set(cellRoots, filesystem, target.getBasePath(), arg, null);
}
}
} catch (ParamInfoException error) {
throw new RuntimeException(error);
}
return arg;
}
@SuppressWarnings("unchecked")
@Override
protected final ImmutableSortedSet<BuildTarget> getDepsFromArg(TArg arg) {
// Not all rules have deps, but all rules call them deps. When they do, they're always optional.
// Grab them in the unsafest way I know.
try {
Field depsField = arg.getClass().getField("deps");
Object deps = depsField.get(arg);
if (deps == null) {
return ImmutableSortedSet.of();
}
// Here's a whole series of assumptions in one lump of a Bad Idea.
return (ImmutableSortedSet<BuildTarget>) deps;
} catch (ReflectiveOperationException ignored) {
// Field doesn't exist: no deps.
return ImmutableSortedSet.of();
}
}
}