View Javadoc
1   /*
2    * #%L
3    * wcm.io
4    * %%
5    * Copyright (C) 2014 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.dam.impl;
21  
22  import org.apache.commons.io.FilenameUtils;
23  import org.apache.commons.lang3.StringUtils;
24  import org.apache.sling.api.adapter.Adaptable;
25  import org.apache.sling.api.adapter.SlingAdaptable;
26  import org.apache.sling.api.resource.Resource;
27  import org.apache.sling.api.resource.ValueMap;
28  import org.apache.sling.api.wrappers.ValueMapDecorator;
29  import org.jetbrains.annotations.NotNull;
30  import org.jetbrains.annotations.Nullable;
31  
32  import com.day.cq.dam.api.DamConstants;
33  
34  import io.wcm.handler.media.Asset;
35  import io.wcm.handler.media.CropDimension;
36  import io.wcm.handler.media.Dimension;
37  import io.wcm.handler.media.Media;
38  import io.wcm.handler.media.MediaArgs;
39  import io.wcm.handler.media.MediaFileType;
40  import io.wcm.handler.media.Rendition;
41  import io.wcm.handler.media.UriTemplate;
42  import io.wcm.handler.media.UriTemplateType;
43  import io.wcm.handler.media.spi.MediaHandlerConfig;
44  import io.wcm.handler.mediasource.dam.AssetRendition;
45  import io.wcm.handler.mediasource.dam.impl.dynamicmedia.DynamicMediaSupportService;
46  import io.wcm.handler.mediasource.dam.impl.weboptimized.WebOptimizedImageDeliveryService;
47  import io.wcm.wcm.commons.util.AemObjectReflectionToStringBuilder;
48  
49  /**
50   * {@link Asset} implementation for DAM assets.
51   */
52  public final class DamAsset extends SlingAdaptable implements Asset {
53  
54    private final com.day.cq.dam.api.Asset asset;
55    private final CropDimension cropDimension;
56    private final Integer rotation;
57    private final MediaArgs defaultMediaArgs;
58    private final DamContext damContext;
59  
60    /**
61     * @param media Media metadata
62     * @param asset DAM asset
63     * @param mediaHandlerConfig Media handler config
64     * @param dynamicMediaSupportService Dynamic media support service
65     * @param webOptimizedImageDeliveryService Web optimized image delivery service
66     * @param adaptable Adaptable from current context
67     */
68    public DamAsset(Media media, com.day.cq.dam.api.Asset asset, MediaHandlerConfig mediaHandlerConfig,
69        DynamicMediaSupportService dynamicMediaSupportService,
70        WebOptimizedImageDeliveryService webOptimizedImageDeliveryService,
71        Adaptable adaptable) {
72      this.asset = asset;
73      this.cropDimension = rescaleCropDimension(asset, media.getCropDimension());
74      this.rotation = media.getRotation();
75      this.defaultMediaArgs = media.getMediaRequest().getMediaArgs();
76      this.damContext = new DamContext(asset, defaultMediaArgs, mediaHandlerConfig,
77          dynamicMediaSupportService, webOptimizedImageDeliveryService, adaptable);
78    }
79  
80    /**
81     * Crop dimension stored in repository is always calucated against the web-enabled rendition of an asset.
82     * Rescale the crop-dimension here once to calculate it against the original image, which will be used for the actual
83     * cropping.
84     * @param asset Asset
85     * @param cropDimension Crop dimension from repository/input parameters
86     * @return Rescaled crop dimension
87     */
88    private static @Nullable CropDimension rescaleCropDimension(@NotNull com.day.cq.dam.api.Asset asset, @Nullable CropDimension cropDimension) {
89      if (cropDimension == null) {
90        return null;
91      }
92      return WebEnabledRenditionCropping.getCropDimensionForOriginal(asset, cropDimension);
93    }
94  
95    @Override
96    public String getTitle() {
97      String title = getPropertyAwareOfArray(DamConstants.DC_TITLE);
98      // fallback to asset name if title is empty
99      return StringUtils.defaultString(title, damContext.getAsset().getName());
100   }
101 
102   /**
103    * Get string value from properties. If value is an array, get first item of array.
104    * It might happen that the adobe xmp lib creates an array, e.g. if the asset file already has a title attribute.
105    * @param propertyName Property name
106    * @return Single value
107    */
108   private @Nullable String getPropertyAwareOfArray(@NotNull String propertyName) {
109     Object valueObject = asset.getMetadataValueFromJcr(propertyName);
110     String value = null;
111     if (valueObject != null) {
112       if (valueObject instanceof Object[]) {
113         Object[] valueArray = (Object[])valueObject;
114         if (valueArray.length > 0) {
115           value = valueArray[0].toString();
116         }
117       }
118       else {
119         value = valueObject.toString();
120       }
121     }
122     return StringUtils.defaultIfBlank(value, null);
123   }
124 
125   @Override
126   public String getAltText() {
127     if (defaultMediaArgs.isDecorative()) {
128       return "";
129     }
130     if (!defaultMediaArgs.isForceAltValueFromAsset() && StringUtils.isNotEmpty(defaultMediaArgs.getAltText())) {
131       return defaultMediaArgs.getAltText();
132     }
133     return StringUtils.defaultString(getDescription(), getTitle());
134   }
135 
136   @Override
137   public String getDescription() {
138     return getPropertyAwareOfArray(DamConstants.DC_DESCRIPTION);
139   }
140 
141   @Override
142   public @NotNull String getPath() {
143     return this.damContext.getAsset().getPath();
144   }
145 
146   @Override
147   public @NotNull ValueMap getProperties() {
148     return new ValueMapDecorator(asset.getMetadata());
149   }
150 
151   @Override
152   public Rendition getDefaultRendition() {
153     return getRendition(this.defaultMediaArgs);
154   }
155 
156   @Override
157   public Rendition getRendition(@NotNull MediaArgs mediaArgs) {
158     Rendition rendition = getDamRendition(mediaArgs);
159 
160     // check if rendition is valid - otherwise return null
161     if (StringUtils.isEmpty(rendition.getUrl())) {
162       rendition = null;
163     }
164 
165     return rendition;
166   }
167 
168   @Override
169   public Rendition getImageRendition(@NotNull MediaArgs mediaArgs) {
170     Rendition rendition = getRendition(mediaArgs);
171     if (rendition != null && rendition.isImage()) {
172       return rendition;
173     }
174     else {
175       return null;
176     }
177   }
178 
179   @Override
180   public Rendition getDownloadRendition(@NotNull MediaArgs mediaArgs) {
181     Rendition rendition = getRendition(mediaArgs);
182     if (rendition != null && rendition.isDownload()) {
183       return rendition;
184     }
185     else {
186       return null;
187     }
188   }
189 
190   /**
191    * Get DAM rendition instance.
192    * @param mediaArgs Media args
193    * @return DAM rendition instance (may be invalid rendition)
194    */
195   protected Rendition getDamRendition(MediaArgs mediaArgs) {
196     return new DamRendition(this.cropDimension, this.rotation, mediaArgs, damContext);
197   }
198 
199   @Override
200   @SuppressWarnings({ "unchecked", "null" })
201   public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
202     if (type == com.day.cq.dam.api.Asset.class) {
203       return (AdapterType)this.damContext.getAsset();
204     }
205     if (type == Resource.class) {
206       return (AdapterType)this.damContext.getAsset().adaptTo(Resource.class);
207     }
208     return super.adaptTo(type);
209   }
210 
211   @Override
212   public @NotNull UriTemplate getUriTemplate(@NotNull UriTemplateType type) {
213     String extension = FilenameUtils.getExtension(damContext.getAsset().getName());
214     if (!MediaFileType.isImage(extension) || MediaFileType.isVectorImage(extension)) {
215       throw new UnsupportedOperationException("Unable to build URI template for this asset type: " + getPath());
216     }
217     com.day.cq.dam.api.Rendition original = damContext.getAsset().getOriginal();
218     Dimension dimension = AssetRendition.getDimension(original);
219     if (dimension == null) {
220       throw new IllegalArgumentException("Unable to get dimension for original rendition of asset: " + getPath());
221     }
222     return new DamUriTemplate(type, dimension, original, null, null, null, damContext);
223   }
224 
225   @Override
226   public String toString() {
227     return new AemObjectReflectionToStringBuilder(this,
228         io.wcm.wcm.commons.util.ToStringStyle.SHORT_PREFIX_OMIT_NULL_STYLE).build();
229   }
230 
231 }