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