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.media;
21  
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.List;
25  import java.util.function.Function;
26  
27  import org.apache.commons.lang3.builder.ToStringBuilder;
28  import org.apache.commons.lang3.builder.ToStringStyle;
29  import org.jdom2.Element;
30  import org.jetbrains.annotations.NotNull;
31  import org.jetbrains.annotations.Nullable;
32  import org.osgi.annotation.versioning.ProviderType;
33  
34  import com.fasterxml.jackson.annotation.JsonIgnore;
35  import com.fasterxml.jackson.annotation.JsonInclude;
36  import com.fasterxml.jackson.annotation.JsonInclude.Include;
37  
38  import io.wcm.handler.commons.dom.HtmlElement;
39  import io.wcm.handler.commons.dom.Span;
40  import io.wcm.handler.media.imagemap.ImageMapArea;
41  import io.wcm.handler.media.spi.MediaSource;
42  
43  /**
44   * Holds information about a media request processed and resolved by {@link MediaHandler}.
45   */
46  @ProviderType
47  @JsonInclude(Include.NON_EMPTY)
48  public final class Media {
49  
50    private final @NotNull MediaSource mediaSource;
51    private @NotNull MediaRequest mediaRequest;
52    private HtmlElement element;
53    private Function<Media, HtmlElement> elementBuilder;
54    private String url;
55    private Asset asset;
56    private Collection<Rendition> renditions;
57    private CropDimension cropDimension;
58    private Integer rotation;
59    private List<ImageMapArea> map;
60    private MediaInvalidReason mediaInvalidReason;
61    private String mediaInvalidReasonCustomMessage;
62    private String markup;
63  
64    /**
65     * @param mediaSource Media source
66     * @param mediaRequest Processed media request
67     */
68    public Media(@NotNull MediaSource mediaSource, @NotNull MediaRequest mediaRequest) {
69      this.mediaSource = mediaSource;
70      this.mediaRequest = mediaRequest;
71    }
72  
73    /**
74     * @return Media source
75     */
76    @JsonIgnore
77    public @NotNull MediaSource getMediaSource() {
78      return this.mediaSource;
79    }
80  
81    /**
82     * @return Media handling request
83     */
84    @JsonIgnore
85    public @NotNull MediaRequest getMediaRequest() {
86      return this.mediaRequest;
87    }
88  
89    /**
90     * @param mediaRequest Media handling request
91     */
92    public void setMediaRequest(@NotNull MediaRequest mediaRequest) {
93      this.mediaRequest = mediaRequest;
94    }
95  
96    /**
97     * @return Html element
98     */
99    @JsonIgnore
100   public @Nullable HtmlElement getElement() {
101     if (this.element == null && this.elementBuilder != null) {
102       this.element = this.elementBuilder.apply(this);
103       this.elementBuilder = null;
104     }
105     return this.element;
106   }
107 
108   /**
109    * @return Media HTML element serialized to string. Returns null if media element is null.
110    */
111   @JsonIgnore
112   public @Nullable String getMarkup() {
113     HtmlElement el = getElement();
114     if (markup == null && el != null) {
115       if (el instanceof Span) {
116         // in case of span get inner HTML markup, do not include span element itself
117         StringBuilder result = new StringBuilder();
118         for (Element child : el.getChildren()) {
119           result.append(child.toString());
120         }
121         markup = result.toString();
122       }
123       else {
124         markup = el.toString();
125       }
126     }
127     return markup;
128   }
129 
130   /**
131    * @param value Function that builds the HTML element representation on demand
132    */
133   public void setElementBuilder(@NotNull Function<Media, HtmlElement> value) {
134     this.elementBuilder = value;
135     this.markup = null;
136   }
137 
138   /**
139    * @return Media URL
140    */
141   public @Nullable String getUrl() {
142     return this.url;
143   }
144 
145   /**
146    * @param value Media URL
147    */
148   public void setUrl(@Nullable String value) {
149     this.url = value;
150   }
151 
152   /**
153    * Get media item info that was resolved during media handler processing
154    * @return Media item
155    */
156   public @Nullable Asset getAsset() {
157     return this.asset;
158   }
159 
160   /**
161    * Set media item that was resolved during media handler processing
162    * @param asset Media item
163    */
164   public void setAsset(@Nullable Asset asset) {
165     this.asset = asset;
166   }
167 
168   /**
169    * Get first (and best-match) rendition that was resolved during media handler processing
170    * @return Rendition
171    */
172   @JsonIgnore
173   public @Nullable Rendition getRendition() {
174     if (this.renditions == null || this.renditions.isEmpty()) {
175       return null;
176     }
177     return this.renditions.iterator().next();
178   }
179 
180   /**
181    * Get all renditions that were resolved during media handler processing
182    * @return Renditions
183    */
184   public @NotNull Collection<Rendition> getRenditions() {
185     if (this.renditions == null) {
186       return Collections.emptyList();
187     }
188     else {
189       return this.renditions;
190     }
191   }
192 
193   /**
194    * Set all renditions that was resolved during media handler processing
195    * @param renditions Renditions
196    */
197   public void setRenditions(@Nullable Collection<Rendition> renditions) {
198     this.renditions = renditions;
199   }
200 
201   /**
202    * @return Crop dimensions (optional)
203    */
204   @JsonIgnore
205   public @Nullable CropDimension getCropDimension() {
206     return this.cropDimension;
207   }
208 
209   /**
210    * @param cropDimension Crop dimensions (optional)
211    */
212   public void setCropDimension(@Nullable CropDimension cropDimension) {
213     this.cropDimension = cropDimension;
214   }
215 
216   /**
217    * @return Image rotation (optional)
218    */
219   @JsonIgnore
220   public @Nullable Integer getRotation() {
221     return this.rotation;
222   }
223 
224   /**
225    * @param rotation Image Rotation (optional)
226    */
227   public void setRotation(@Nullable Integer rotation) {
228     this.rotation = rotation;
229   }
230 
231   /**
232    * @return Image map (optional)
233    */
234   @JsonIgnore
235   public @Nullable List<ImageMapArea> getMap() {
236     return this.map;
237   }
238 
239   /**
240    * @param map Image map (optional)
241    */
242   public void setMap(@Nullable List<ImageMapArea> map) {
243     this.map = map;
244   }
245 
246   /**
247    * @return true if link is valid and was resolved successfully
248    */
249   public boolean isValid() {
250     return (mediaInvalidReason == null);
251   }
252 
253   /**
254    * @return Reason why the requested media could not be resolved and is invalid
255    */
256   @JsonIgnore
257   public @Nullable MediaInvalidReason getMediaInvalidReason() {
258     return this.mediaInvalidReason;
259   }
260 
261   /**
262    * @param mediaInvalidReason Reason why the requested media could not be resolved and is invalid
263    */
264   public void setMediaInvalidReason(@Nullable MediaInvalidReason mediaInvalidReason) {
265     this.mediaInvalidReason = mediaInvalidReason;
266   }
267 
268   /**
269    * @return Custom message when {@link #getMediaInvalidReason()} is set to {@link MediaInvalidReason#CUSTOM}.
270    *         Message is interpreted as i18n key.
271    */
272   public @Nullable String getMediaInvalidReasonCustomMessage() {
273     return this.mediaInvalidReasonCustomMessage;
274   }
275 
276   /**
277    * @param mediaInvalidReasonCustomMessage Custom message when {@link #getMediaInvalidReason()} is set to
278    *          {@link MediaInvalidReason#CUSTOM}. Message is interpreted as i18n key.
279    */
280   public void setMediaInvalidReasonCustomMessage(@Nullable String mediaInvalidReasonCustomMessage) {
281     this.mediaInvalidReasonCustomMessage = mediaInvalidReasonCustomMessage;
282   }
283 
284   @Override
285   public String toString() {
286     ToStringBuilder sb = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
287     if (isValid()) {
288       sb.append("url", getUrl());
289     }
290     else {
291       sb.append("mediaInvalidReason", this.mediaInvalidReason);
292     }
293     sb.append("mediaSource", mediaSource.getId());
294     if (asset != null) {
295       sb.append("asset", asset.getPath());
296     }
297     if (renditions != null) {
298       sb.append("renditions", renditions);
299     }
300     if (cropDimension != null) {
301       sb.append("cropDimension", cropDimension);
302     }
303     if (rotation != null) {
304       sb.append("rotation", rotation);
305     }
306     if (map != null) {
307       sb.append("map", map);
308     }
309     sb.append("mediaRequest", mediaRequest);
310     return sb.build();
311   }
312 
313 }