222f970031edce01707969d69cc0f7842d8053a7
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / media / MediaUriTransformationProcessor.java
1 /**
2 * Copyright (C) 2020 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9 package eu.etaxonomy.cdm.api.service.media;
10
11 import java.awt.Point;
12 import java.net.URISyntaxException;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.List;
16 import java.util.Optional;
17 import java.util.regex.Matcher;
18
19 import org.apache.log4j.Logger;
20
21 import eu.etaxonomy.cdm.common.URI;
22 import eu.etaxonomy.cdm.model.media.ImageFile;
23 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
24 import eu.etaxonomy.cdm.model.media.MediaRepresentationPart;
25 import eu.etaxonomy.cdm.model.metadata.PreferencePredicate;
26
27 /**
28 * Creates new purely volatile {@link MediaRepresentation MediaRepresentations} objects based on
29 * a list of {@link MediaUriTransformation} rules. These rules are usually stored in the
30 * per data base {@link PreferencePredicate.MediaRepresentationTransformations MediaRepresentationTransformations} property
31 * (See also {@link MediaToolbox#readTransformations()}).
32 * <p>
33 * These volatile {@link MediaRepresentation MediaRepresentations} objects must not be persisted!
34 *
35 * @author a.kohlbecker
36 * @since Jul 8, 2020
37 */
38 public class MediaUriTransformationProcessor {
39
40 private static final Logger logger = Logger.getLogger(MediaUriTransformationProcessor.class);
41
42 private List<MediaUriTransformation> transformations = new ArrayList<>();
43
44 public void add(MediaUriTransformation transformation) {
45 transformations.add(transformation);
46 }
47
48 public void addAll(Collection<MediaUriTransformation> trans) {
49 transformations.addAll(trans);
50 }
51
52 /**
53 * Applies the the transformations of this processor to the given URI.
54 * And returns the all the URLs which resulted from the transformations
55 * or an empty list if none was applicable.
56 *
57 * @param uri
58 * the URI to transform
59 */
60 public List<URI> applyTo(URI uri) {
61
62 logger.debug("original: " + uri.toString());
63 String pathQueryFragment = buildPathQueryFragment(uri);
64
65 List<URI> newUris = new ArrayList<>();
66 for (MediaUriTransformation transformation : transformations) {
67
68 try {
69 Optional<URI> newUri = uriTransformation(uri, pathQueryFragment, transformation);
70 newUri.ifPresent(u -> newUris.add(u));
71 } catch (URISyntaxException e) {
72 logger.error(e);
73 }
74 }
75
76 return newUris;
77 }
78
79 private Optional<URI> uriTransformation(URI uri, String pathQueryFragment, MediaUriTransformation replacement)
80 throws URISyntaxException {
81
82 String newScheme = uri.getScheme();
83 String newHost = uri.getHost();
84 int newPort = uri.getPort();
85 String newPathQueryFragment = pathQueryFragment;
86
87 boolean isMatch = true;
88 // replace the parts
89 if (replacement.getScheme() != null) {
90 Matcher m = replacement.getScheme().searchPattern().matcher(newScheme);
91 isMatch &= m.find();
92 newScheme = m.replaceAll(replacement.getScheme().getReplace());
93 }
94 if (replacement.getHost() != null) {
95 Matcher m = replacement.getHost().searchPattern().matcher(newHost);
96 isMatch &= m.find();
97 newHost = m.replaceAll(replacement.getHost().getReplace());
98 }
99 // TODO port
100
101 if (replacement.getPathQueryFragment() != null) {
102 Matcher m = replacement.getPathQueryFragment().searchPattern().matcher(newPathQueryFragment);
103 isMatch &= m.find();
104 newPathQueryFragment = m.replaceAll(replacement.getPathQueryFragment().getReplace());
105 }
106 if (isMatch) {
107 // recombine
108 String newURIString = newScheme + "://" + newHost + (newPort > 0 ? ":" + String.valueOf(newPort) : "")
109 + newPathQueryFragment;
110
111 URI newUri = new URI(newURIString);
112 logger.debug("transformed: " + newUri.toString());
113 return Optional.of(newUri);
114 } else {
115 return Optional.empty();
116 }
117 }
118
119 protected String buildPathQueryFragment(URI uri) {
120 String pathQueryFragment = uri.getPath();
121 if (uri.getQuery() != null) {
122 pathQueryFragment += "?" + uri.getQuery();
123 }
124 if (uri.getFragment() != null) {
125 pathQueryFragment += "#" + uri.getFragment();
126 }
127 return pathQueryFragment;
128 }
129
130 @Deprecated
131 public List<MediaRepresentation> makeNewMediaRepresentationsFor(URI uri) {
132
133 List<MediaRepresentation> repr = new ArrayList<>();
134
135 String pathQueryFragment = buildPathQueryFragment(uri);
136
137 for (MediaUriTransformation transformation : transformations) {
138
139 try {
140 Optional<URI> newUri = uriTransformation(uri, pathQueryFragment, transformation);
141 if(newUri.isPresent()) {
142 MediaRepresentation mRepresentation = MediaRepresentation.NewInstance(transformation.getMimeType(), null);
143 MediaRepresentationPart part;
144 if (transformation.getMimeType() != null && transformation.getMimeType().startsWith("image/")) {
145 part = ImageFile.NewInstance(newUri.get(), null, transformation.getHeight(), transformation.getWidth());
146 } else {
147 part = MediaRepresentationPart.NewInstance(newUri.get(), null);
148 }
149 mRepresentation.addRepresentationPart(part);
150 repr.add(mRepresentation);
151 }
152
153 } catch (URISyntaxException e) {
154 logger.error(e);
155 }
156 }
157
158 return repr;
159 }
160
161 public List<MediaRepresentation> makeNewMediaRepresentationsFor(MediaRepresentationPart part) {
162
163 List<MediaRepresentation> repr = new ArrayList<>();
164
165 String pathQueryFragment = buildPathQueryFragment(part.getUri());
166
167 for (MediaUriTransformation transformation : transformations) {
168
169 try {
170 Optional<URI> newUri = uriTransformation(part.getUri(), pathQueryFragment, transformation);
171 if (newUri.isPresent()) {
172 MediaRepresentation mRepresentation = MediaRepresentation.NewInstance(transformation.getMimeType(),
173 null);
174 MediaRepresentationPart newPart;
175 if (transformation.getMimeType() != null && transformation.getMimeType().startsWith("image/")) {
176 if (part instanceof ImageFile) {
177 ImageFile originalImageFile = (ImageFile) part;
178 Point newSize = calculateTargetSize(transformation, originalImageFile.getWidth(),
179 originalImageFile.getHeight());
180 newPart = ImageFile.NewInstance(newUri.get(), null, newSize.y, newSize.x);
181 } else {
182 newPart = ImageFile.NewInstance(newUri.get(), null, transformation.getHeight(),
183 transformation.getWidth());
184 }
185 } else {
186 newPart = MediaRepresentationPart.NewInstance(newUri.get(), null);
187 }
188 mRepresentation.addRepresentationPart(newPart);
189 repr.add(mRepresentation);
190 }
191
192 } catch (URISyntaxException e) {
193 logger.error(e);
194 }
195 }
196
197 return repr;
198 }
199
200 /**
201 *
202 * @param trans
203 * The transfomation
204 * @param originalWidth
205 * @param originalHeight
206 * @param calculateMaxExtend
207 * @return
208 */
209 protected Point calculateTargetSize(MediaUriTransformation trans, Integer originalWidth, Integer originalHeight) {
210
211 if (trans.getWidth() == null && trans.getHeight() == null) {
212 return null;
213 } else if (originalWidth == null || originalHeight == null) {
214 return new Point(trans.getWidth(), trans.getHeight());
215 } else {
216 if(trans.getHeight() != null && trans.getWidth() != null && !trans.isMaxExtend()) {
217 // CROP
218 return new Point(trans.getWidth(), trans.getHeight());
219 } else {
220 // MAX EXTEND
221 float originalAspectRatio = ((float) originalWidth / (float) originalHeight);
222
223 boolean widthIsLimiting = trans.getHeight() == null ||
224 trans.getWidth() != null && trans.getHeight() * originalAspectRatio > trans.getWidth();
225 if (widthIsLimiting){
226 return new Point(trans.getWidth(), Math.round(trans.getWidth() / originalAspectRatio ));
227 } else {
228 return new Point(Math.round(trans.getHeight() * originalAspectRatio), trans.getHeight());
229 }
230 }
231 }
232 }
233 }