下面列出了com.google.common.collect.ImmutableMap#size ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Override
public Map<Serializable, Object> findDictionaryItem(Set<Serializable> codes) {
if (codes.isEmpty()) {
return Collections.emptyMap();
}
// 1. 根据 字典编码查询可用的字典列表
LbqWrapper<DictionaryItem> query = Wraps.<DictionaryItem>lbQ()
.in(DictionaryItem::getCode, codes)
.eq(DictionaryItem::getStatus, true)
.orderByAsc(DictionaryItem::getSortValue);
List<DictionaryItem> list = super.list(query);
// 2. 将 list 转换成 Map,Map的key是字典编码,value是字典名称
ImmutableMap<String, String> typeMap = MapHelper.uniqueIndex(list, DictionaryItem::getCode, DictionaryItem::getName);
// 3. 将 Map<String, String> 转换成 Map<Serializable, Object>
Map<Serializable, Object> typeCodeNameMap = new LinkedHashMap<>(typeMap.size());
typeMap.forEach((key, value) -> typeCodeNameMap.put(key, value));
return typeCodeNameMap;
}
@Override
public Map<Serializable, Object> findDictionaryItem(Set<Serializable> codes) {
if (codes.isEmpty()) {
return Collections.emptyMap();
}
// 1. 根据 字典编码查询可用的字典列表
LbqWrapper<DictionaryItem> query = Wraps.<DictionaryItem>lbQ()
.in(DictionaryItem::getCode, codes)
.eq(DictionaryItem::getStatus, true)
.orderByAsc(DictionaryItem::getSortValue);
List<DictionaryItem> list = super.list(query);
// 2. 将 list 转换成 Map,Map的key是字典编码,value是字典名称
ImmutableMap<String, String> typeMap = MapHelper.uniqueIndex(list, DictionaryItem::getCode, DictionaryItem::getName);
// 3. 将 Map<String, String> 转换成 Map<Serializable, Object>
Map<Serializable, Object> typeCodeNameMap = new LinkedHashMap<>(typeMap.size());
typeMap.forEach((key, value) -> typeCodeNameMap.put(key, value));
return typeCodeNameMap;
}
public static String getStringFromMap(ImmutableMap<String, ImmutableSet<PrivilegeType>> mapSet) {
String result = "";
if (mapSet == null || mapSet.size() == 0) {
return "";
}
boolean isFirst = false;
for(Entry<String, ImmutableSet<PrivilegeType>> ele : mapSet.entrySet()) {
String dbName = ele.getKey();
String privileges = getStringFromArray2(ele.getValue());
if (isFirst) {
result = result + dbName + ": (" + privileges + "); ";
isFirst = false;
} else {
result = result + dbName + ": (" + privileges + "); ";
}
}
return result;
}
/**
* Helper method to find out if the only included fieldmapping metadata is typed NULL, which means
* that type and index exist, but the field did not
*/
private boolean isFieldMappingMissingField(ImmutableMap<String, ImmutableMap<String, ImmutableMap<String, FieldMappingMetaData>>> mappingsByIndex) throws IOException {
if (mappingsByIndex.size() != 1) {
return false;
}
for (ImmutableMap<String, ImmutableMap<String, FieldMappingMetaData>> value : mappingsByIndex.values()) {
for (ImmutableMap<String, FieldMappingMetaData> fieldValue : value.values()) {
for (Map.Entry<String, FieldMappingMetaData> fieldMappingMetaDataEntry : fieldValue.entrySet()) {
if (fieldMappingMetaDataEntry.getValue().isNull()) {
return true;
}
}
}
}
return false;
}
private DocumentFilterList filtering(final Predicate<DocumentFilter> predicate)
{
if (isEmpty())
{
return this;
}
final ImmutableMap<String, DocumentFilter> newFiltersById = filtersById.entrySet()
.stream()
.filter(entry -> predicate.test(entry.getValue()))
.collect(GuavaCollectors.toImmutableMap());
if (newFiltersById.isEmpty())
{
return EMPTY;
}
else if (newFiltersById.size() == filtersById.size())
{
return this;
}
else
{
return new DocumentFilterList(newFiltersById);
}
}
/**
* If the config specifies a value for "toolchain_target", returns a {@link UnresolvedCxxPlatform}
* backed by the specified target.
*/
public static Optional<UnresolvedCxxPlatform> getProviderBasedPlatform(
BuckConfig config, Flavor flavor) {
String cxxSection = new CxxBuckConfig(config, flavor).cxxSection;
Optional<UnconfiguredBuildTarget> toolchainTarget =
config.getMaybeUnconfiguredBuildTarget(cxxSection, TOOLCHAIN_TARGET);
if (!toolchainTarget.isPresent()) {
return Optional.empty();
}
if (!cxxSection.equals(UNFLAVORED_CXX_SECTION)) {
// In a flavored cxx section, we don't allow any configuration except for configuration of the
// platform.
ImmutableMap<String, String> allEntries = config.getEntriesForSection(cxxSection);
if (allEntries.size() != 1) {
throw new HumanReadableException(
"When configuring a cxx %s, no other configuration is allowed in that section. Got unexpected keys [%s]",
TOOLCHAIN_TARGET,
Joiner.on(", ")
.join(Sets.difference(allEntries.keySet(), ImmutableSet.of(TOOLCHAIN_TARGET))));
}
}
return Optional.of(new ProviderBasedUnresolvedCxxPlatform(toolchainTarget.get(), flavor));
}
/**
* Invalidates all target nodes defined in {@param path}. Optionally also invalidates the build
* targets {@link UnflavoredBuildTarget} depending on {@param invalidateBuildTargets}.
*
* @return The number of invalidated nodes.
*/
int invalidateNodesInPath(AbsPath path, boolean invalidateBuildTargets) {
try (AutoCloseableLock writeLock = cachesLock.writeLock()) {
int invalidatedRawNodes = 0;
BuildFileManifest buildFileManifest = allBuildFileManifests.getIfPresent(path);
if (buildFileManifest != null) {
ImmutableMap<String, ImmutableMap<String, Object>> rawNodes =
buildFileManifest.getTargets();
// Increment the counter
invalidatedRawNodes = rawNodes.size();
for (Map<String, Object> rawNode : rawNodes.values()) {
UnflavoredBuildTarget target =
UnflavoredBuildTargetFactory.createFromRawNode(
cellRoot.getPath(), cellCanonicalName, rawNode, path.getPath());
LOG.debug("Invalidating target for path %s: %s", path, target);
for (Cache<?, ?> cache : typedNodeCaches()) {
cache.invalidateFor(target);
}
if (invalidateBuildTargets) {
allRawNodeTargets.remove(target);
}
}
}
return invalidatedRawNodes;
}
}
/**
* Factory method for creating UnitOfWorkInvoker.
*/
public Invoker create(Object service, Invoker rootInvoker, SessionFactory sessionFactory) {
ImmutableMap.Builder<String, UnitOfWork> unitOfWorkMethodsBuilder =
new ImmutableMap.Builder<>();
for (Method m : service.getClass().getMethods()) {
if (m.isAnnotationPresent(UnitOfWork.class)) {
unitOfWorkMethodsBuilder.put(m.getName(), m.getAnnotation(UnitOfWork.class));
}
}
ImmutableMap<String, UnitOfWork> unitOfWorkMethods = unitOfWorkMethodsBuilder.build();
Invoker invoker = rootInvoker;
if (unitOfWorkMethods.size() > 0) {
invoker = new UnitOfWorkInvoker(invoker, unitOfWorkMethods, sessionFactory);
}
return invoker;
}
@VisibleForTesting
ImmutableList<MetricPoint<V>> getTimestampedValues(Instant timestamp) {
ImmutableMap<ImmutableList<String>, V> values = valuesSupplier.get();
ImmutableList.Builder<MetricPoint<V>> metricPoints = new ImmutableList.Builder<>();
for (Entry<ImmutableList<String>, V> entry : values.entrySet()) {
metricPoints.add(
MetricPoint.create(this, entry.getKey(), timestamp, timestamp, entry.getValue()));
}
cardinality = values.size();
return metricPoints.build();
}
@Override
protected void handleResponseSetIDs(final ResponseLinking.Builder responseLinking,
final ImmutableSet<ResponseSet> responseSets,
final ImmutableMap<String, ResponseSet> responseIDs,
final Optional<ImmutableMap.Builder<String, String>> foreignLinkingIdToLocal)
throws IOException {
if (responseSets.size() == responseIDs.size()) {
responseLinking.responseSetIds(ImmutableBiMap.copyOf(responseIDs));
responseLinking.build();
} else if (responseSets.size() > responseIDs.size() || !foreignLinkingIdToLocal.isPresent()) {
throw new IOException("Read " + responseSets.size() + " response sets but "
+ responseIDs.size() + " ID assignments");
} else {
log.warn(
"Warning - converting ResponseSet IDs and saving them, this is almost definitely an error!");
final ImmutableMultimap<ResponseSet, String> responseSetToIds =
responseIDs.asMultimap().inverse();
final LaxImmutableMapBuilder<String, ResponseSet> idsMapB =
MapUtils.immutableMapBuilderAllowingSameEntryTwice();
for (final Map.Entry<ResponseSet, Collection<String>> setAndIds : responseSetToIds.asMap()
.entrySet()) {
final Collection<String> ids = setAndIds.getValue();
final String selectedID =
checkNotNull(getFirst(usingToString().immutableSortedCopy(ids), null));
for (final String oldId : ids) {
log.debug("Selecting id {} for cluster {}", selectedID, oldId);
foreignLinkingIdToLocal.get().put(oldId, selectedID);
idsMapB.put(selectedID, responseIDs.get(oldId));
}
}
responseLinking.responseSetIds(ImmutableBiMap.copyOf(idsMapB.build()));
}
}
MutableOffsetMap(final ImmutableMap<K, Integer> offsets, final Map<K, V> source) {
this(offsets, new Object[offsets.size()]);
for (Entry<K, V> e : source.entrySet()) {
objects[offsets.get(e.getKey())] = requireNonNull(e.getValue());
}
this.needClone = false;
}
static XmlResourceValue parsePublic(
XMLEventReader eventReader, StartElement start, Namespaces.Collector namespacesCollector)
throws XMLStreamException {
namespacesCollector.collectFrom(start);
// The tag should be unary.
if (!isEndTag(eventReader.peek(), start.getName())) {
throw new XMLStreamException(
String.format("<public> tag should be unary %s", start), start.getLocation());
}
// The tag should have a valid type attribute, and optionally an id attribute.
ImmutableMap<String, String> attributes = ImmutableMap.copyOf(parseTagAttributes(start));
String typeAttr = attributes.get(SdkConstants.ATTR_TYPE);
ResourceType type;
if (typeAttr != null) {
type = ResourceType.getEnum(typeAttr);
if (type == null || type == ResourceType.PUBLIC) {
throw new XMLStreamException(
String.format("<public> tag has invalid type attribute %s", start),
start.getLocation());
}
} else {
throw new XMLStreamException(
String.format("<public> tag missing type attribute %s", start), start.getLocation());
}
String idValueAttr = attributes.get(SdkConstants.ATTR_ID);
Optional<Integer> id = Optional.absent();
if (idValueAttr != null) {
try {
id = Optional.of(Integer.decode(idValueAttr));
} catch (NumberFormatException e) {
throw new XMLStreamException(
String.format("<public> has invalid id number %s", start), start.getLocation());
}
}
if (attributes.size() > 2) {
throw new XMLStreamException(
String.format("<public> has unexpected attributes %s", start), start.getLocation());
}
return PublicXmlResourceValue.create(type, id);
}
static Optional<String> getModuleName(ImmutableMap<Path, SourcePath> links) {
if (links.size() > 0) {
return Optional.of(links.keySet().iterator().next().getName(0).toString());
} else {
return Optional.empty();
}
}
@Override
@SuppressWarnings("unchecked")
public BuildFileManifest getManifest(Path buildFile)
throws BuildFileParseException, InterruptedException, IOException {
LOG.verbose("Started parsing build file %s", buildFile);
ParseBuckFileEvent.Started startEvent =
ParseBuckFileEvent.started(
buildFile, ParseBuckFileEvent.ParserKind.SKYLARK, this.getClass());
buckEventBus.post(startEvent);
int rulesParsed = 0;
try {
ParseResult parseResult = parse(buildFile);
ImmutableMap<String, Map<String, Object>> rawRules = parseResult.getRawRules();
rulesParsed = rawRules.size();
// By contract, BuildFileManifestPojoizer converts any Map to ImmutableMap.
// ParseResult.getRawRules() returns ImmutableMap<String, Map<String, Object>>, so it is
// a safe downcast here
ImmutableMap<String, ImmutableMap<String, Object>> targets =
(ImmutableMap<String, ImmutableMap<String, Object>>)
getBuildFileManifestPojoizer().convertToPojo(rawRules);
rulesParsed = targets.size();
return BuildFileManifest.of(
targets,
ImmutableSortedSet.copyOf(parseResult.getLoadedPaths()),
(ImmutableMap<String, Object>)
(ImmutableMap<String, ? extends Object>) parseResult.getReadConfigurationOptions(),
Optional.empty(),
parseResult.getGlobManifestWithResult(),
ImmutableList.of());
} finally {
LOG.verbose("Finished parsing build file %s", buildFile);
buckEventBus.post(ParseBuckFileEvent.finished(startEvent, rulesParsed, 0L, Optional.empty()));
}
}
public void readConnect(final @NotNull ChannelHandlerContext ctx, final @NotNull CONNECT connect) {
final Channel channel = ctx.channel();
final String clientId = channel.attr(ChannelAttributes.CLIENT_ID).get();
if (clientId == null) {
return;
}
final ImmutableMap<String, ConnectInboundInterceptorProvider> providers =
interceptors.connectInboundInterceptorProviders();
if (providers.isEmpty()) {
ctx.fireChannelRead(connect);
return;
}
final ClientInformation clientInfo = PluginInformationUtil.getAndSetClientInformation(channel, clientId);
final ConnectionInformation connectionInfo = PluginInformationUtil.getAndSetConnectionInformation(channel);
final ConnectInboundProviderInputImpl providerInput =
new ConnectInboundProviderInputImpl(serverInformation, clientInfo, connectionInfo);
final ConnectPacketImpl packet = new ConnectPacketImpl(connect);
final ConnectInboundInputImpl input = new ConnectInboundInputImpl(clientInfo, connectionInfo, packet);
final ExtensionParameterHolder<ConnectInboundInputImpl> inputHolder = new ExtensionParameterHolder<>(input);
final ModifiableConnectPacketImpl modifiablePacket =
new ModifiableConnectPacketImpl(packet, configurationService);
final ConnectInboundOutputImpl output = new ConnectInboundOutputImpl(asyncer, modifiablePacket);
final ExtensionParameterHolder<ConnectInboundOutputImpl> outputHolder = new ExtensionParameterHolder<>(output);
final ConnectInterceptorContext context =
new ConnectInterceptorContext(clientId, providers.size(), ctx, inputHolder, outputHolder);
for (final Map.Entry<String, ConnectInboundInterceptorProvider> entry : providers.entrySet()) {
final ConnectInboundInterceptorProvider provider = entry.getValue();
final HiveMQExtension extension = hiveMQExtensions.getExtension(entry.getKey());
if (extension == null) { // disabled extension would be null
context.finishInterceptor();
continue;
}
final ConnectInterceptorTask task =
new ConnectInterceptorTask(provider, providerInput, extension.getId(), clientId);
executorService.handlePluginInOutTaskExecution(context, inputHolder, outputHolder, task);
}
}
public void writeConnack(
final @NotNull ChannelHandlerContext ctx,
final @NotNull CONNACK connack,
final @NotNull ChannelPromise promise) {
final Channel channel = ctx.channel();
final String clientId = channel.attr(ChannelAttributes.CLIENT_ID).get();
if (clientId == null) {
return;
}
final ImmutableMap<String, ConnackOutboundInterceptorProvider> providers =
interceptors.connackOutboundInterceptorProviders();
if (providers.isEmpty()) {
ctx.write(connack, promise);
return;
}
final ClientInformation clientInfo = PluginInformationUtil.getAndSetClientInformation(channel, clientId);
final ConnectionInformation connectionInfo = PluginInformationUtil.getAndSetConnectionInformation(channel);
final boolean requestResponseInformation =
Objects.requireNonNullElse(channel.attr(ChannelAttributes.REQUEST_RESPONSE_INFORMATION).get(), false);
final ConnackOutboundProviderInputImpl providerInput =
new ConnackOutboundProviderInputImpl(serverInformation, clientInfo, connectionInfo);
final ConnackPacketImpl packet = new ConnackPacketImpl(connack);
final ConnackOutboundInputImpl input = new ConnackOutboundInputImpl(clientInfo, connectionInfo, packet);
final ExtensionParameterHolder<ConnackOutboundInputImpl> inputHolder = new ExtensionParameterHolder<>(input);
final ModifiableConnackPacketImpl modifiablePacket =
new ModifiableConnackPacketImpl(packet, configurationService, requestResponseInformation);
final ConnackOutboundOutputImpl output = new ConnackOutboundOutputImpl(asyncer, modifiablePacket);
final ExtensionParameterHolder<ConnackOutboundOutputImpl> outputHolder = new ExtensionParameterHolder<>(output);
final ConnackInterceptorContext context =
new ConnackInterceptorContext(clientId, providers.size(), ctx, promise, inputHolder, outputHolder);
for (final Map.Entry<String, ConnackOutboundInterceptorProvider> entry : providers.entrySet()) {
final ConnackOutboundInterceptorProvider provider = entry.getValue();
final HiveMQExtension extension = hiveMQExtensions.getExtension(entry.getKey());
if (extension == null) { // disabled extension would be null
context.finishInterceptor();
continue;
}
final ConnackInterceptorTask task = new ConnackInterceptorTask(provider, providerInput, extension.getId());
executorService.handlePluginInOutTaskExecution(context, inputHolder, outputHolder, task);
}
}
@Transactional(rollbackFor = CustomException.class)
@Override
public ResultVO syncByContainerId(String containerId) {
try {
List<ContainerNetwork> dbList = containerNetworkMapper.selectList(new EntityWrapper<ContainerNetwork>().eq("container_id", containerId));
boolean[] dbFlag = new boolean[dbList.size()];
Arrays.fill(dbFlag, false);
ContainerInfo containerInfo = dockerClient.inspectContainer(containerId);
ImmutableMap<String, AttachedNetwork> networkImmutableMap = containerInfo.networkSettings().networks();
int addCount = 0, deleteCount = 0;
if(networkImmutableMap != null && networkImmutableMap.size() > 0) {
boolean flag = false;
for(AttachedNetwork attachedNetwork : networkImmutableMap.values()) {
String networkId = attachedNetwork.networkId();
if(StringUtils.isNotBlank(networkId)) {
// 判断数据库中是否有该条记录
for(int i=0; i<dbList.size(); i++) {
if(dbFlag[i]) {
continue;
}
if(hasExist(containerId, networkId)) {
dbFlag[i] = true;
flag = true;
break;
}
}
// 保存新纪录
if(!flag) {
ContainerNetwork containerNetwork = new ContainerNetwork(containerId, networkId);
containerNetworkMapper.insert(containerNetwork);
addCount++;
}
}
}
// 删除失效记录
for(int i=0; i< dbList.size(); i++) {
if(!dbFlag[i]) {
containerNetworkMapper.deleteById(dbList.get(i).getId());
deleteCount++;
}
}
}
Map<String, Integer> map = new HashMap<>(16);
map.put("add", addCount);
map.put("delete", deleteCount);
return ResultVOUtils.success(map);
} catch (Exception e) {
return ResultVOUtils.error(ResultEnum.CONTAINER_NETWORK_SYNC_ERROR);
}
}
/**
* Reads all the files generated by a Report into a FilesWithEntryPoint object.
*
* <p>Every FilesWithEntryPoint must have a single link "entry point" that gives users access to
* all the files. If the report generated just one file - we will just link to that file.
*
* <p>However, if the report generated more than one file - the only thing we can safely do is to
* zip all the files and link to the zip file.
*
* <p>As an alternative to using a zip file, we allow the caller to supply an optional "entry
* point" file that will link to all the other files. If that file is given and is "appropriate"
* (exists and is in the correct location) - we will upload all the report files "as is" and link
* to the entry file.
*
* @param destination the location of the output. Either a file or a directory. If a directory -
* then all the files inside that directory are the outputs we're looking for.
* @param entryPointHint If present - a hint to what the entry point to this directory tree is.
* Will only be used if all of the following apply: (a) {@code
* destination.isDirectory()==true}, (b) there are 2 or more files in the {@code destination}
* directory, and (c) {@code entryPointHint.get()} is one of the files nested inside of the
* {@code destination} directory.
*/
static FilesWithEntryPoint readFilesWithEntryPoint(
File destination, Optional<File> entryPointHint, Path rootDir) {
Path destinationPath = rootDir.relativize(toNormalizedPath(destination));
if (destination.isFile()) {
// The destination is a single file - find its root, and add this single file to the
// FilesWithEntryPoint.
return FilesWithEntryPoint.createSingleFile(
destinationPath, toByteArraySupplier(destination));
}
if (!destination.isDirectory()) {
// This isn't a file nor a directory - so it doesn't exist! Return empty FilesWithEntryPoint
return FilesWithEntryPoint.create(ImmutableMap.of(), destinationPath);
}
// The destination is a directory - find all the actual files first
ImmutableMap<Path, Supplier<byte[]>> files =
Streams.stream(Files.fileTraverser().depthFirstPreOrder(destination))
.filter(File::isFile)
.collect(
toImmutableMap(
file -> rootDir.relativize(toNormalizedPath(file)),
GcsPluginUtils::toByteArraySupplier));
if (files.isEmpty()) {
// The directory exists, but is empty. Return empty FilesWithEntryPoint
return FilesWithEntryPoint.create(ImmutableMap.of(), destinationPath);
}
if (files.size() == 1) {
// We got a directory, but it only has a single file. We can link to that.
return FilesWithEntryPoint.create(files, getOnlyElement(files.keySet()));
}
// There are multiple files in the report! We need to check the entryPointHint
Optional<Path> entryPointPath =
entryPointHint.map(file -> rootDir.relativize(toNormalizedPath(file)));
if (entryPointPath.isPresent() && files.containsKey(entryPointPath.get())) {
// We were given the entry point! Use it!
return FilesWithEntryPoint.create(files, entryPointPath.get());
}
// We weren't given an appropriate entry point. But we still need a single link to all this data
// - so we'll zip it and just host a single file.
Path zipFilePath = destinationPath.resolve(destinationPath.getFileName().toString() + ".zip");
return FilesWithEntryPoint.createSingleFile(zipFilePath, createZippedByteArraySupplier(files));
}
/**
* Processes a list of registrar/list-of-threat pairings and sends a notification email to the
* appropriate address.
*/
void emailSpec11Reports(
LocalDate date,
SoyTemplateInfo soyTemplateInfo,
String subject,
ImmutableSet<RegistrarThreatMatches> registrarThreatMatchesSet) {
ImmutableMap.Builder<RegistrarThreatMatches, Throwable> failedMatchesBuilder =
ImmutableMap.builder();
for (RegistrarThreatMatches registrarThreatMatches : registrarThreatMatchesSet) {
RegistrarThreatMatches filteredMatches = filterOutNonPublishedMatches(registrarThreatMatches);
if (!filteredMatches.threatMatches().isEmpty()) {
try {
// Handle exceptions individually per registrar so that one failed email doesn't prevent
// the rest from being sent.
emailRegistrar(date, soyTemplateInfo, subject, filteredMatches);
} catch (Throwable e) {
failedMatchesBuilder.put(registrarThreatMatches, getRootCause(e));
}
}
}
ImmutableMap<RegistrarThreatMatches, Throwable> failedMatches = failedMatchesBuilder.build();
if (!failedMatches.isEmpty()) {
ImmutableList<Map.Entry<RegistrarThreatMatches, Throwable>> failedMatchesList =
failedMatches.entrySet().asList();
// Send an alert email and throw a RuntimeException with the first failure as the cause,
// but log the rest so that we have that information.
Throwable firstThrowable = failedMatchesList.get(0).getValue();
sendAlertEmail(
String.format("Spec11 Emailing Failure %s", date),
String.format("Emailing Spec11 reports failed due to %s", firstThrowable.getMessage()));
for (int i = 1; i < failedMatches.size(); i++) {
logger.atSevere().withCause(failedMatchesList.get(i).getValue()).log(
"Additional exception thrown when sending email to registrar %s, in addition to the"
+ " re-thrown exception",
failedMatchesList.get(i).getKey().clientId());
}
throw new RuntimeException(
"Emailing Spec11 reports failed, first exception:", firstThrowable);
}
sendAlertEmail(
String.format("Spec11 Pipeline Success %s", date),
"Spec11 reporting completed successfully.");
}
/**
* Validates that non-zero fees are acked (i.e. they are specified and the amount matches).
*
* <p>This is used directly by update operations, i.e. those that otherwise don't have implicit
* costs, and is also used as a helper method to validate if fees are required for operations that
* do have implicit costs, e.g. creates and renews.
*/
public static void validateFeesAckedIfPresent(
final Optional<? extends FeeTransformCommandExtension> feeCommand,
FeesAndCredits feesAndCredits)
throws EppException {
// Check for the case where a fee command extension was required but not provided.
// This only happens when the total fees are non-zero and include custom fees requiring the
// extension.
if (!feeCommand.isPresent()) {
if (!feesAndCredits.getEapCost().isZero()) {
throw new FeesRequiredDuringEarlyAccessProgramException(feesAndCredits.getEapCost());
}
if (feesAndCredits.getTotalCost().isZero() || !feesAndCredits.isFeeExtensionRequired()) {
return;
}
throw new FeesRequiredForNonFreeOperationException(feesAndCredits.getTotalCost());
}
List<Fee> fees = feeCommand.get().getFees();
// The schema guarantees that at least one fee will be present.
checkState(!fees.isEmpty());
BigDecimal total = zeroInCurrency(feeCommand.get().getCurrency());
for (Fee fee : fees) {
if (!fee.hasDefaultAttributes()) {
throw new UnsupportedFeeAttributeException();
}
total = total.add(fee.getCost());
}
for (Credit credit : feeCommand.get().getCredits()) {
if (!credit.hasDefaultAttributes()) {
throw new UnsupportedFeeAttributeException();
}
total = total.add(credit.getCost());
}
Money feeTotal;
try {
feeTotal = Money.of(feeCommand.get().getCurrency(), total);
} catch (ArithmeticException e) {
throw new CurrencyValueScaleException();
}
if (!feeTotal.getCurrencyUnit().equals(feesAndCredits.getCurrency())) {
throw new CurrencyUnitMismatchException();
}
// If more than one fees are required, always validate individual fees.
ImmutableMap<FeeType, Money> expectedFeeMap =
buildFeeMap(feesAndCredits.getFees(), feesAndCredits.getCurrency());
if (expectedFeeMap.size() > 1) {
ImmutableMap<FeeType, Money> providedFeeMap =
buildFeeMap(feeCommand.get().getFees(), feeCommand.get().getCurrency());
for (FeeType type : expectedFeeMap.keySet()) {
if (!providedFeeMap.containsKey(type)) {
throw new FeesMismatchException(type);
}
Money expectedCost = expectedFeeMap.get(type);
if (!providedFeeMap.get(type).isEqual(expectedCost)) {
throw new FeesMismatchException(type, expectedCost);
}
}
}
// Checking if total amount is expected. Extra fees that we are not expecting may be passed in.
// Or if there is only a single fee type expected.
if (!feeTotal.equals(feesAndCredits.getTotalCost())) {
throw new FeesMismatchException(feesAndCredits.getTotalCost());
}
}