Skip to content

Commit a9203ab

Browse files
committed
Update genres and fix incorrect parsing of enums with delimiter in name
1 parent 9153ca2 commit a9203ab

12 files changed

Lines changed: 107 additions & 66 deletions

library/src/main/java/me/proxer/library/api/DelimitedEnumSetAdapterFactory.java

Lines changed: 76 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
import java.lang.reflect.Type;
2121
import java.util.ArrayList;
2222
import java.util.Arrays;
23-
import java.util.Collections;
2423
import java.util.EnumSet;
2524
import java.util.List;
25+
import java.util.Locale;
2626
import java.util.Set;
2727

2828
/**
@@ -60,14 +60,42 @@ private static class DelimitedEnumSetAdapter<T extends Enum<T>> extends JsonAdap
6060
private final Class<T> enumType;
6161
private final String delimiter;
6262

63+
private final T[] enumConstants;
64+
private final String[] enumNames;
65+
private final T fallbackEnum;
66+
6367
DelimitedEnumSetAdapter(final Class<T> enumType, final String delimiter) {
6468
this.enumType = enumType;
6569
this.delimiter = delimiter;
70+
71+
this.enumConstants = enumType.getEnumConstants();
72+
this.enumNames = new String[this.enumConstants.length];
73+
74+
for (int i = 0; i < this.enumConstants.length; i++) {
75+
final T constant = this.enumConstants[i];
76+
77+
try {
78+
final Json annotation = enumType.getField(constant.name()).getAnnotation(Json.class);
79+
final String name = annotation != null ? annotation.name() : constant.name();
80+
81+
this.enumNames[i] = name.toLowerCase(Locale.US);
82+
} catch (NoSuchFieldException e) {
83+
throw new AssertionError("Missing field in " + enumType.getName(), e);
84+
}
85+
}
86+
87+
final FallbackEnum fallbackAnnotation = enumType.getAnnotation(FallbackEnum.class);
88+
89+
if (fallbackAnnotation != null) {
90+
fallbackEnum = Enum.valueOf(enumType, fallbackAnnotation.name());
91+
} else {
92+
fallbackEnum = null;
93+
}
6694
}
6795

6896
@Override
6997
public Set<T> fromJson(final JsonReader reader) throws IOException {
70-
final List<String> parts = parseParts(reader);
98+
final String parts = parseParts(reader);
7199

72100
return convertToEnumConstants(reader, parts);
73101
}
@@ -92,7 +120,7 @@ public void toJson(final JsonWriter writer, @Nullable final Set<T> value) throws
92120
writer.value(result.toString());
93121
}
94122

95-
private List<String> parseParts(final JsonReader reader) throws IOException {
123+
private String parseParts(final JsonReader reader) throws IOException {
96124
final JsonReader.Token nextToken = reader.peek();
97125

98126
if (nextToken == JsonReader.Token.BEGIN_ARRAY) {
@@ -106,57 +134,70 @@ private List<String> parseParts(final JsonReader reader) throws IOException {
106134

107135
reader.endArray();
108136

109-
return result;
137+
return ProxerUtils.join(delimiter, result);
110138
} else if (nextToken == JsonReader.Token.NULL) {
111139
reader.nextNull();
112140

113-
return Collections.emptyList();
141+
return "";
114142
} else {
115-
final String value = reader.nextString();
116-
117-
if (value.isEmpty()) {
118-
return Collections.emptyList();
119-
} else {
120-
return Arrays.asList(value.split(delimiter));
121-
}
143+
return reader.nextString();
122144
}
123145
}
124146

125-
private Set<T> convertToEnumConstants(final JsonReader reader, final List<String> parts) {
147+
private Set<T> convertToEnumConstants(final JsonReader reader, final String parts) {
126148
final EnumSet<T> result = EnumSet.noneOf(enumType);
127149

128-
for (final String part : parts) {
129-
final T[] constants = enumType.getEnumConstants();
130-
final String[] nameStrings = new String[constants.length];
131-
T value = null;
150+
String currentParts = parts.toLowerCase(Locale.US);
132151

133-
try {
134-
for (int i = 0; i < constants.length; i++) {
135-
final T constant = constants[i];
136-
final Json annotation = enumType.getField(constant.name()).getAnnotation(Json.class);
137-
final String name = annotation != null ? annotation.name() : constant.name();
138-
nameStrings[i] = name;
139-
140-
if (name.equalsIgnoreCase(part)) {
141-
value = constant;
152+
while (currentParts.length() > 0) {
153+
boolean found = false;
154+
155+
for (int i = 0; i < enumNames.length; i++) {
156+
final String name = enumNames[i];
157+
158+
if (currentParts.startsWith(name)) {
159+
final boolean isLast = currentParts.length() == name.length();
160+
final boolean matches = isLast || currentParts.startsWith(delimiter, name.length());
161+
162+
if (matches) {
163+
result.add(enumConstants[i]);
164+
165+
if (isLast) {
166+
currentParts = "";
167+
} else {
168+
currentParts = currentParts.substring(name.length() + delimiter.length());
169+
}
170+
171+
found = true;
172+
break;
142173
}
143174
}
144-
} catch (NoSuchFieldException e) {
145-
throw new AssertionError("Missing field in " + enumType.getName(), e);
146175
}
147176

148-
if (value == null) {
149-
final FallbackEnum fallbackAnnotation = enumType.getAnnotation(FallbackEnum.class);
177+
if (!found) {
178+
final int nextDelimiter = currentParts.indexOf(delimiter);
179+
180+
if (fallbackEnum != null) {
181+
result.add(fallbackEnum);
150182

151-
if (fallbackAnnotation != null) {
152-
value = Enum.valueOf(enumType, fallbackAnnotation.name());
183+
if (nextDelimiter >= 0 && currentParts.length() > nextDelimiter + delimiter.length()) {
184+
currentParts = currentParts.substring(nextDelimiter + delimiter.length());
185+
} else {
186+
currentParts = "";
187+
}
153188
} else {
154-
throw new JsonDataException("Expected one of " + Arrays.asList(nameStrings)
189+
final String part;
190+
191+
if (nextDelimiter >= 0) {
192+
part = currentParts.substring(0, nextDelimiter);
193+
} else {
194+
part = currentParts;
195+
}
196+
197+
throw new JsonDataException("Expected one of " + Arrays.asList(enumNames)
155198
+ " but was " + part + " at path " + reader.getPath());
156199
}
157200
}
158-
159-
result.add(value);
160201
}
161202

162203
return result;

library/src/main/java/me/proxer/library/enums/Genre.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
*/
1111
@FallbackEnum(name = "UNKNOWN")
1212
public enum Genre {
13-
@Json(name = "Abenteuer") ADVENTURE,
1413
@Json(name = "Action") ACTION,
1514
@Json(name = "Adult") ADULT,
15+
@Json(name = "Adventure") ADVENTURE,
1616
@Json(name = "Comedy") COMEDY,
1717
@Json(name = "Cyberpunk") CYBERPUNK,
1818
@Json(name = "Drama") DRAMA,
@@ -39,13 +39,13 @@ public enum Genre {
3939
@Json(name = "Shoujou-Ai") SHOUJOU_AI,
4040
@Json(name = "Shounen") SHOUNEN,
4141
@Json(name = "Shounen-Ai") SHOUNEN_AI,
42-
@Json(name = "Slice_of_Life") SLICE_OF_LIFE,
42+
@Json(name = "Slice of Life") SLICE_OF_LIFE,
4343
@Json(name = "Smut") SMUT,
4444
@Json(name = "Splatter") SPLATTER,
4545
@Json(name = "Sport") SPORT,
46+
@Json(name = "Steampunk") STEAMPUNK,
4647
@Json(name = "Supernatural") SUPERNATURAL,
4748
@Json(name = "Superpower") SUPERPOWER,
48-
@Json(name = "Vampire") VAMPIRE,
4949
@Json(name = "Violence") VIOLENCE,
5050
@Json(name = "Yaoi") YAOI,
5151
@Json(name = "Yuri") YURI,

library/src/test/java/me/proxer/library/api/DelimitedEnumSetAdapterFactoryTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public void testCreateNoParameterType() {
7979
@Test
8080
public void testFromJsonSingle() throws IOException {
8181
final GenresTestClass result = moshi.adapter(GenresTestClass.class)
82-
.fromJson("{\"genres\":\"Abenteuer\"}");
82+
.fromJson("{\"genres\":\"Adventure\"}");
8383

8484
assertThat(result).isNotNull();
8585
assertThat(result.genres).containsExactly(Genre.ADVENTURE);
@@ -88,16 +88,16 @@ public void testFromJsonSingle() throws IOException {
8888
@Test
8989
public void testFromJsonMultiple() throws IOException {
9090
final GenresTestClass result = moshi.adapter(GenresTestClass.class)
91-
.fromJson("{\"genres\":\"Abenteuer Action\"}");
91+
.fromJson("{\"genres\":\"Adventure Action\"}");
9292

9393
assertThat(result).isNotNull();
94-
assertThat(result.genres).containsExactly(Genre.ADVENTURE, Genre.ACTION);
94+
assertThat(result.genres).containsExactly(Genre.ACTION, Genre.ADVENTURE);
9595
}
9696

9797
@Test
9898
public void testFromJsonInvalidDelimiter() throws IOException {
9999
final GenresTestClass result = moshi.adapter(GenresTestClass.class)
100-
.fromJson("{\"genres\":\"Abenteuer;Action\"}");
100+
.fromJson("{\"genres\":\"Action;Adventure\"}");
101101

102102
assertThat(result).isNotNull();
103103
assertThat(result.genres).containsExactly(Genre.UNKNOWN);
@@ -154,7 +154,7 @@ public void testToJsonMultiple() {
154154
final GenresTestClass testSubject = new GenresTestClass(EnumSet.of(Genre.ADVENTURE, Genre.ACTION));
155155
final String result = moshi.adapter(GenresTestClass.class).toJson(testSubject);
156156

157-
assertThat(result).isEqualTo("{\"genres\":\"Abenteuer Action\"}");
157+
assertThat(result).isEqualTo("{\"genres\":\"Action Adventure\"}");
158158
}
159159

160160
@Value

library/src/test/java/me/proxer/library/api/EnumRetrofitConverterFactoryTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ public void testCreateNoEnum() {
4040
@Test
4141
public void testConvert() throws IOException {
4242
assertThat(((Converter<Enum<?>, String>) factory.stringConverter(Genre.class, new Annotation[0], retrofit))
43-
.convert(Genre.ADVENTURE)).isEqualTo("Abenteuer");
43+
.convert(Genre.ADVENTURE)).isEqualTo("Adventure");
4444
}
4545
}

library/src/test/java/me/proxer/library/util/ProxerUtilsTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ public void testJoinEmpty() {
6060

6161
@Test
6262
public void testJoinEnums() {
63-
assertThat(ProxerUtils.joinEnums(";", EnumSet.of(Genre.ADVENTURE, Genre.ACTION, Genre.DRAMA)))
64-
.isEqualTo("Abenteuer;Action;Drama");
63+
assertThat(ProxerUtils.joinEnums(";", EnumSet.of(Genre.ACTION, Genre.ADVENTURE, Genre.SLICE_OF_LIFE)))
64+
.isEqualTo("Action;Adventure;Slice of Life");
6565
}
6666

6767
@Test

library/src/test/resources/calendar.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"weekday": "sat",
1414
"uptime": "1518266091",
1515
"entryname": "Time Bokan: Gyakushuu no San Akunin",
16-
"genre": "Abenteuer Comedy Mecha",
16+
"genre": "Adventure Comedy Mecha",
1717
"rate_sum": "7",
1818
"rate_count": "2",
1919
"industryname": null

library/src/test/resources/entry.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"data": {
55
"id": "6174",
66
"name": "LuCu LuCu",
7-
"genre": "Comedy Fantasy Seinen Slice_of_Life",
7+
"genre": "Comedy Fantasy Seinen Slice of Life",
88
"fsk": "",
99
"description": "Humans are a despicable lot, committing sin after sin, filling the endless boundaries of the underworld with tortured souls. Now, it would seem, Hell isn't so endless after all, and it has become dangerously close to filling, and then overflowing into the human realm. Princess Lucuha sees this imminent disaster and has a plan: save Hell by making humans decent again. Of course, the angels can't simply allow demons to roam freely on Earth, and they do their best to stop Lucu and her dastardly plans.",
1010
"medium": "mangaseries",

library/src/test/resources/entry_core.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"data": {
55
"id": "6174",
66
"name": "LuCu LuCu",
7-
"genre": "Comedy Fantasy Seinen Slice_of_Life",
7+
"genre": "Comedy Fantasy Seinen Slice of Life",
88
"fsk": "bad_language",
99
"description": "Humans are a despicable lot, committing sin after sin, filling the endless boundaries of the underworld with tortured souls. Now, it would seem, Hell isn't so endless after all, and it has become dangerously close to filling, and then overflowing into the human realm. Princess Lucuha sees this imminent disaster and has a plan: save Hell by making humans decent again. Of course, the angels can't simply allow demons to roam freely on Earth, and they do their best to stop Lucu and her dastardly plans.",
1010
"medium": "mangaseries",

library/src/test/resources/industry_project_list.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"id": "5653",
2929
"name": "1-en no Otoko",
3030
"fsk": "fsk18 sex",
31-
"genre": "Comedy Drama Romance Slice_of_Life Yaoi",
31+
"genre": "Comedy Drama Romance Slice of Life Yaoi",
3232
"medium": "mangaseries",
3333
"state": "1",
3434
"rate_sum": "169",
@@ -50,7 +50,7 @@
5050
"id": "6627",
5151
"name": "A Channel",
5252
"fsk": "fsk6",
53-
"genre": "Comedy Seinen Slice_of_Life",
53+
"genre": "Comedy Seinen Slice of Life",
5454
"medium": "mangaseries",
5555
"state": "2",
5656
"rate_sum": "63",
@@ -116,7 +116,7 @@
116116
"id": "13635",
117117
"name": "Anne Happy♪",
118118
"fsk": "",
119-
"genre": "Comedy School Seinen Slice_of_Life",
119+
"genre": "Comedy School Seinen Slice of Life",
120120
"medium": "mangaseries",
121121
"state": "2",
122122
"rate_sum": "0",

library/src/test/resources/media_list_entry.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
{
1717
"name": ".hack//G.U. Returner",
1818
"id": "1412",
19-
"genre": "Abenteuer SciFi",
19+
"genre": "Adventure SciFi",
2020
"medium": "ova",
2121
"count": "1",
2222
"state": "1",
@@ -27,7 +27,7 @@
2727
{
2828
"name": ".hack//G.U. Trilogy",
2929
"id": "292",
30-
"genre": "Abenteuer Action Drama SciFi",
30+
"genre": "Adventure Action Drama SciFi",
3131
"medium": "movie",
3232
"count": "1",
3333
"state": "1",
@@ -49,7 +49,7 @@
4949
{
5050
"name": ".hack//LIMINALITY",
5151
"id": "295",
52-
"genre": "Abenteuer Drama Mystery SciFi",
52+
"genre": "Adventure Drama Mystery SciFi",
5353
"medium": "ova",
5454
"count": "4",
5555
"state": "1",
@@ -71,7 +71,7 @@
7171
{
7272
"name": ".hack//Roots",
7373
"id": "296",
74-
"genre": "Abenteuer Action Drama Romance SciFi",
74+
"genre": "Adventure Action Drama Romance SciFi",
7575
"medium": "animeseries",
7676
"count": "26",
7777
"state": "1",
@@ -82,7 +82,7 @@
8282
{
8383
"name": ".hack//Sekai no Mukou ni",
8484
"id": "4642",
85-
"genre": "Abenteuer Action Fantasy Mystery SciFi",
85+
"genre": "Adventure Action Fantasy Mystery SciFi",
8686
"medium": "movie",
8787
"count": "1",
8888
"state": "1",
@@ -93,7 +93,7 @@
9393
{
9494
"name": ".hack//SIGN",
9595
"id": "1148",
96-
"genre": "Abenteuer Drama Magic Mystery Psychological SciFi",
96+
"genre": "Adventure Drama Magic Mystery Psychological SciFi",
9797
"medium": "animeseries",
9898
"count": "26",
9999
"state": "1",

0 commit comments

Comments
 (0)