1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.i18nsystem;
35
36 import static java.util.stream.Collectors.toList;
37
38 import java.lang.reflect.AnnotatedElement;
39 import java.lang.reflect.Field;
40 import java.lang.reflect.InvocationTargetException;
41 import java.lang.reflect.Method;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.Iterator;
45 import java.util.List;
46 import java.util.Optional;
47 import java.util.Set;
48
49 import org.apache.commons.lang3.StringUtils;
50 import org.reflections.ReflectionUtils;
51
52
53
54
55
56
57
58
59 public abstract class AbstractI18nKeyGenerator<T> implements I18nKeyGenerator<T> {
60
61 protected static final String FIELDS = "fields";
62 private static final String LABEL = "label";
63
64
65
66
67
68 @Override
69 public String[] keysFor(String undecoratedResult, T object, AnnotatedElement el) {
70 final List<String> keys = new ArrayList<>();
71 keysFor(keys, object, el);
72 if (undecoratedResult != null) {
73 keys.add(0, undecoratedResult);
74 }
75 return keys.toArray(new String[keys.size()]);
76 }
77
78
79
80
81 @Deprecated
82 @Override
83 public String messageBundleNameFor(T object) {
84 return resolveMessageBundleNameUpwards(object);
85 }
86
87
88
89
90
91 @Deprecated
92 protected String resolveMessageBundleNameUpwards(Object object) {
93 String bundleName = null;
94
95 try {
96 Method getI18nBasename = object.getClass().getMethod("getI18nBasename");
97 bundleName = (String) getI18nBasename.invoke(object);
98 } catch (NoSuchMethodException e) {
99
100 } catch (SecurityException | IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
101 throw new RuntimeException(e);
102 }
103
104 if (bundleName == null) {
105 Object parent = getParentViaCast(object);
106 if (parent != null) {
107 bundleName = resolveMessageBundleNameUpwards(parent);
108 }
109 }
110
111 return bundleName;
112 }
113
114 protected abstract void keysFor(List<String> keys, T object, AnnotatedElement el);
115
116
117
118
119 protected String fieldOrGetterName(AnnotatedElement el) {
120 if (el instanceof Field) {
121 return ((Field) el).getName();
122 } else if (el instanceof Method) {
123 return getterToField((Method) el);
124 } else {
125 throw new IllegalArgumentException("Can't derive i18n key suffix from " + el);
126 }
127 }
128
129
130
131
132
133 protected <P, C> P getParentViaCast(C obj) {
134 if (!I18nParentable.class.isInstance(obj)) {
135 throw new IllegalStateException("Can't reach parent of " + obj);
136 }
137 final I18nParentable<P> cast = I18nParentable.class.cast(obj);
138 return cast.getI18nContextParent();
139 }
140
141
142
143
144
145
146
147 protected <C> List<Object> getAncestors(C obj) {
148 final ArrayList<Object> ancestors = new ArrayList<>();
149 Object p = getParentViaCast(obj);
150 while (p != null) {
151 ancestors.add(p);
152 p = getParentViaCast(p);
153 }
154 return ancestors;
155 }
156
157
158
159
160
161
162 protected <C> Object getRoot(C obj) {
163 Object root = null;
164 Object p = getParentViaCast(obj);
165 while (p != null) {
166 root = p;
167 p = getParentViaCast(p);
168 }
169 return root;
170 }
171
172
173
174
175
176
177 protected <C> List<I18nKeyGenerator> getAncestorKeyGenerators(C obj) {
178 return getAncestors(obj).stream()
179 .map(this::getKeyGenerator)
180 .collect(toList());
181 }
182
183
184
185
186
187
188 protected <C> I18nKeyGenerator getRootKeyGenerator(C obj) {
189 return getKeyGenerator(getRoot(obj));
190 }
191
192
193
194
195 protected <P> I18nKeyGenerator<P> getKeyGenerator(P obj) {
196 return I18nKeyGeneratorFactory.newKeyGeneratorFor(obj);
197 }
198
199
200
201
202
203
204 protected void addKey(List<String> keys, boolean stripLabelSuffix, String... parts) {
205 this.addKey(keys, keyify(parts));
206 if (stripLabelSuffix && LABEL.equals(parts[parts.length - 1])) {
207 this.addKey(keys, keyify(Arrays.copyOfRange(parts, 0, parts.length - 1)));
208 }
209 }
210
211 protected void addKey(List<String> keys, String... parts) {
212 this.addKey(keys, true, parts);
213 }
214
215 private void addKey(List<String> keys, String key) {
216 if (!keys.contains(key)) {
217 keys.add(key);
218 }
219 }
220
221
222
223
224 protected String keyify(String... parts) {
225 return StringUtils.join(parts, '.');
226 }
227
228
229
230
231 protected String keyify(String id) {
232 return StringUtils.replaceChars(id, ":/", "..");
233 }
234
235
236
237
238
239
240 protected String getIdOrNameForUnknownRoot(Object obj, boolean keyify) {
241
242 final Object root = getParentViaCast(obj) != null ? getRoot(obj) : obj;
243 final Set<Method> methods = ReflectionUtils.getMethods(root.getClass(),
244 method -> "getId".equals(method.getName()) || "getName".equals(method.getName()));
245
246 try {
247 String idOrName = null;
248 Iterator<Method> iterator = methods.iterator();
249
250 while (iterator.hasNext()) {
251 idOrName = (String) iterator.next().invoke(root);
252 if (StringUtils.isNotBlank(idOrName)) {
253 return keyify ? keyify(idOrName) : idOrName;
254 }
255 }
256 return null;
257
258 } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
259 throw new RuntimeException(e);
260 }
261 }
262
263 protected String getIdOrNameForUnknownRoot(Object obj) {
264 return getIdOrNameForUnknownRoot(obj, true);
265 }
266
267 protected String getModuleName(String id) {
268 return StringUtils.contains(id, ":") ? StringUtils.substringBefore(id, ":") : StringUtils.EMPTY;
269 }
270
271 protected String getIdWithoutModuleName(String id) {
272 return keyify(StringUtils.contains(id, ":") ? StringUtils.substringAfter(id, ":") : id);
273 }
274
275 protected String replaceColons(String e) {
276 return Optional.ofNullable(StringUtils.replaceChars(e, ':', '-')).orElse(StringUtils.EMPTY);
277 }
278
279
280
281
282 private String getterToField(Method method) {
283 final String methodName = method.getName();
284 if (methodName.startsWith("get")) {
285 return methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
286 } else {
287 throw new IllegalArgumentException(method + " is not a getter method");
288 }
289 }
290
291 }