com.google.common.collect.ImmutableList#subList ( )源码实例Demo

下面列出了com.google.common.collect.ImmutableList#subList ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。

源代码1 项目: nomulus   文件: DomainNameUtils.java
/**
 * Returns the second level domain name for a fully qualified host name under a given tld.
 *
 * <p>This function is merely a string parsing utility, and does not verify if the tld is operated
 * by the registry.
 *
 * @throws IllegalArgumentException if either argument is null or empty, or the domain name is not
 *     under the tld
 */
public static String getSecondLevelDomain(String hostName, String tld) {
  checkArgument(
      !Strings.isNullOrEmpty(hostName),
      "hostName cannot be null or empty");
  checkArgument(!Strings.isNullOrEmpty(tld), "tld cannot be null or empty");
  ImmutableList<String> domainParts = InternetDomainName.from(hostName).parts();
  ImmutableList<String> tldParts = InternetDomainName.from(tld).parts();
  checkArgument(
      domainParts.size() > tldParts.size(),
      "hostName must be at least one level below the tld");
  checkArgument(
      domainParts
          .subList(domainParts.size() - tldParts.size(), domainParts.size())
          .equals(tldParts),
      "hostName must be under the tld");
  ImmutableList<String> sldParts =
      domainParts.subList(domainParts.size() - tldParts.size() - 1, domainParts.size());
  return Joiner.on(".").join(sldParts);
}
 
源代码2 项目: nomulus   文件: DnsUpdateWriter.java
@Override
public void publishHost(String hostName) {
  // Get the superordinate domain name of the host.
  InternetDomainName host = InternetDomainName.from(hostName);
  ImmutableList<String> hostParts = host.parts();
  Optional<InternetDomainName> tld = Registries.findTldForName(host);

  // host not managed by our registry, no need to update DNS.
  if (!tld.isPresent()) {
    return;
  }

  ImmutableList<String> tldParts = tld.get().parts();
  ImmutableList<String> domainParts =
      hostParts.subList(hostParts.size() - tldParts.size() - 1, hostParts.size());
  String domain = Joiner.on(".").join(domainParts);

  // Refresh the superordinate domain, always delete the host first to ensure idempotency,
  // and only publish the host if it is a glue record.
  publishDomain(domain, hostName);
}
 
源代码3 项目: glowroot   文件: LiveWeavingServiceImpl.java
@Override
public List<String> getMatchingMethodNames(String agentId, String className,
        String partialMethodName, int limit) {
    String partialMethodNameUpper = partialMethodName.toUpperCase(Locale.ENGLISH);
    Set<String> methodNames = Sets.newHashSet();
    for (UiAnalyzedMethod analyzedMethod : getClasspathCache().getAnalyzedMethods(className)) {
        String methodName = analyzedMethod.name();
        if (methodName.equals("<init>") || methodName.equals("<clinit>")) {
            // static initializers are not supported by weaver
            // (see AdviceMatcher.isMethodNameMatch())
            // and constructors do not support @OnBefore advice at this time
            continue;
        }
        if (methodName.toUpperCase(Locale.ENGLISH).contains(partialMethodNameUpper)) {
            methodNames.add(methodName);
        }
    }
    ImmutableList<String> sortedMethodNames =
            Ordering.natural().immutableSortedCopy(methodNames);
    if (methodNames.size() > limit) {
        return sortedMethodNames.subList(0, limit);
    } else {
        return sortedMethodNames;
    }
}
 
源代码4 项目: glowroot   文件: LiveJvmServiceImpl.java
@Override
public List<String> getMatchingMBeanObjectNames(String agentId, String partialObjectName,
        int limit) throws Exception {
    ObjectNameQueryExp queryExp = new ObjectNameQueryExp(partialObjectName);
    List<MBeanServer> mbeanServers = lazyPlatformMBeanServer.findAllMBeanServers();
    Set<ObjectName> objectNames =
            lazyPlatformMBeanServer.queryNames(null, queryExp, mbeanServers);
    // unfortunately Wildfly returns lots of mbean object names without checking them against
    // the query (see TODO comment in org.jboss.as.jmx.model.ModelControllerMBeanHelper)
    // so must re-filter
    List<String> names = Lists.newArrayList();
    for (ObjectName objectName : objectNames) {
        String objectNameStr = objectName.toString();
        if (queryExp.apply(objectNameStr)) {
            names.add(objectNameStr);
        }
    }
    ImmutableList<String> sortedNames = Ordering.natural().immutableSortedCopy(names);
    if (sortedNames.size() > limit) {
        sortedNames = sortedNames.subList(0, limit);
    }
    return sortedNames;
}
 
