blob: 1eaceda3eeb2bac4f00e8e4a1f53de371254bc2f [file] [log] [blame]
/*
* Copyright (C) 2017 The Dagger Authors.
*
* 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 dagger.internal.codegen;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import java.util.HashMap;
import java.util.Map;
/**
* A central repository of fields used to access any {@link ComponentRequirement} available to a
* component.
*/
final class ComponentRequirementFields {
// TODO(dpb,ronshapiro): refactor this and ComponentBindingExpressions into a
// HierarchicalComponentMap<K, V>, or perhaps this use a flattened ImmutableMap, built from its
// parents? If so, maybe make ComponentRequirementField.Factory create it.
/**
* A list of component requirement field maps. The first element contains the fields on this
* component; the second contains the fields owned by its parent; and so on.
*/
private final ImmutableList<Map<ComponentRequirement, ComponentRequirementField>>
componentRequirementFieldsMaps;
private ComponentRequirementFields(
ImmutableList<Map<ComponentRequirement, ComponentRequirementField>>
componentRequirementFieldsMaps) {
this.componentRequirementFieldsMaps = componentRequirementFieldsMaps;
}
ComponentRequirementFields() {
this(ImmutableList.of(newComponentRequirementsMap()));
}
/**
* Returns an expression for the {@code componentRequirement} to be used when implementing a
* component method. This may add a field to the component in order to reference the component
* requirement outside of the {@code initialize()} methods.
*/
CodeBlock getExpression(ComponentRequirement componentRequirement, ClassName requestingClass) {
return getField(componentRequirement).getExpression(requestingClass);
}
/**
* Returns an expression for the {@code componentRequirement} to be used only within {@code
* initialize()} methods, where the component builder is available.
*
* <p>When accessing this field from a subcomponent, this may cause a field to be initialized in
* the component that owns this {@link ComponentRequirement}.
*/
CodeBlock getExpressionDuringInitialization(
ComponentRequirement componentRequirement, ClassName requestingClass) {
return getField(componentRequirement).getExpressionDuringInitialization(requestingClass);
}
private ComponentRequirementField getField(ComponentRequirement componentRequirement) {
for (Map<ComponentRequirement, ComponentRequirementField> componentRequirementFieldsMap :
componentRequirementFieldsMaps) {
ComponentRequirementField field = componentRequirementFieldsMap.get(componentRequirement);
if (field != null) {
return field;
}
}
throw new IllegalStateException(
"no component requirement field found for " + componentRequirement);
}
/**
* Adds a component requirement field for a single component requirement owned by this component.
*/
// TODO(user): remove this method and create ComponentRequirementFields lazily, on demand.
void add(ComponentRequirementField field) {
componentRequirementFieldsMaps.get(0).put(field.componentRequirement(), field);
}
private static Map<ComponentRequirement, ComponentRequirementField>
newComponentRequirementsMap() {
return new HashMap<>();
}
/**
* Returns a new object representing the fields available from a child component of this one.
*/
ComponentRequirementFields forChildComponent() {
return new ComponentRequirementFields(
FluentIterable.of(newComponentRequirementsMap())
.append(componentRequirementFieldsMaps)
.toList());
}
}