下面列出了com.google.common.collect.ListMultimap#get ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
public static ImmutableSet<ConfiguredTargetKey> findImplicitDeps(RuleContext ruleContext) {
Set<ConfiguredTargetKey> maybeImplicitDeps = CompactHashSet.create();
Set<ConfiguredTargetKey> explicitDeps = CompactHashSet.create();
AttributeMap attributes = ruleContext.attributes();
ListMultimap<String, ConfiguredTargetAndData> targetMap =
ruleContext.getConfiguredTargetAndDataMap();
for (String attrName : attributes.getAttributeNames()) {
List<ConfiguredTargetAndData> attrValues = targetMap.get(attrName);
if (attrValues != null && !attrValues.isEmpty()) {
if (attributes.isAttributeValueExplicitlySpecified(attrName)) {
addLabelsAndConfigs(explicitDeps, attrValues);
} else {
addLabelsAndConfigs(maybeImplicitDeps, attrValues);
}
}
}
return ImmutableSet.copyOf(Sets.difference(maybeImplicitDeps, explicitDeps));
}
@Test
public void test_load_all_curves() {
ListMultimap<LocalDate, RatesCurveGroup> allGroups = RatesCurvesCsvLoader.loadAllDates(
ResourceLocator.of(GROUPS_1),
ResourceLocator.of(SETTINGS_1),
ImmutableList.of(ResourceLocator.of(CURVES_1), ResourceLocator.of(CURVES_2), ResourceLocator.of(CURVES_3)));
assertThat(allGroups.size()).isEqualTo(2);
assertCurves(allGroups.get(CURVE_DATE));
List<RatesCurveGroup> curves3 = allGroups.get(CURVE_DATE_CURVES_3);
assertThat(curves3).hasSize(1);
RatesCurveGroup group = curves3.get(0);
// All curve points are set to 0 in test data to ensure these are really different curve instances
Curve usdDisc = group.findDiscountCurve(Currency.USD).get();
InterpolatedNodalCurve usdDiscNodal = (InterpolatedNodalCurve) usdDisc;
assertThat(usdDiscNodal.getMetadata().getCurveName()).isEqualTo(CurveName.of("USD-Disc"));
assertThat(usdDiscNodal.getYValues().equalZeroWithTolerance(0d)).isTrue();
Curve usd3ml = group.findForwardCurve(IborIndices.USD_LIBOR_3M).get();
InterpolatedNodalCurve usd3mlNodal = (InterpolatedNodalCurve) usd3ml;
assertThat(usd3mlNodal.getMetadata().getCurveName()).isEqualTo(CurveName.of("USD-3ML"));
assertThat(usd3mlNodal.getYValues().equalZeroWithTolerance(0d)).isTrue();
}
@NotNull
public static <T extends GenomePosition> Map<Chromosome, ChromosomeLength> create(int windowSize,
@NotNull final ListMultimap<Chromosome, T> position) {
final Map<Chromosome, ChromosomeLength> result = Maps.newHashMap();
for (Chromosome chromosome : position.keySet()) {
List<T> chromosomeList = position.get(chromosome);
if (!chromosomeList.isEmpty()) {
T last = chromosomeList.get(chromosomeList.size() - 1);
final long max = last.position() + windowSize - 1;
final String contig = last.chromosome();
result.put(chromosome, ImmutableChromosomeLength.builder().chromosome(contig).length(max).build());
}
}
return result;
}
private void checkQuotaLimitNamesAreUnique(Quota quota) {
ListMultimap<String, QuotaLimit> limitNameCounts = LinkedListMultimap.create();
for (QuotaLimit limit : quota.getLimitsList()) {
limitNameCounts.put(limit.getName(), limit);
}
for (String limitName : limitNameCounts.keySet()) {
List<QuotaLimit> limits = limitNameCounts.get(limitName);
if (limits.size() > 1) {
error(
MessageLocationContext.create(Iterables.getLast(limits), "name"),
"There are %d quota limits with name '%s'.",
limits.size(),
limitName);
}
}
}
@Test
public void testInspect() {
ListMultimap<CustomizationType, Customization> customizationMap = loadCustomizationMap();
List<Customization> curCustomizations = customizationMap.get(CustomizationType.INFO);
for(Customization cur : curCustomizations) {
System.out.println(ReflectionToStringBuilder.toString(cur, ToStringStyle.MULTI_LINE_STYLE));
}
}
/**
*/
private void findCycleFreeRec(ListMultimap<FnID, FnID> candidates,
Set<FnID> visited, Set<FnID> toRemove,
ListMultimap<FnID, FnID> newCandidates,
StackLite<FnID> callStack, FnID curr) {
List<FnID> callers = candidates.get(curr);
if (callers == null || callers.size() == 0) {
// not a candidate for inlining
return;
}
if (visited.contains(curr))
return; // Don't process again
visited.add(curr);
for (FnID caller: callers) {
if (callStack.contains(caller) || caller.equals(curr)) {
// Adding this would create cycle, do nothing
if (alwaysInline.contains(curr)) {
Logging.getSTCLogger().warn("Recursive loop of functions with no "
+ " other callers: " + curr + " " + callStack);
}
} else if (blacklist.contains(Pair.create(caller, curr))) {
// Already inlined, don't do it again
} else {
// Mark for inlining
newCandidates.put(curr, caller);
callStack.push(curr);
findCycleFreeRec(candidates, visited, toRemove, newCandidates, callStack,
caller);
callStack.pop();
}
}
}
private static SwapTrade parseVariableFixedRate(SwapTrade trade, List<CsvRow> variableRows) {
ImmutableList<SwapLeg> legs = trade.getProduct().getLegs();
ListMultimap<Integer, ValueStep> steps =
extractSteps(variableRows, legs, FIXED_RATE_FIELD, str -> LoaderUtils.parseDoublePercent(str));
if (steps.isEmpty()) {
return trade;
}
// adjust the trade, inserting the variable element
ImmutableList.Builder<SwapLeg> legBuilder = ImmutableList.builder();
for (int i = 0; i < legs.size(); i++) {
SwapLeg swapLeg = legs.get(i);
List<ValueStep> legSteps = steps.get(i);
if (!legSteps.isEmpty() && swapLeg instanceof RateCalculationSwapLeg) {
RateCalculationSwapLeg leg = (RateCalculationSwapLeg) swapLeg;
if (leg.getCalculation() instanceof FixedRateCalculation) {
FixedRateCalculation baseCalc = (FixedRateCalculation) leg.getCalculation();
swapLeg = leg.toBuilder()
.calculation(baseCalc.toBuilder()
.rate(ValueSchedule.of(baseCalc.getRate().getInitialValue(), legSteps))
.build())
.build();
}
}
legBuilder.add(swapLeg);
}
return replaceLegs(trade, legBuilder.build());
}
public void print(StyledTextOutput output) {
final List<Task> tasks = sort(selection.getTasks());
output.text("Detailed task information for ").withStyle(UserInput).println(taskPath);
final ListMultimap<Class, Task> classListMap = groupTasksByType(tasks);
final Set<Class> classes = classListMap.keySet();
boolean multipleClasses = classes.size() > 1;
final List<Class> sortedClasses = sort(classes, new Comparator<Class>() {
public int compare(Class o1, Class o2) {
return o1.getSimpleName().compareTo(o2.getSimpleName());
}
});
for (Class clazz : sortedClasses) {
output.println();
final List<Task> tasksByType = classListMap.get(clazz);
final LinePrefixingStyledTextOutput pathOutput = createIndentedOutput(output, INDENT);
pathOutput.println(tasksByType.size() > 1 ? "Paths" : "Path");
for (Task task : tasksByType) {
pathOutput.withStyle(UserInput).println(task.getPath());
}
output.println();
final LinePrefixingStyledTextOutput typeOutput = createIndentedOutput(output, INDENT);
typeOutput.println("Type");
typeOutput.withStyle(UserInput).text(clazz.getSimpleName());
typeOutput.println(String.format(" (%s)", clazz.getName()));
printlnCommandlineOptions(output, tasksByType);
output.println();
printTaskDescription(output, tasksByType);
if (multipleClasses) {
output.println();
output.println("----------------------");
}
}
}
public static void examineCorpus(Corpus corpus) {
ListMultimap<FeatureVectorAsObject, Integer> wsByFeatureVectorGroup = ArrayListMultimap.create();
ListMultimap<FeatureVectorAsObject, Integer> hposByFeatureVectorGroup = ArrayListMultimap.create();
int numContexts = corpus.featureVectors.size();
for (int i = 0; i<numContexts; i++) {
int[] X = corpus.featureVectors.get(i);
int y1 = corpus.injectWhitespace.get(i);
int y2 = corpus.hpos.get(i);
wsByFeatureVectorGroup.put(new FeatureVectorAsObject(X, Trainer.FEATURES_INJECT_WS), y1);
hposByFeatureVectorGroup.put(new FeatureVectorAsObject(X, Trainer.FEATURES_HPOS), y2);
}
List<Double> wsEntropies = new ArrayList<>();
List<Double> hposEntropies = new ArrayList<>();
for (FeatureVectorAsObject x : wsByFeatureVectorGroup.keySet()) {
List<Integer> cats = wsByFeatureVectorGroup.get(x);
List<Integer> cats2 = hposByFeatureVectorGroup.get(x);
HashBag<Integer> wsCats = getCategoriesBag(cats);
HashBag<Integer> hposCats = getCategoriesBag(cats2);
double wsEntropy = getNormalizedCategoryEntropy(getCategoryRatios(wsCats.values()));
double hposEntropy = getNormalizedCategoryEntropy(getCategoryRatios(hposCats.values()));
wsEntropies.add(wsEntropy);
hposEntropies.add(hposEntropy);
System.out.printf("%130s : %s,%s %s,%s\n", x,
wsCats, wsEntropy,
hposCats, hposEntropy);
}
System.out.println("MEAN "+mean(wsEntropies));
System.out.println("MEAN "+mean(hposEntropies));
float contextRichness = wsEntropies.size()/(float) numContexts; // 0..1 where 1 means every token had different context
System.out.println("Context richness = "+contextRichness+
" uniq ctxs="+wsEntropies.size()+", nctxs="+numContexts);
}
/**
* Finds all split work units in the input collection and merges the file parts into the expected output files.
* @param fs {@link FileSystem} where file parts exist.
* @param workUnits Collection of {@link WorkUnitState}s possibly containing split work units.
* @return The collection of {@link WorkUnitState}s where split work units for each file have been merged.
* @throws IOException
*/
public static Collection<WorkUnitState> mergeAllSplitWorkUnits(FileSystem fs, Collection<WorkUnitState> workUnits)
throws IOException {
ListMultimap<CopyableFile, WorkUnitState> splitWorkUnitsMap = ArrayListMultimap.create();
for (WorkUnitState workUnit : workUnits) {
if (isSplitWorkUnit(workUnit)) {
CopyableFile copyableFile = (CopyableFile) CopySource.deserializeCopyEntity(workUnit);
splitWorkUnitsMap.put(copyableFile, workUnit);
}
}
for (CopyableFile file : splitWorkUnitsMap.keySet()) {
log.info(String.format("Merging split file %s.", file.getDestination()));
WorkUnitState oldWorkUnit = splitWorkUnitsMap.get(file).get(0);
Path outputDir = FileAwareInputStreamDataWriter.getOutputDir(oldWorkUnit);
CopyEntity.DatasetAndPartition datasetAndPartition =
file.getDatasetAndPartition(CopySource.deserializeCopyableDataset(oldWorkUnit));
Path parentPath = FileAwareInputStreamDataWriter.getOutputFilePath(file, outputDir, datasetAndPartition)
.getParent();
WorkUnitState newWorkUnit = mergeSplits(fs, file, splitWorkUnitsMap.get(file), parentPath);
for (WorkUnitState wu : splitWorkUnitsMap.get(file)) {
// Set to committed so that task states will not fail
wu.setWorkingState(WorkUnitState.WorkingState.COMMITTED);
workUnits.remove(wu);
}
workUnits.add(newWorkUnit);
}
return workUnits;
}
@NonNull
public Map<String, ResourceValue> getConfiguredResources(
@NonNull Map<ResourceType, ListMultimap<String, ResourceItem>> itemMap,
@NonNull ResourceType type,
@NonNull FolderConfiguration referenceConfig) {
// get the resource item for the given type
ListMultimap<String, ResourceItem> items = itemMap.get(type);
if (items == null) {
return Maps.newHashMap();
}
Set<String> keys = items.keySet();
// create the map
Map<String, ResourceValue> map = Maps.newHashMapWithExpectedSize(keys.size());
for (String key : keys) {
List<ResourceItem> keyItems = items.get(key);
// look for the best match for the given configuration
// the match has to be of type ResourceFile since that's what the input list contains
ResourceItem match = (ResourceItem) referenceConfig.findMatchingConfigurable(keyItems);
if (match != null) {
ResourceValue value = match.getResourceValue(mFramework);
if (value != null) {
map.put(match.getName(), value);
}
}
}
return map;
}
private static void sendChunkToError(
String[] ids,
ListMultimap<String, Record> recordsToRetrieve,
ToErrorContext context,
ApiFault e
) {
for (String id : ids) {
for (Record record : recordsToRetrieve.get(id)) {
context.toError(record, e);
}
}
}
@SuppressWarnings("unchecked")
@Override
public PhysicalOperator visitGroupScan(@SuppressWarnings("rawtypes") GroupScan groupScan, IndexedFragmentNode iNode) throws ExecutionSetupException {
ListMultimap<Integer, CompleteWork> splits = splitSets.get(groupScan);
Preconditions.checkNotNull(splits);
List<CompleteWork> work = splits.get(iNode.getMinorFragmentId());
Preconditions.checkNotNull(work);
PhysicalOperator child = groupScan.getSpecificScan(work);
iNode.addAllocation(groupScan);
return child;
}
@Nullable
public List<ResourceItem> getResourceItem(@NonNull ResourceType resourceType,
@NonNull String resourceName) {
synchronized (ITEM_MAP_LOCK) {
ListMultimap<String, ResourceItem> map = getMap(resourceType, false);
if (map != null) {
return map.get(resourceName);
}
}
return null;
}
public void print(StyledTextOutput output) {
final List<Task> tasks = sort(selection.getTasks());
output.text("Detailed task information for ").withStyle(UserInput).println(taskPath);
final ListMultimap<Class, Task> classListMap = groupTasksByType(tasks);
final Set<Class> classes = classListMap.keySet();
boolean multipleClasses = classes.size() > 1;
final List<Class> sortedClasses = sort(classes, new Comparator<Class>() {
public int compare(Class o1, Class o2) {
return o1.getSimpleName().compareTo(o2.getSimpleName());
}
});
for (Class clazz : sortedClasses) {
output.println();
final List<Task> tasksByType = classListMap.get(clazz);
final LinePrefixingStyledTextOutput pathOutput = createIndentedOutput(output, INDENT);
pathOutput.println(tasksByType.size() > 1 ? "Paths" : "Path");
for (Task task : tasksByType) {
pathOutput.withStyle(UserInput).println(task.getPath());
}
output.println();
final LinePrefixingStyledTextOutput typeOutput = createIndentedOutput(output, INDENT);
typeOutput.println("Type");
typeOutput.withStyle(UserInput).text(clazz.getSimpleName());
typeOutput.println(String.format(" (%s)", clazz.getName()));
printlnCommandlineOptions(output, tasksByType);
output.println();
printTaskDescription(output, tasksByType);
if (multipleClasses) {
output.println();
output.println("----------------------");
}
}
}
@NotNull
public static <T,U> ListMultimap<T, U> filterEntries(@NotNull final ListMultimap<T, U> map, @NotNull final Predicate<U> predicate) {
final ListMultimap<T, U> result = ArrayListMultimap.create();
for (T key : map.keySet()) {
for (U value : map.get(key)) {
if (predicate.test(value)) {
result.put(key, value);
}
}
}
return result;
}
private void validateProvidesOverrides(TypeElement subject,
ValidationReport.Builder<TypeElement> builder,
ListMultimap<String, ExecutableElement> allMethodsByName,
ListMultimap<String, ExecutableElement> bindingMethodsByName) {
// For every @Provides method, confirm it overrides nothing *and* nothing overrides it.
// Consider the following hierarchy:
// class Parent {
// @Provides Foo a() {}
// @Provides Foo b() {}
// Foo c() {}
// }
// class Child extends Parent {
// @Provides Foo a() {}
// Foo b() {}
// @Provides Foo c() {}
// }
// In each of those cases, we want to fail. "a" is clear, "b" because Child is overriding
// a method marked @Provides in Parent, and "c" because Child is defining an @Provides
// method that overrides Parent.
TypeElement currentClass = subject;
TypeMirror objectType = elements.getTypeElement(Object.class.getCanonicalName()).asType();
// We keep track of methods that failed so we don't spam with multiple failures.
Set<ExecutableElement> failedMethods = Sets.newHashSet();
while (!types.isSameType(currentClass.getSuperclass(), objectType)) {
currentClass = MoreElements.asType(types.asElement(currentClass.getSuperclass()));
List<ExecutableElement> superclassMethods =
ElementFilter.methodsIn(currentClass.getEnclosedElements());
for (ExecutableElement superclassMethod : superclassMethods) {
String name = superclassMethod.getSimpleName().toString();
// For each method in the superclass, confirm our @Provides methods don't override it
for (ExecutableElement providesMethod : bindingMethodsByName.get(name)) {
if (!failedMethods.contains(providesMethod)
&& elements.overrides(providesMethod, superclassMethod, subject)) {
failedMethods.add(providesMethod);
builder.addItem(String.format(PROVIDES_METHOD_OVERRIDES_ANOTHER,
methodClass.getSimpleName(), methodSignatureFormatter.format(superclassMethod)),
providesMethod);
}
}
// For each @Provides method in superclass, confirm our methods don't override it.
if (isAnnotationPresent(superclassMethod, methodClass)) {
for (ExecutableElement method : allMethodsByName.get(name)) {
if (!failedMethods.contains(method)
&& elements.overrides(method, superclassMethod, subject)) {
failedMethods.add(method);
builder.addItem(String.format(METHOD_OVERRIDES_PROVIDES_METHOD,
methodClass.getSimpleName(), methodSignatureFormatter.format(superclassMethod)),
method);
}
}
}
allMethodsByName.put(superclassMethod.getSimpleName().toString(), superclassMethod);
}
}
}
/**
* Registers actions to create a multi-arch Apple binary.
*
* @param extraLinkArgs the extra linker args to add to link actions linking single-architecture
* binaries together
* @param dependencySpecificConfigurations a set of {@link DependencySpecificConfiguration} that
* corresponds to child configurations for this target. Can be obtained via {@link
* #getDependencySpecificConfigurations}
* @param extraLinkInputs the extra linker inputs to be made available during link actions
* @param cpuToDepsCollectionMap a multimap from dependency configuration to the list of
* provider collections which are propagated from the dependencies of that configuration
* @param outputMapCollector a map to which output groups created by compile action generation are
* added
* @return a set containing all single-architecture binaries that are linked from this call
* @throws RuleErrorException if there are attribute errors in the current rule context
*/
public NestedSet<Artifact> registerActions(
ExtraLinkArgs extraLinkArgs,
Set<DependencySpecificConfiguration> dependencySpecificConfigurations,
Iterable<Artifact> extraLinkInputs,
ListMultimap<String, TransitiveInfoCollection> cpuToDepsCollectionMap,
Map<String, NestedSet<Artifact>> outputMapCollector)
throws RuleErrorException, InterruptedException {
NestedSetBuilder<Artifact> binariesToLipo =
NestedSetBuilder.<Artifact>stableOrder();
for (DependencySpecificConfiguration dependencySpecificConfiguration :
dependencySpecificConfigurations) {
IntermediateArtifacts intermediateArtifacts =
ObjcRuleClasses.intermediateArtifacts(
ruleContext, dependencySpecificConfiguration.config());
Iterable<TransitiveInfoCollection> infoCollections =
cpuToDepsCollectionMap.get(dependencySpecificConfiguration.config().getCpu());
J2ObjcMappingFileProvider j2ObjcMappingFileProvider =
J2ObjcMappingFileProvider.union(
getTypedProviders(infoCollections, J2ObjcMappingFileProvider.class));
J2ObjcEntryClassProvider j2ObjcEntryClassProvider =
new J2ObjcEntryClassProvider.Builder()
.addTransitive(getTypedProviders(infoCollections, J2ObjcEntryClassProvider.class))
.build();
binariesToLipo.add(intermediateArtifacts.strippedSingleArchitectureBinary());
ObjcProvider objcProvider = dependencySpecificConfiguration.objcLinkProvider();
CompilationArtifacts compilationArtifacts = new CompilationArtifacts.Builder()
.setIntermediateArtifacts(ObjcRuleClasses.intermediateArtifacts(
ruleContext, dependencySpecificConfiguration.config()))
.build();
CompilationSupport compilationSupport =
new CompilationSupport.Builder()
.setRuleContext(ruleContext)
.setConfig(dependencySpecificConfiguration.config())
.setToolchainProvider(dependencySpecificConfiguration.toolchain())
.setOutputGroupCollector(outputMapCollector)
.build();
compilationSupport
.registerCompileAndArchiveActions(compilationArtifacts, ObjcCompilationContext.EMPTY)
.registerLinkActions(
objcProvider,
j2ObjcMappingFileProvider,
j2ObjcEntryClassProvider,
extraLinkArgs,
extraLinkInputs)
.validateAttributes();
ruleContext.assertNoErrors();
}
return binariesToLipo.build();
}
@Override
public List<CloudVmMetaDataStatus> collect(AuthenticatedContext authenticatedContext, List<CloudResource> resources, List<CloudInstance> vms,
List<CloudInstance> knownInstances) {
try {
YarnClient yarnClient = yarnClientUtil.createYarnClient(authenticatedContext);
CloudResource yarnApplication = getYarnApplcationResource(resources);
ApplicationDetailRequest applicationDetailRequest = new ApplicationDetailRequest();
applicationDetailRequest.setName(yarnApplication.getName());
ResponseContext responseContext = yarnClient.getApplicationDetail(applicationDetailRequest);
if (responseContext.getStatusCode() == YarnResourceConstants.HTTP_SUCCESS) {
ApplicationDetailResponse applicationDetailResponse = (ApplicationDetailResponse) responseContext.getResponseObject();
ListMultimap<String, CloudInstance> groupInstancesByInstanceGroup = groupInstancesByInstanceGroup(vms);
ListMultimap<String, Container> groupContainersByInstanceGroup = groupContainersByInstanceGroup(applicationDetailResponse.getContainers());
List<CloudVmMetaDataStatus> cloudVmMetaDataStatuses = Lists.newArrayList();
for (String groupName : groupContainersByInstanceGroup.keySet()) {
List<CloudInstance> groupInstances = groupInstancesByInstanceGroup.get(groupName);
List<Container> groupContainers = groupContainersByInstanceGroup.get(groupName);
Map<String, CloudInstance> mapByInstanceId = mapByInstanceId(groupInstances);
Queue<CloudInstance> untrackedInstances = untrackedInstances(groupInstances);
for (Container container : groupContainers) {
String containerId = container.getId();
CloudInstance cloudInstance = mapByInstanceId.get(containerId);
if (cloudInstance == null) {
if (!untrackedInstances.isEmpty()) {
cloudInstance = untrackedInstances.remove();
cloudInstance = new CloudInstance(containerId, cloudInstance.getTemplate(), cloudInstance.getAuthentication());
}
}
if (cloudInstance != null) {
String ipAddress = container.getIp();
CloudInstanceMetaData md = new CloudInstanceMetaData(ipAddress, ipAddress);
CloudVmInstanceStatus cloudVmInstanceStatus = new CloudVmInstanceStatus(cloudInstance, InstanceStatus.CREATED);
CloudVmMetaDataStatus cloudVmMetaDataStatus = new CloudVmMetaDataStatus(cloudVmInstanceStatus, md);
cloudVmMetaDataStatuses.add(cloudVmMetaDataStatus);
}
}
}
return cloudVmMetaDataStatuses;
} else {
ApplicationErrorResponse errorResponse = responseContext.getResponseError();
throw new CloudConnectorException(String.format("Failed to get yarn application details: HTTP Return: %d Error: %s",
responseContext.getStatusCode(), errorResponse == null ? "unknown" : errorResponse.getDiagnostics()));
}
} catch (MalformedURLException ex) {
throw new CloudConnectorException("Failed to get yarn application details", ex);
}
}
/**
* @param configAncestorSet is the chain of configs that have led to this one getting expanded.
* This should only contain the configs that expanded, recursively, to this one, and should
* not contain "siblings," as it is used to detect cycles. {@code build:foo --config=bar},
* {@code build:bar --config=foo}, is a cycle, detected because this list will be [foo, bar]
* when we find another 'foo' to expand. However, {@code build:foo --config=bar}, {@code
* build:foo --config=bar} is not a cycle just because bar is expanded twice, and the 1st bar
* should not be in the parents list of the second bar.
* @param longestChain will be populated with the longest inheritance chain of configs.
*/
private static List<String> getExpansion(
ListMultimap<String, RcChunkOfArgs> commandToRcArgs,
List<String> commandsToParse,
LinkedHashSet<String> configAncestorSet,
String configToExpand,
List<String> longestChain,
Consumer<String> rcFileNotesConsumer)
throws OptionsParsingException {
List<String> expansion = new ArrayList<>();
boolean foundDefinition = false;
// The expansion order of rc files is first by command priority, and then in the order the
// rc files were read, respecting import statement placement.
for (String commandToParse : commandsToParse) {
String configDef = commandToParse + ":" + configToExpand;
for (RcChunkOfArgs rcArgs : commandToRcArgs.get(configDef)) {
foundDefinition = true;
rcFileNotesConsumer.accept(
String.format(
"Found applicable config definition %s in file %s: %s",
configDef, rcArgs.getRcFile(), String.join(" ", rcArgs.getArgs())));
// For each arg in the rcARgs chunk, we first check if it is a config, and if so, expand
// it in place. We avoid cycles by tracking the parents of this config.
for (String arg : rcArgs.getArgs()) {
expansion.add(arg);
if (arg.length() >= 8 && arg.substring(0, 8).equals("--config")) {
// We have a config. For sanity, because we don't want to worry about formatting,
// we will only accept --config=value, and will not accept value on a following line.
int charOfConfigValue = arg.indexOf('=');
if (charOfConfigValue < 0) {
throw new OptionsParsingException(
String.format(
"In file %s, the definition of config %s expands to another config "
+ "that either has no value or is not in the form --config=value. For "
+ "recursive config definitions, please do not provide the value in a "
+ "separate token, such as in the form '--config value'.",
rcArgs.getRcFile(), configToExpand));
}
String newConfigValue = arg.substring(charOfConfigValue + 1);
LinkedHashSet<String> extendedConfigAncestorSet =
new LinkedHashSet<>(configAncestorSet);
if (!extendedConfigAncestorSet.add(newConfigValue)) {
throw new OptionsParsingException(
String.format(
"Config expansion has a cycle: config value %s expands to itself, "
+ "see inheritance chain %s",
newConfigValue, extendedConfigAncestorSet));
}
if (extendedConfigAncestorSet.size() > longestChain.size()) {
longestChain.clear();
longestChain.addAll(extendedConfigAncestorSet);
}
expansion.addAll(
getExpansion(
commandToRcArgs,
commandsToParse,
extendedConfigAncestorSet,
newConfigValue,
longestChain,
rcFileNotesConsumer));
}
}
}
}
if (!foundDefinition) {
throw new OptionsParsingException(
"Config value " + configToExpand + " is not defined in any .rc file");
}
return expansion;
}