源代码5 项目: bce-sdk-java   文件: Path.java
private Path(ImmutableList<String> dirs) {
    if (dirs == null || dirs.isEmpty()) {
        this.me = "";
        this.origin = me;
        this.dir = true;
        this.last = me;
    } else {
        for (String s : dirs) {
            this.me = String.format("%s/%s", me, s);
        }
        this.origin = me;
        this.last = dirs.get(dirs.size() - 1);
        this.dir = true;
        this.parent = new Path(dirs.subList(0, dirs.size() - 1));
    }
}
 
源代码6 项目: bazel   文件: BytecodeTypeInference.java
private static ImmutableList<InferredType> removeBackFromList(
    ImmutableList<InferredType> list, int countToRemove) {
  int origSize = list.size();
  int index = origSize - 1;

  while (index >= 0 && countToRemove > 0) {
    InferredType type = list.get(index);
    if (type.equals(InferredType.TOP) && index > 0 && list.get(index - 1).isCategory2()) {
      --index; // A category 2 takes two slots.
    }
    --index; // Eat this local variable.
    --countToRemove;
  }
  checkState(
      countToRemove == 0,
      "countToRemove is %s but not 0. index=%s, list=%s",
      countToRemove,
      index,
      list);
  return list.subList(0, index + 1);
}
 
源代码7 项目: buck   文件: ArtifactCache.java
/**
 * Store the list of artifacts at path specified by output to cache in passed order, such that it
 * can later be fetched using ruleKey as the lookup key. If any internal errors occur, fail
 * silently and continue execution. Store may be performed synchronously or asynchronously.
 *
 * <p>This is a noop if {@link #getCacheReadMode()}} returns {@code READONLY}.
 *
 * @param artifacts list of artifact info and path to be uploaded to the cache in given order.
 * @return {@link ListenableFuture} that completes once the store has finished.
 */
default ListenableFuture<Unit> store(
    ImmutableList<Pair<ArtifactInfo, BorrowablePath>> artifacts) {
  if (artifacts.isEmpty()) {
    return Futures.immediateFuture(null);
  }

  Pair<ArtifactInfo, BorrowablePath> first = artifacts.get(0);
  ImmutableList<Pair<ArtifactInfo, BorrowablePath>> rest = artifacts.subList(1, artifacts.size());

  return Futures.transformAsync(
      this.store(first.getFirst(), first.getSecond()),
      input -> this.store(rest),
      MoreExecutors.directExecutor());
}
 
源代码8 项目: cloud-opensource-java   文件: LinkageChecker.java
public static LinkageChecker create(Bom bom, Path exclusionFile) throws IOException {
  // duplicate code from DashboardMain follows. We need to refactor to extract this.
  ImmutableList<Artifact> managedDependencies = bom.getManagedDependencies();

  ClassPathBuilder classPathBuilder = new ClassPathBuilder();
  ClassPathResult classPathResult = classPathBuilder.resolve(managedDependencies);
  ImmutableList<ClassPathEntry> classpath = classPathResult.getClassPath();

  // When checking a BOM, entry point classes are the ones in the artifacts listed in the BOM
  List<ClassPathEntry> artifactsInBom = classpath.subList(0, managedDependencies.size());
  ImmutableSet<ClassPathEntry> entryPoints = ImmutableSet.copyOf(artifactsInBom);

  return LinkageChecker.create(classpath, entryPoints, exclusionFile);
}
 
