View Javadoc
1   /*
2    * #%L
3    * wcm.io
4    * %%
5    * Copyright (C) 2024 wcm.io
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package io.wcm.handler.mediasource.ngdm.impl.metadata;
21  
22  import static com.day.cq.dam.api.DamConstants.TIFF_IMAGELENGTH;
23  import static com.day.cq.dam.api.DamConstants.TIFF_IMAGEWIDTH;
24  
25  import java.util.Collections;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Objects;
29  import java.util.TreeMap;
30  import java.util.stream.Collectors;
31  
32  import org.apache.commons.lang3.StringUtils;
33  import org.apache.commons.lang3.builder.ToStringBuilder;
34  import org.apache.sling.api.resource.ValueMap;
35  import org.apache.sling.api.wrappers.ValueMapDecorator;
36  import org.jetbrains.annotations.NotNull;
37  import org.jetbrains.annotations.Nullable;
38  
39  import com.fasterxml.jackson.core.JsonProcessingException;
40  import com.fasterxml.jackson.databind.json.JsonMapper;
41  
42  import io.wcm.handler.media.Dimension;
43  import io.wcm.handler.mediasource.ngdm.impl.metadata.MetadataResponse.RepositoryMetadata;
44  import io.wcm.wcm.commons.contenttype.ContentType;
45  import io.wcm.wcm.commons.util.ToStringStyle;
46  
47  /**
48   * Metadata for Next Gen Dynamic Media asset fetched from the HTTP API.
49   */
50  public final class NextGenDynamicMediaMetadata {
51  
52    private final String mimeType;
53    private final Long fileSize;
54    private final Dimension dimension;
55    private final String assetStatus;
56    private final ValueMap properties;
57    private final List<SmartCrop> smartCrops;
58  
59    private static final JsonMapper OBJECT_MAPPER = new JsonMapper();
60    static final String RT_RENDITION_SMARTCROP = "dam/rendition/smartcrop";
61  
62    NextGenDynamicMediaMetadata(@Nullable String mimeType, @Nullable Long fileSize, @Nullable Dimension dimension,
63        @Nullable String assetStatus, @Nullable ValueMap properties, @Nullable List<SmartCrop> smartCrops) {
64      this.mimeType = mimeType;
65      this.fileSize = fileSize;
66      this.dimension = dimension;
67      this.assetStatus = assetStatus;
68      if (properties != null) {
69        this.properties = properties;
70      }
71      else {
72        this.properties = ValueMap.EMPTY;
73      }
74      if (smartCrops != null) {
75        this.smartCrops = smartCrops;
76      }
77      else {
78        this.smartCrops = Collections.emptyList();
79      }
80    }
81  
82    /**
83     * @return Mime type
84     */
85    public @NotNull String getMimeType() {
86      return Objects.toString(mimeType, ContentType.OCTET_STREAM);
87    }
88  
89    /**
90     * @return File size (in bytes) or null if not available
91     */
92    public @Nullable Long getFileSize() {
93      return fileSize;
94    }
95  
96    /**
97     * @return Image Dimension or null if no image or dimension not available
98     */
99    public @Nullable Dimension getDimension() {
100     return dimension;
101   }
102 
103   /**
104    * @return Asset review status
105    */
106   public String getAssetStatus() {
107     return this.assetStatus;
108   }
109 
110   /**
111    * @return Asset properties
112    */
113   public ValueMap getProperties() {
114     return properties;
115   }
116 
117   /**
118    * @return Named smart crop definitions.
119    */
120   public List<SmartCrop> getSmartCrops() {
121     return Collections.unmodifiableList(smartCrops);
122   }
123 
124   /**
125    * @return true if metadata is valid (has mime type)
126    */
127   public boolean isValid() {
128     return mimeType != null;
129   }
130 
131   @Override
132   public String toString() {
133     return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_OMIT_NULL_STYLE)
134         .append("mimeType", mimeType)
135         .append("fileSize", fileSize)
136         .append("dimension", dimension)
137         .append("assetStatus", assetStatus)
138         .append("properties", properties.isEmpty() ? null : new TreeMap<String, Object>(properties))
139         .append("smartCrops", smartCrops.isEmpty() ? null : smartCrops)
140         .toString();
141   }
142 
143   /**
144    * Converts JSON response from NGDM API to metadata object.
145    * @param jsonResponse JSON response
146    * @return Metadata object
147    * @throws JsonProcessingException If JSON parsing fails
148    */
149   @SuppressWarnings("null")
150   public static @NotNull NextGenDynamicMediaMetadata fromJson(@NotNull String jsonResponse) throws JsonProcessingException {
151     MetadataResponse response = OBJECT_MAPPER.readValue(jsonResponse, MetadataResponse.class);
152     RepositoryMetadata respositoryMetadata = response.repositoryMetadata;
153     Map<String, Object> assetMetadata = response.assetMetadata;
154     ValueMap properties = null;
155 
156     long width = 0;
157     long height = 0;
158     String assetStatus = null;
159     if (assetMetadata != null) {
160       properties = new ValueMapDecorator(assetMetadata);
161       width = properties.get(TIFF_IMAGEWIDTH, 0L);
162       height = properties.get(TIFF_IMAGELENGTH, 0L);
163       assetStatus = properties.get("dam:assetStatus", String.class);
164     }
165     Dimension dimension = toDimension(width, height);
166 
167     String mimeType = null;
168     Long fileSize = null;
169     List<SmartCrop> smartCrops = null;
170     if (respositoryMetadata != null) {
171       mimeType = respositoryMetadata.dcFormat;
172       fileSize = respositoryMetadata.repoSize;
173       if (respositoryMetadata.smartCrops != null && dimension != null) {
174         smartCrops = respositoryMetadata.smartCrops.entrySet().stream()
175             .filter(entry -> isSmartCropDefinitionValid(entry.getKey(), entry.getValue()))
176             .map(entry -> new SmartCrop(entry.getKey(), entry.getValue(), dimension))
177             .collect(Collectors.toList());
178       }
179     }
180 
181     return new NextGenDynamicMediaMetadata(mimeType, fileSize, dimension, assetStatus, properties, smartCrops);
182   }
183 
184   private static @Nullable Dimension toDimension(long width, long height) {
185     if (width > 0 && height > 0) {
186       return new Dimension(width, height);
187     }
188     return null;
189   }
190 
191   private static boolean isSmartCropDefinitionValid(@NotNull String name, @NotNull MetadataResponse.SmartCrop smartCop) {
192     return StringUtils.isNotBlank(name)
193         && smartCop.normalizedWidth > 0
194         && smartCop.normalizedHeight > 0
195         && smartCop.left >= 0
196         && smartCop.top >= 0;
197   }
198 
199 }