Skip to content

Commit 86ec198

Browse files
author
Amir Tocker
committed
Add Conditional Transformations
1 parent c320d58 commit 86ec198

File tree

4 files changed

+441
-150
lines changed

4 files changed

+441
-150
lines changed

cloudinary-core/src/main/java/com/cloudinary/Transformation.java

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
package com.cloudinary;
22

3-
import java.util.ArrayList;
4-
import java.util.HashMap;
5-
import java.util.List;
6-
import java.util.Map;
7-
import java.util.SortedMap;
8-
import java.util.TreeMap;
3+
import java.util.*;
94
import java.util.regex.Matcher;
105
import java.util.regex.Pattern;
116

127
import com.cloudinary.transformation.AbstractLayer;
8+
import com.cloudinary.transformation.Condition;
139
import com.cloudinary.utils.ObjectUtils;
1410
import com.cloudinary.utils.StringUtils;
1511

@@ -352,6 +348,41 @@ public Transformation responsiveWidth(boolean value) {
352348
return param("responsive_width", value);
353349
}
354350

351+
public Condition ifCondition() {
352+
return new Condition().setParent(this);
353+
}
354+
public Transformation ifCondition(String condition) {
355+
return param("if", condition);
356+
}
357+
public Transformation ifElse() {
358+
chain();
359+
return param("if", "else");
360+
}
361+
362+
public Transformation endIf() {
363+
364+
chain();
365+
int transSize = this.transformations.size();
366+
for (int i = transSize - 1; i >= 0; i--) {
367+
Map segment = this.transformations.get(i); // [..., {if: "w_gt_1000",c: "fill", w: 500}, ...]
368+
Object value = segment.get("if");
369+
if (value != null) { // if: "w_gt_1000"
370+
String ifValue = value.toString();
371+
if (ifValue.equals("end")) break;
372+
if (segment.size() > 1) {
373+
segment.remove("if"); // {c: fill, w: 500}
374+
transformations.set(i, segment); // [..., {c: fill, w: 500}, ...]
375+
transformations.add(i, ObjectUtils.asMap("if", value)); // // [..., "if_w_gt_1000", {c: fill, w: 500}, ...]
376+
}
377+
if (!"else".equals(ifValue)) break; // otherwise keep looking for if_condition
378+
}
379+
}
380+
381+
param("if", "end");
382+
return chain();
383+
}
384+
385+
355386
public boolean isResponsive() {
356387
return this.isResponsive;
357388
}
@@ -403,7 +434,9 @@ public String toString() {
403434
public String generate(Iterable<Map> optionsList) {
404435
List<String> components = new ArrayList<String>();
405436
for (Map options : optionsList) {
406-
components.add(generate(options));
437+
if(options.size() > 0){
438+
components.add(generate(options));
439+
}
407440
}
408441
return StringUtils.join(components, "/");
409442
}
@@ -539,6 +572,12 @@ public String generate(Map options) {
539572
if (raw_transformation != null) {
540573
components.add(raw_transformation);
541574
}
575+
576+
String ifValue = (String) options.get("if");
577+
if(ifValue != null){
578+
components.add(0, "if_" + new Condition(ifValue).toString());
579+
}
580+
542581
if (!components.isEmpty()) {
543582
transformations.add(StringUtils.join(components, ","));
544583
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package com.cloudinary.transformation;
2+
3+
import com.cloudinary.Transformation;
4+
import com.cloudinary.utils.ObjectUtils;
5+
import com.cloudinary.utils.StringUtils;
6+
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
import java.util.Map;
10+
11+
/**
12+
*
13+
*/
14+
public class Condition {
15+
public static final Map OPERATORS = ObjectUtils.asMap(
16+
"=", "eq",
17+
"!=", "ne",
18+
"<", "lt",
19+
">", "gt",
20+
"<=", "lte",
21+
">=", "gte",
22+
"&&", "and",
23+
"||", "or");
24+
25+
protected List<String> predicateList = null;
26+
private Transformation parent = null;
27+
28+
public Condition( ) {
29+
predicateList = new ArrayList<String>();
30+
31+
}
32+
public Condition( String conditionStr) {
33+
this();
34+
if (conditionStr != null) {
35+
predicateList.add( literal(conditionStr));
36+
}
37+
}
38+
39+
private String literal(String conditionStr) {
40+
String[] list = conditionStr.split("[ _]+");
41+
String[] translated = new String[list.length];
42+
for (int i = 0, j = 0; i < list.length; i++) {
43+
String s = list[i];
44+
if (OPERATORS.containsKey(s)) {
45+
translated[j++] = (String) OPERATORS.get(s);
46+
} else {
47+
translated[j++] = s;
48+
}
49+
}
50+
return StringUtils.join(translated, "_");
51+
}
52+
public Transformation getParent() { return parent;}
53+
public Condition setParent( Transformation parent) {
54+
this.parent = parent;
55+
return this;
56+
}
57+
58+
public String serialize() { return StringUtils.join(predicateList, "_");}
59+
60+
@Override
61+
public String toString() {
62+
return serialize();
63+
}
64+
65+
protected Condition predicate( String name, String operator, String value) {
66+
if (OPERATORS.containsKey(operator)) {
67+
operator = (String) OPERATORS.get(operator);
68+
}
69+
predicateList.add(String.format("%s_%s_%s",name, operator, value));
70+
return this;
71+
}
72+
73+
public Condition and() {
74+
predicateList.add("and");
75+
return this;
76+
}
77+
78+
public Condition or() {
79+
predicateList.add("or");
80+
return this;
81+
}
82+
83+
public Transformation then() {
84+
getParent().ifCondition( serialize());
85+
return getParent();
86+
}
87+
88+
public Condition width(String operator, Object value) {
89+
predicateList.add("w_"+ operator + "_" + value);
90+
return this;
91+
}
92+
93+
public Condition height(String operator, Object value) {
94+
predicateList.add("h_"+ operator + "_" + value);
95+
return this;
96+
}
97+
98+
public Condition aspectRatio(String operator, Object value) {
99+
predicateList.add("ar_"+ operator + "_" + value);
100+
return this;
101+
}
102+
103+
104+
public Condition pages(String operator, Object value) {
105+
predicateList.add("pg_"+ operator + "_" + value);
106+
return this;
107+
}
108+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package com.cloudinary;
2+
3+
import com.cloudinary.utils.ObjectUtils;
4+
import org.cloudinary.json.JSONArray;
5+
import org.junit.After;
6+
import org.junit.Before;
7+
import org.junit.Test;
8+
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
import java.util.Map;
12+
13+
import static org.junit.Assert.*;
14+
15+
/**
16+
*
17+
*/
18+
@SuppressWarnings("unchecked")
19+
public class TransformationTest {
20+
21+
@Before
22+
public void setUp() throws Exception {
23+
24+
}
25+
26+
@After
27+
public void tearDown() throws Exception {
28+
29+
}
30+
31+
@Test
32+
public void withLiteral() throws Exception {
33+
Transformation transformation = new Transformation().ifCondition("w_lt_200").crop("fill").height(120).width(80);
34+
assertEquals("should include the if parameter as the first component in the transformation string", "if_w_lt_200,c_fill,h_120,w_80", transformation.toString());
35+
36+
transformation = new Transformation().crop("fill").height(120).ifCondition("w_lt_200").width(80);
37+
assertEquals("should include the if parameter as the first component in the transformation string", "if_w_lt_200,c_fill,h_120,w_80", transformation.toString());
38+
39+
String chained = "[{if: \"w_lt_200\",crop: \"fill\",height: 120, width: 80}, {if: \"w_gt_400\",crop: \"fit\",width: 150,height: 150},{effect: \"sepia\"}]";
40+
List transformations = ObjectUtils.toList(new JSONArray(chained));
41+
42+
transformation = new Transformation(transformations);
43+
assertEquals("should allow multiple conditions when chaining transformations", "if_w_lt_200,c_fill,h_120,w_80/if_w_gt_400,c_fit,h_150,w_150/e_sepia", transformation.toString());
44+
}
45+
46+
@Test
47+
public void literalWithSpaces() throws Exception {
48+
Map map = ObjectUtils.asMap("if", "w < 200", "crop", "fill", "height", 120, "width", 80);
49+
List<Map> list = new ArrayList<Map>();
50+
list.add(map);
51+
Transformation transformation = new Transformation(list);
52+
53+
assertEquals("should translate operators", "if_w_lt_200,c_fill,h_120,w_80", transformation.toString());
54+
}
55+
56+
@Test
57+
public void endIf() throws Exception {
58+
String chained = "[{if: \"w_lt_200\"},\n" +
59+
" {crop: \"fill\", height: 120, width: 80,effect: \"sharpen\"},\n" +
60+
" {effect: \"brightness:50\"},\n" +
61+
" {effect: \"shadow\",color: \"red\"}, {if: \"end\"}]";
62+
List transformations = ObjectUtils.toList(new JSONArray(chained));
63+
64+
Transformation transformation = new Transformation(transformations);
65+
assertEquals("should include the if_end as the last parameter in its component", "if_w_lt_200/c_fill,e_sharpen,h_120,w_80/e_brightness:50/co_red,e_shadow/if_end", transformation.toString());
66+
67+
}
68+
69+
@Test
70+
public void ifElse() throws Exception {
71+
String chained = "[{if: \"w_lt_200\",crop: \"fill\",height: 120,width: 80},\n" +
72+
" {if: \"else\",crop: \"fill\",height: 90, width: 100}]";
73+
List transformations = ObjectUtils.toList(new JSONArray(chained));
74+
75+
Transformation transformation = new Transformation(transformations);
76+
77+
assertEquals("should support if_else with transformation parameters", "if_w_lt_200,c_fill,h_120,w_80/if_else,c_fill,h_90,w_100", transformation.toString());
78+
79+
chained = "[{if: \"w_lt_200\"},\n" +
80+
" {crop: \"fill\",height: 120,width: 80},\n" +
81+
" {if: \"else\"},\n" +
82+
" {crop: \"fill\",height: 90,width: 100}]";
83+
transformations = ObjectUtils.toList(new JSONArray(chained));
84+
85+
transformation = new Transformation(transformations);
86+
assertEquals("if_else should be without any transformation parameters", "if_w_lt_200/c_fill,h_120,w_80/if_else/c_fill,h_90,w_100", transformation.toString());
87+
}
88+
89+
@Test
90+
public void chainedConditions() throws Exception {
91+
Transformation transformation = new Transformation().ifCondition().aspectRatio("gt", "3:4").then().width(100).crop("scale");
92+
assertEquals("passing an operator and a value adds a condition", "if_ar_gt_3:4,c_scale,w_100", transformation.toString());
93+
transformation = new Transformation().ifCondition().aspectRatio("gt", "3:4").and().width("gt", 100).then().width(50).crop("scale");
94+
assertEquals("should chaining condition with `and`", "if_ar_gt_3:4_and_w_gt_100,c_scale,w_50", transformation.toString());
95+
transformation = new Transformation().ifCondition().aspectRatio("gt", "3:4").and().width("gt", 100).or().width("gt", 200).then().width(50).crop("scale");
96+
assertEquals("should chain conditions with `or`", "if_ar_gt_3:4_and_w_gt_100_or_w_gt_200,c_scale,w_50", transformation.toString());
97+
transformation = new Transformation().ifCondition().aspectRatio(">", "3:4").and().width("<=", 100).or().width("gt", 200).then().width(50).crop("scale");
98+
assertEquals("should translate operators", "if_ar_gt_3:4_and_w_lte_100_or_w_gt_200,c_scale,w_50", transformation.toString());
99+
transformation = new Transformation().ifCondition().aspectRatio(">", "3:4").and().width("<=", 100).or().width(">", 200).then().width(50).crop("scale");
100+
assertEquals("should translate operators", "if_ar_gt_3:4_and_w_lte_100_or_w_gt_200,c_scale,w_50", transformation.toString());
101+
transformation = new Transformation().ifCondition().aspectRatio(">=", "3:4").and().pages(">=", 100).or().pages("!=", 0).then().width(50).crop("scale");
102+
assertEquals("should translate operators", "if_ar_gte_3:4_and_pg_gte_100_or_pg_ne_0,c_scale,w_50", transformation.toString());
103+
104+
}
105+
106+
@Test
107+
public void shouldSupportAndTranslateOperators() {
108+
109+
String allOperators =
110+
"if_" +
111+
"w_eq_0_and" +
112+
"_w_ne_0_or" +
113+
"_w_lt_0_and" +
114+
"_w_gt_0_and" +
115+
"_w_lte_0_and" +
116+
"_w_gte_0" +
117+
",e_grayscale";
118+
assertEquals("should support and translate operators: '=', '!=', '<', '>', '<=', '>=', '&&', '||'",
119+
allOperators, new Transformation().ifCondition()
120+
.width("=", 0).and()
121+
.width("!=", 0).or()
122+
.width("<", 0).and()
123+
.width(">", 0).and()
124+
.width("<=", 0).and()
125+
.width(">=", 0)
126+
.then().effect("grayscale").toString());
127+
128+
assertEquals(allOperators, new Transformation().ifCondition("w = 0 && w != 0 || w < 0 and w > 0 and w <= 0 and w >= 0")
129+
.effect("grayscale")
130+
.toString());
131+
}
132+
133+
@Test
134+
public void endIf2() throws Exception {
135+
Transformation transformation = new Transformation().ifCondition().width("gt", 100).and().width("lt", 200).then().width(50).crop("scale").endIf();
136+
assertEquals("should serialize to 'if_end'", "if_w_gt_100_and_w_lt_200/c_scale,w_50/if_end", transformation.toString());
137+
transformation = new Transformation().ifCondition().width("gt", 100).and().width("lt", 200).then().width(50).crop("scale").endIf();
138+
assertEquals("force the if clause to be chained", "if_w_gt_100_and_w_lt_200/c_scale,w_50/if_end", transformation.toString());
139+
transformation = new Transformation().ifCondition().width("gt", 100).and().width("lt", 200).then().width(50).crop("scale").ifElse().width(100).crop("crop").endIf();
140+
assertEquals("force the if_else clause to be chained", "if_w_gt_100_and_w_lt_200/c_scale,w_50/if_else/c_crop,w_100/if_end", transformation.toString());
141+
142+
}
143+
144+
}

0 commit comments

Comments
 (0)