源代码9 项目: dremio-oss   文件: JoinPruleBase.java
protected void createDistBothPlan(RelOptRuleCall call, JoinRel join,
    RelNode left, RelNode right,
    RelCollation collationLeft, RelCollation collationRight, boolean hashSingleKey) throws InvalidRelException, UnsupportedRelOperatorException {

  /* If join keys are  l1 = r1 and l2 = r2 and ... l_k = r_k, then consider the following options of plan:
   *   1) Plan1: distributed by (l1, l2, ..., l_k) for left side and by (r1, r2, ..., r_k) for right side.
   *   2) Plan2: distributed by l1 for left side, by r1 for right side.
   *   3) Plan3: distributed by l2 for left side, by r2 for right side.
   *   ...
   *      Plan_(k+1): distributed by l_k for left side, by r_k by right side.
   *
   *   Whether enumerate plan 2, .., Plan_(k+1) depends on option : hashSingleKey.
   */

  final ImmutableList<DistributionField> leftDistributionFields = getDistributionField(join.getLeftKeys());
  final ImmutableList<DistributionField> rightDistributionFields = getDistributionField(join.getRightKeys());

  DistributionTrait hashLeftPartition = new DistributionTrait(DistributionTrait.DistributionType.HASH_DISTRIBUTED, leftDistributionFields);
  DistributionTrait hashRightPartition = new DistributionTrait(DistributionTrait.DistributionType.HASH_DISTRIBUTED, rightDistributionFields);

  createDistBothPlan(call, join, left, right, collationLeft, collationRight, hashLeftPartition, hashRightPartition);

  assert (join.getLeftKeys().size() == join.getRightKeys().size());

  if (!hashSingleKey) {
    return;
  }

  int numJoinKeys = join.getLeftKeys().size();
  if (numJoinKeys > 1) {
    for (int i = 0; i < numJoinKeys; i++) {
      hashLeftPartition = new DistributionTrait(DistributionTrait.DistributionType.HASH_DISTRIBUTED, leftDistributionFields.subList(i, i+1));
      hashRightPartition = new DistributionTrait(DistributionTrait.DistributionType.HASH_DISTRIBUTED, rightDistributionFields.subList(i, i+1));

      createDistBothPlan(call, join, left, right, collationLeft, collationRight, hashLeftPartition, hashRightPartition);
    }
  }
}
 
源代码10 项目: api-compiler   文件: RestAnalyzer.java
static RestMethod createCustomMethod(
    Method method, HttpAttribute httpConfig, String customNamePrefix) {
  ImmutableList<PathSegment> path = httpConfig.getFlatPath();
  PathSegment lastSegment = path.get(path.size() - 1);

  // Determine base name.
  String customName = "";
  if (lastSegment instanceof LiteralSegment) {
    customName = ((LiteralSegment) lastSegment).getLiteral();
    path = path.subList(0, path.size() - 1);
  } else {
    if (method.getModel().getConfigVersion() > 1) {
      // From version 2 on, we generate a meaningful name here.
      customName = method.getSimpleName();
    } else if (customNamePrefix.isEmpty()){
      // Older versions use the prefix or derive from the http method.
      customName = httpConfig.getMethodKind().toString().toLowerCase();
    }
  }

  // Prepend prefix.
  if (!customNamePrefix.isEmpty()
      && !customName.toLowerCase().startsWith(customNamePrefix.toLowerCase())) {
    customName = customNamePrefix + ensureUpperCase(customName);
  }

  // Ensure effective start is lower case.
  customName = ensureLowerCase(customName);

  String restMethodName = "";

  CollectionName collectionName = RestAnalyzer.buildCollectionName(path, method.getModel());

  return RestMethod.create(
      method, RestKind.CUSTOM, collectionName, customName, restMethodName);
}
 
源代码11 项目: copybara   文件: ChangeReader.java
private CharSequence branchCommitLog(GitRevision ref, List<GitRevision> parents)
    throws RepoException {
  if (parents.size() <= 1) {
    // Not a merge commit, so don't bother showing full log of branch commits. This would only
    // contain the raw commit of 'ref', which will be redundant.
    return "";
  }
  if (!includeBranchCommitLogs) {
    return "";
  }

  ImmutableList<GitLogEntry> entries =
      repository
          .log(parents.get(0).getSha1() + ".." + ref.getSha1())
          // This might give incorrect results but several migrations rely on this behavior.
          // and first_parent = False doesn't work for ITERATIVE
          .withPaths(Glob.isEmptyRoot(roots) ? ImmutableList.of() : roots)
          .firstParent(false)
          .run();

  if (entries.isEmpty()) {
    return "";
  }
  // Remove the merge commit. Since we already have that in the body.
  entries = entries.subList(1, entries.size());

  return "\n" + BRANCH_COMMIT_LOG_HEADING + "\n" +
      Joiner.on("\n").join(entries.stream()
          .map(e -> ""
              + "commit " + e.getCommit().getSha1() + "\n"
              + "Author:  " + filterAuthor(e.getAuthor()) + "\n"
              + "Date:    " + e.getAuthorDate() + "\n"
              + "\n"
              + "    " + e.getBody().replace("\n", "    \n"))
          .collect(Collectors.toList()));
}
 
