MediaFormatResolver.java
/*
* #%L
* wcm.io
* %%
* Copyright (C) 2019 wcm.io
* %%
* 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.
* #L%
*/
package io.wcm.handler.media.impl;
import static io.wcm.handler.media.MediaNameConstants.MEDIAFORMAT_PROP_PARENT_MEDIA_FORMAT;
import static org.apache.commons.lang3.ArrayUtils.isEmpty;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.wcm.handler.media.MediaArgs;
import io.wcm.handler.media.MediaArgs.ImageSizes;
import io.wcm.handler.media.MediaArgs.MediaFormatOption;
import io.wcm.handler.media.MediaArgs.PictureSource;
import io.wcm.handler.media.MediaArgs.WidthOption;
import io.wcm.handler.media.format.MediaFormat;
import io.wcm.handler.media.format.MediaFormatBuilder;
import io.wcm.handler.media.format.MediaFormatHandler;
/**
* Resolves media formats before starting the media handler processing.
*/
final class MediaFormatResolver {
private final MediaFormatHandler mediaFormatHandler;
private static final Logger log = LoggerFactory.getLogger(MediaFormatResolver.class);
static final String MEDIAFORMAT_NAME_SEPARATOR = "___";
MediaFormatResolver(MediaFormatHandler mediaFormatHandler) {
this.mediaFormatHandler = mediaFormatHandler;
}
/**
* Resolve media format names and responsive media formats.
* @param mediaArgs Media args
* @return true if resolution was successful.
*/
public boolean resolve(MediaArgs mediaArgs) {
return resolveMediaFormatOptionsByNames(mediaArgs)
&& resolvePictureSourcesByNames(mediaArgs)
&& addResponsiveImageMediaFormats(mediaArgs);
}
/**
* Resolve media format names to media formats in media options so all downstream logic has only to handle the
* resolved media formats.
* @param mediaArgs Media args
* @return true if resolution was successful.
*/
private boolean resolveMediaFormatOptionsByNames(MediaArgs mediaArgs) {
MediaFormatOption[] mediaFormatOptions = mediaArgs.getMediaFormatOptions();
if (mediaFormatOptions == null) {
return true;
}
// resolve media format options that have only a name set
boolean resolutionSuccessful = true;
for (int i = 0; i < mediaFormatOptions.length; i++) {
MediaFormatOption option = mediaFormatOptions[i];
String mediaFormatName = option.getMediaFormatName();
if (option.getMediaFormat() == null && mediaFormatName != null) {
MediaFormat mediaFormat = mediaFormatHandler.getMediaFormat(mediaFormatName);
if (mediaFormat == null) {
log.warn("Media format name '{}' is invalid.", option.getMediaFormatName());
resolutionSuccessful = false;
}
mediaFormatOptions[i] = new MediaFormatOption(mediaFormat, option.isMandatory());
}
}
mediaArgs.mediaFormatOptions(mediaFormatOptions);
return resolutionSuccessful;
}
/**
* Resolve media format names to media formats in picture sources so all downstream logic has only to handle the
* resolved media formats.
* @param mediaArgs Media args
* @return true if resolution was successful.
*/
@SuppressWarnings("null")
private boolean resolvePictureSourcesByNames(MediaArgs mediaArgs) {
PictureSource[] pictureSources = mediaArgs.getPictureSources();
if (pictureSources == null) {
return true;
}
// resolve media format options that have only a name set
boolean resolutionSuccessful = true;
for (int i = 0; i < pictureSources.length; i++) {
PictureSource pictureSource = pictureSources[i];
String mediaFormatName = pictureSource.getMediaFormatName();
if (pictureSource.getMediaFormat() == null && mediaFormatName != null) {
MediaFormat mediaFormat = mediaFormatHandler.getMediaFormat(mediaFormatName);
if (mediaFormat == null) {
log.warn("Media format name '{}' is invalid.", pictureSource.getMediaFormatName());
resolutionSuccessful = false;
}
else {
pictureSources[i] = new PictureSource(mediaFormat)
.media(pictureSource.getMedia())
.sizes(pictureSource.getSizes())
.widthOptions(pictureSource.getWidthOptions());
}
}
}
mediaArgs.pictureSources(pictureSources);
return resolutionSuccessful;
}
/**
* Add on-the-fly generated media formats if required for responsive image handling
* via image sizes or picture sources.
* @param mediaArgs Media args
* @return true if resolution was successful
*/
private boolean addResponsiveImageMediaFormats(MediaArgs mediaArgs) {
Map<String, MediaFormatOption> additionalMediaFormats = new LinkedHashMap<>();
// check if additional on-the-fly generated media formats needs to be added for responsive image handling
if (!resolveForImageSizes(mediaArgs, additionalMediaFormats)) {
return false;
}
resolveForResponsivePictureSources(mediaArgs, additionalMediaFormats);
// if additional media formats where found add them to the media format list in media args
if (!additionalMediaFormats.isEmpty()) {
List<MediaFormatOption> allMediaFormats = new ArrayList<>();
MediaFormatOption[] mediaFormatOptions = mediaArgs.getMediaFormatOptions();
if (mediaFormatOptions != null) {
allMediaFormats.addAll(Arrays.asList(mediaFormatOptions));
}
allMediaFormats.addAll(additionalMediaFormats.values());
mediaArgs.mediaFormatOptions(allMediaFormats.toArray(new MediaFormatOption[0]));
}
return true;
}
private boolean resolveForImageSizes(MediaArgs mediaArgs, Map<String, MediaFormatOption> additionalMediaFormats) {
ImageSizes imageSizes = mediaArgs.getImageSizes();
if (imageSizes == null) {
return true;
}
final MediaFormat[] mediaFormats = mediaArgs.getMediaFormats();
if (isEmpty(mediaFormats)) {
log.warn("No media format with ratio given - unable to fulfill resolve image sizes.");
return false;
}
Arrays.stream(mediaFormats)
.filter(Objects::nonNull)
.forEach(mediaFormat -> generateMediaFormatsForWidths(additionalMediaFormats, mediaFormat, true, imageSizes.getWidthOptions()));
return true;
}
private void resolveForResponsivePictureSources(MediaArgs mediaArgs, Map<String, MediaFormatOption> additionalMediaFormats) {
PictureSource[] pictureSources = mediaArgs.getPictureSources();
if (pictureSources == null || pictureSources.length == 0) {
return;
}
for (PictureSource pictureSource : pictureSources) {
generateMediaFormatsForWidths(additionalMediaFormats, pictureSource.getMediaFormat(), false, pictureSource.getWidthOptions());
}
}
private void generateMediaFormatsForWidths(@NotNull Map<String, MediaFormatOption> additionalMediaFormats,
@Nullable MediaFormat mediaFormat, boolean setParent, @NotNull WidthOption @Nullable... widthOptions) {
if (mediaFormat == null || widthOptions == null) {
return;
}
for (WidthOption widthOption : widthOptions) {
MediaFormat widthMediaFormat = MediaFormatBuilder.create(
mediaFormat.getName() + MEDIAFORMAT_NAME_SEPARATOR + widthOption.getWidth())
.label(mediaFormat.getLabel())
.extensions(mediaFormat.getExtensions())
.ratio(mediaFormat.getRatio())
.width(widthOption.getWidth())
.property(MEDIAFORMAT_PROP_PARENT_MEDIA_FORMAT, setParent ? mediaFormat : null)
.build();
additionalMediaFormats.put(widthMediaFormat.getName(), new MediaFormatOption(widthMediaFormat, widthOption.isMandatory()));
}
}
}