源代码12 项目: dagger2-sample   文件: BindingGraphValidator.java
private void reportMissingBinding(
    Deque<ResolvedRequest> path, ValidationReport.Builder<BindingGraph> reportBuilder) {
  Key key = path.peek().request().key();
  TypeMirror type = key.type();
  String typeName = TypeNames.forTypeMirror(type).toString();
  boolean requiresContributionMethod = !key.isValidImplicitProvisionKey(types);
  boolean requiresProvision = doesPathRequireProvisionOnly(path);
  StringBuilder errorMessage = new StringBuilder();
  String requiresErrorMessageFormat = requiresContributionMethod
      ? requiresProvision
          ? REQUIRES_PROVIDER_FORMAT
          : REQUIRES_PROVIDER_OR_PRODUCER_FORMAT
      : requiresProvision
          ? REQUIRES_AT_INJECT_CONSTRUCTOR_OR_PROVIDER_FORMAT
          : REQUIRES_AT_INJECT_CONSTRUCTOR_OR_PROVIDER_OR_PRODUCER_FORMAT;
  errorMessage.append(String.format(requiresErrorMessageFormat, typeName));
  if (key.isValidMembersInjectionKey()
      && !injectBindingRegistry.getOrFindMembersInjectionBinding(key).injectionSites()
          .isEmpty()) {
    errorMessage.append(" ").append(ErrorMessages.MEMBERS_INJECTION_DOES_NOT_IMPLY_PROVISION);
  }
  ImmutableList<String> printableDependencyPath =
      FluentIterable.from(path)
          .transform(REQUEST_FROM_RESOLVED_REQUEST)
          .transform(dependencyRequestFormatter)
          .filter(Predicates.not(Predicates.equalTo("")))
          .toList()
          .reverse();
  for (String dependency :
      printableDependencyPath.subList(1, printableDependencyPath.size())) {
    errorMessage.append("\n").append(dependency);
  }
  reportBuilder.addItem(errorMessage.toString(), path.getLast().request().requestElement());
}
 
源代码13 项目: glowroot   文件: ConfigRepositoryImpl.java
private static ImmutableList<Integer> fix(ImmutableList<Integer> thisList,
        List<Integer> defaultList) {
    if (thisList.size() >= defaultList.size()) {
        return thisList.subList(0, defaultList.size());
    }
    List<Integer> correctedList = Lists.newArrayList(thisList);
    for (int i = thisList.size(); i < defaultList.size(); i++) {
        correctedList.add(defaultList.get(i));
    }
    return ImmutableList.copyOf(correctedList);
}
 
源代码14 项目: buck   文件: RenderingConsoleTest.java
Iterable<String> getNewLinesFromStderr() throws IOException {
  String text = console.getTextWrittenToStdErr();
  ImmutableList<String> allLines = MoreStrings.lines(text);
  ImmutableList<String> newLines = allLines.subList(lastNumLinesRead, allLines.size());
  lastNumLinesRead = allLines.size();
  return newLines;
}
 
源代码15 项目: intellij   文件: AspectStrategyTest.java
private static ImmutableList<String> getBlazeFlags(BlazeCommand.Builder builder) {
  ImmutableList<String> args = builder.build().toList();
  return args.subList(3, args.indexOf("--"));
}
 
源代码16 项目: keywhiz   文件: SecretController.java
/**
 * @param expireMaxTime timestamp for farthest expiry to include
 * @param limit         limit on number of results to return
 * @param cursor        cursor to be used to enforce pagination
 * @return all existing sanitized secrets and their groups matching criteria.
 */
public SanitizedSecretWithGroupsListAndCursor getSanitizedSecretsWithGroupsAndCursor(
    @Nullable Long expireMaxTime,
    @Nullable Integer limit,
    @Nullable SecretRetrievalCursor cursor) {
  // Retrieve secrets based on the cursor (if provided).
  ImmutableList<SecretSeriesAndContent> secrets;

  // Retrieve one additional record to detect when information is missing
  Integer updatedLimit = null;
  if (limit != null) {
    updatedLimit = limit + 1;
  }

  if (cursor == null) {
    secrets = secretDAO.getSecrets(expireMaxTime, null, null, null, updatedLimit);
  } else {
    secrets = secretDAO.getSecrets(expireMaxTime, null, cursor.expiry(), cursor.name(),
        updatedLimit);
  }

  // Set the cursor and strip the final record from the secrets if necessary
  SecretRetrievalCursor newCursor = null;
  if (limit != null && secrets.size() > limit) {
    // The name and expiry in the new cursor will be the first entry in the next set of results
    newCursor = SecretRetrievalCursor.of(secrets.get(limit).series().name(),
        secrets.get(limit).content().expiry());
    // Trim the last record from the list
    secrets = secrets.subList(0, limit);
  }

  Set<Long> secretIds = secrets.stream().map(s -> s.series().id()).collect(toSet());

  Map<Long, List<Group>> groupsForSecrets = aclDAO.getGroupsForSecrets(secretIds);

  List<SanitizedSecretWithGroups> secretsWithGroups = secrets.stream().map(s -> {
    List<Group> groups = groupsForSecrets.get(s.series().id());
    if (groups == null) {
      groups = ImmutableList.of();
    }
    return fromSecretSeriesAndContentAndGroups(s, groups);
  }).collect(toList());

  try {
    return SanitizedSecretWithGroupsListAndCursor.of(secretsWithGroups,
        SecretRetrievalCursor.toUrlEncodedString(newCursor));
  } catch (Exception e) {
    logger.warn("Unable to encode cursor to string (cursor: {}): {}", newCursor, e.getMessage());
    // The cursor is malformed; return what information could be gathered
    return SanitizedSecretWithGroupsListAndCursor.of(secretsWithGroups, null);
  }
}
 
源代码17 项目: dagger2-sample   文件: EnumWriter.java
@Override
public Appendable write(Appendable appendable, Context context) throws IOException {
  context = context.createSubcontext(FluentIterable.from(nestedTypeWriters)
      .transform(new Function<TypeWriter, ClassName>() {
        @Override public ClassName apply(TypeWriter input) {
          return input.name;
        }
      })
      .toSet());
  writeAnnotations(appendable, context);
  writeModifiers(appendable).append("enum ").append(name.simpleName());
  Iterator<TypeName> implementedTypesIterator = implementedTypes.iterator();
  if (implementedTypesIterator.hasNext()) {
    appendable.append(" implements ");
    implementedTypesIterator.next().write(appendable, context);
    while (implementedTypesIterator.hasNext()) {
      appendable.append(", ");
      implementedTypesIterator.next().write(appendable, context);
    }
  }
  appendable.append(" {");

  checkState(!constantWriters.isEmpty(), "Cannot write an enum with no constants.");
  appendable.append('\n');
  ImmutableList<ConstantWriter> constantWriterList =
      ImmutableList.copyOf(constantWriters.values());
  for (ConstantWriter constantWriter
      : constantWriterList.subList(0, constantWriterList.size() - 1)) {
    constantWriter.write(appendable, context);
    appendable.append(",\n");
  }
  constantWriterList.get(constantWriterList.size() - 1).write(appendable, context);
  appendable.append(";\n");

  if (!fieldWriters.isEmpty()) {
    appendable.append('\n');
  }
  for (VariableWriter fieldWriter : fieldWriters.values()) {
    fieldWriter.write(new IndentingAppendable(appendable), context).append("\n");
  }
  for (ConstructorWriter constructorWriter : constructorWriters) {
    appendable.append('\n');
    if (!isDefaultConstructor(constructorWriter)) {
      constructorWriter.write(new IndentingAppendable(appendable), context);
    }
  }
  for (MethodWriter methodWriter : methodWriters) {
    appendable.append('\n');
    methodWriter.write(new IndentingAppendable(appendable), context);
  }
  for (TypeWriter nestedTypeWriter : nestedTypeWriters) {
    appendable.append('\n');
    nestedTypeWriter.write(new IndentingAppendable(appendable), context);
  }
  appendable.append("}\n");
  return appendable;
}
 
源代码18 项目: jimfs   文件: JimfsPath.java
@Override
public JimfsPath relativize(Path other) {
  JimfsPath otherPath = checkPath(other);
  if (otherPath == null) {
    throw new ProviderMismatchException(other.toString());
  }

  checkArgument(
      Objects.equals(root, otherPath.root), "Paths have different roots: %s, %s", this, other);

  if (equals(other)) {
    return pathService.emptyPath();
  }

  if (isEmptyPath()) {
    return otherPath;
  }

  ImmutableList<Name> otherNames = otherPath.names;
  int sharedSubsequenceLength = 0;
  for (int i = 0; i < Math.min(getNameCount(), otherNames.size()); i++) {
    if (names.get(i).equals(otherNames.get(i))) {
      sharedSubsequenceLength++;
    } else {
      break;
    }
  }

  int extraNamesInThis = Math.max(0, getNameCount() - sharedSubsequenceLength);

  ImmutableList<Name> extraNamesInOther =
      (otherNames.size() <= sharedSubsequenceLength)
          ? ImmutableList.<Name>of()
          : otherNames.subList(sharedSubsequenceLength, otherNames.size());

  List<Name> parts = new ArrayList<>(extraNamesInThis + extraNamesInOther.size());

  // add .. for each extra name in this path
  parts.addAll(Collections.nCopies(extraNamesInThis, Name.PARENT));
  // add each extra name in the other path
  parts.addAll(extraNamesInOther);

  return pathService.createRelativePath(parts);
}
 
源代码19 项目: buck   文件: FlavorSearchTargetNodeFinder.java
public Optional<TargetNode<?>> get(BuildTarget target) {

    // If this node is in the graph under the given name, return it.
    TargetNode<?> node = getBuildTargetIndex().get(target);
    if (node != null) {
      return Optional.of(node);
    }

    ImmutableSet<ImmutableSet<Flavor>> flavorList =
        getBaseTargetFlavorMap().get(target.getUnflavoredBuildTarget());
    if (flavorList == null) {
      return Optional.empty();
    }

    // Otherwise, see if this node exists in the graph with a "less" flavored name.  We initially
    // select all targets which contain a subset of the original flavors, which should be sorted by
    // from largest flavor set to smallest.  We then use the first match, and verify the subsequent
    // matches are subsets.
    ImmutableList<ImmutableSet<Flavor>> matches =
        RichStream.from(flavorList).filter(target.getFlavors()::containsAll).toImmutableList();
    if (!matches.isEmpty()) {
      ImmutableSet<Flavor> firstMatch = matches.get(0);
      for (ImmutableSet<Flavor> subsequentMatch : matches.subList(1, matches.size())) {
        Preconditions.checkState(
            firstMatch.size() > subsequentMatch.size(),
            "Expected to find larger flavor lists earlier in the flavor map "
                + "index (sizeof(%s) <= sizeof(%s))",
            firstMatch.size(),
            subsequentMatch.size());
        Preconditions.checkState(
            firstMatch.containsAll(subsequentMatch),
            "Found multiple disjoint flavor matches for %s: %s and %s (from %s)",
            target,
            firstMatch,
            subsequentMatch,
            matches);
      }
      return Optional.of(
          Preconditions.checkNotNull(
              getBaseTargetIndex().get(target.withFlavors(firstMatch)),
              "%s missing in index",
              target.withFlavors(firstMatch)));
    }

    // Otherwise, return `null` to indicate this node isn't in the target graph.
    return Optional.empty();
  }
 
@ImmutableConstructor
private BlackFxOptionSmileVolatilitiesSpecification(
    FxOptionVolatilitiesName name,
    CurrencyPair currencyPair,
    DayCount dayCount,
    List<FxOptionVolatilitiesNode> nodes,
    CurveInterpolator timeInterpolator,
    CurveExtrapolator timeExtrapolatorLeft,
    CurveExtrapolator timeExtrapolatorRight,
    CurveInterpolator strikeInterpolator,
    CurveExtrapolator strikeExtrapolatorLeft,
    CurveExtrapolator strikeExtrapolatorRight) {
  JodaBeanUtils.notNull(name, "name");
  JodaBeanUtils.notNull(currencyPair, "currencyPair");
  JodaBeanUtils.notNull(dayCount, "dayCount");
  JodaBeanUtils.notNull(nodes, "nodes");
  JodaBeanUtils.notNull(timeInterpolator, "timeInterpolator");
  JodaBeanUtils.notNull(timeExtrapolatorLeft, "timeExtrapolatorLeft");
  JodaBeanUtils.notNull(timeExtrapolatorRight, "timeExtrapolatorRight");
  JodaBeanUtils.notNull(strikeInterpolator, "strikeInterpolator");
  JodaBeanUtils.notNull(strikeExtrapolatorLeft, "strikeExtrapolatorLeft");
  JodaBeanUtils.notNull(strikeExtrapolatorRight, "strikeExtrapolatorRight");
  this.name = name;
  this.currencyPair = currencyPair;
  this.dayCount = dayCount;
  this.nodes = ImmutableList.copyOf(nodes);
  this.timeInterpolator = timeInterpolator;
  this.timeExtrapolatorLeft = timeExtrapolatorLeft;
  this.timeExtrapolatorRight = timeExtrapolatorRight;
  this.strikeInterpolator = strikeInterpolator;
  this.strikeExtrapolatorLeft = strikeExtrapolatorLeft;
  this.strikeExtrapolatorRight = strikeExtrapolatorRight;
  this.nodesByTenor = nodes.stream()
      .collect(Guavate.toImmutableListMultimap(FxOptionVolatilitiesNode::getTenor));
  ImmutableList<Double> fullDeltas = nodes.stream()
      .map(FxOptionVolatilitiesNode::getStrike)
      .distinct()
      .map(Strike::getValue)
      .sorted()
      .collect(toImmutableList());

  int nDeltas = fullDeltas.size() - 1;
  ArgChecker.isTrue(fullDeltas.get(nDeltas) == 0.5, "0 < delta <= 0.5");
  this.deltas = fullDeltas.subList(0, nDeltas); // ATM removed
  int nParams = nodes.size();
  for (int i = 0; i < nParams; ++i) {
    ArgChecker.isTrue(nodes.get(i).getCurrencyPair().equals(currencyPair), "currency pair must be the same");
    ArgChecker.isTrue(nodes.get(i).getStrike() instanceof DeltaStrike, "Strike must be DeltaStrike");
  }
  for (Tenor tenor : nodesByTenor.keys()) {
    ImmutableList<FxOptionVolatilitiesNode> nodesForTenor = nodesByTenor.get(tenor);
    // value type, delta, size
    List<Double> atmDelta = nodesForTenor.stream()
        .filter(node -> node.getQuoteValueType().equals(ValueType.BLACK_VOLATILITY))
        .map(node -> node.getStrike().getValue())
        .sorted()
        .collect(toList());
    ArgChecker.isTrue(atmDelta.equals(fullDeltas.subList(nDeltas, nDeltas + 1)),
        "The ATM delta set must be " + fullDeltas.subList(nDeltas, nDeltas + 1) + ", but found " + atmDelta + ", for " + tenor);
    List<Double> rrDelta = nodesForTenor.stream()
        .filter(node -> node.getQuoteValueType().equals(ValueType.RISK_REVERSAL))
        .map(node -> node.getStrike().getValue())
        .sorted()
        .collect(toList());
    ArgChecker.isTrue(rrDelta.equals(deltas),
        "The delta set for risk reversal must be " + deltas + ", but found " + rrDelta + ", for " + tenor);
    List<Double> strDelta = nodesForTenor.stream()
        .filter(node -> node.getQuoteValueType().equals(ValueType.STRANGLE))
        .map(node -> node.getStrike().getValue())
        .sorted()
        .collect(toList());
    ArgChecker.isTrue(strDelta.equals(deltas),
        "The delta set for strangle must be " + deltas + ", but found " + strDelta + ", for " + tenor);
    // convention
    Set<BusinessDayAdjustment> busAdj = nodesForTenor.stream()
        .map(FxOptionVolatilitiesNode::getBusinessDayAdjustment)
        .collect(toSet());
    ArgChecker.isTrue(busAdj.size() == 1, "BusinessDayAdjustment must be common to all the nodes");
    Set<DaysAdjustment> offset = nodesForTenor.stream()
        .map(FxOptionVolatilitiesNode::getSpotDateOffset)
        .collect(toSet());
    ArgChecker.isTrue(offset.size() == 1, "DaysAdjustment must be common to all the nodes");
  }
}