Commit | Line | Data |
---|---|---|
9e834013 NR |
1 | package be.nikiroo.utils.resources; |
2 | ||
3 | import java.util.ArrayList; | |
0877d6f5 | 4 | import java.util.Iterator; |
9e834013 | 5 | import java.util.List; |
8517b60c NR |
6 | import java.util.Map; |
7 | import java.util.Set; | |
8 | import java.util.TreeMap; | |
9e834013 NR |
9 | |
10 | import be.nikiroo.utils.resources.Meta.Format; | |
11 | ||
12 | /** | |
13 | * A graphical item that reflect a configuration option from the given | |
14 | * {@link Bundle}. | |
15 | * | |
16 | * @author niki | |
17 | * | |
18 | * @param <E> | |
19 | * the type of {@link Bundle} to edit | |
20 | */ | |
0877d6f5 | 21 | public class MetaInfo<E extends Enum<E>> implements Iterable<MetaInfo<E>> { |
9e834013 NR |
22 | private final Bundle<E> bundle; |
23 | private final E id; | |
24 | ||
25 | private Meta meta; | |
0877d6f5 | 26 | private List<MetaInfo<E>> children = new ArrayList<MetaInfo<E>>(); |
9e834013 NR |
27 | |
28 | private String value; | |
8517b60c NR |
29 | private List<Runnable> reloadedListeners = new ArrayList<Runnable>(); |
30 | private List<Runnable> saveListeners = new ArrayList<Runnable>(); | |
9e834013 NR |
31 | |
32 | private String name; | |
33 | private String description; | |
34 | ||
35 | public MetaInfo(Class<E> type, Bundle<E> bundle, E id) { | |
36 | this.bundle = bundle; | |
37 | this.id = id; | |
38 | ||
39 | try { | |
40 | this.meta = type.getDeclaredField(id.name()).getAnnotation( | |
41 | Meta.class); | |
42 | } catch (NoSuchFieldException e) { | |
43 | } catch (SecurityException e) { | |
44 | } | |
45 | ||
46 | // We consider that if a description bundle is used, everything is in it | |
47 | ||
48 | String description = null; | |
49 | if (bundle.getDescriptionBundle() != null) { | |
50 | description = bundle.getDescriptionBundle().getString(id); | |
51 | if (description != null && description.trim().isEmpty()) { | |
52 | description = null; | |
53 | } | |
54 | } | |
55 | ||
56 | if (description == null) { | |
57 | description = meta.description(); | |
0877d6f5 NR |
58 | if (description == null) { |
59 | description = ""; | |
60 | } | |
9e834013 | 61 | if (meta.info() != null && !meta.info().isEmpty()) { |
0877d6f5 NR |
62 | if (!description.isEmpty()) { |
63 | description += "\n\n"; | |
64 | } | |
65 | description += meta.info(); | |
9e834013 NR |
66 | } |
67 | } | |
68 | ||
69 | String name = id.toString(); | |
70 | if (name.length() > 1) { | |
71 | name = name.substring(0, 1) + name.substring(1).toLowerCase(); | |
72 | name = name.replace("_", " "); | |
73 | } | |
74 | ||
75 | this.name = name; | |
76 | this.description = description; | |
77 | ||
78 | reload(); | |
79 | } | |
80 | ||
81 | /** | |
82 | * THe name of this item, deduced from its ID. | |
83 | * <p> | |
84 | * In other words, it is the ID but presented in a displayable form. | |
85 | * | |
86 | * @return the name | |
87 | */ | |
88 | public String getName() { | |
89 | return name; | |
90 | } | |
91 | ||
92 | /** | |
93 | * The description of this item (information to present to the user). | |
94 | * | |
95 | * @return the description | |
96 | */ | |
97 | public String getDescription() { | |
98 | return description; | |
99 | } | |
100 | ||
101 | public Format getFormat() { | |
102 | return meta.format(); | |
103 | } | |
104 | ||
0877d6f5 NR |
105 | // for ComboBox, this is mostly a suggestion |
106 | public String[] getAllowedValues() { | |
107 | return meta.list(); | |
108 | } | |
109 | ||
110 | // TODO: use it! | |
111 | public boolean isArray() { | |
112 | return meta.array(); | |
113 | } | |
114 | ||
9e834013 NR |
115 | /** |
116 | * The value stored by this item, as a {@link String}. | |
117 | * | |
118 | * @return the value | |
119 | */ | |
120 | public String getString() { | |
121 | return value; | |
122 | } | |
123 | ||
124 | public String getDefaultString() { | |
125 | return meta.def(); | |
126 | } | |
127 | ||
128 | public Boolean getBoolean() { | |
129 | return BundleHelper.parseBoolean(getString()); | |
130 | } | |
131 | ||
132 | public Boolean getDefaultBoolean() { | |
133 | return BundleHelper.parseBoolean(getDefaultString()); | |
134 | } | |
135 | ||
136 | public Character getCharacter() { | |
137 | return BundleHelper.parseCharacter(getString()); | |
138 | } | |
139 | ||
140 | public Character getDefaultCharacter() { | |
141 | return BundleHelper.parseCharacter(getDefaultString()); | |
142 | } | |
143 | ||
144 | public Integer getInteger() { | |
145 | return BundleHelper.parseInteger(getString()); | |
146 | } | |
147 | ||
148 | public Integer getDefaultInteger() { | |
149 | return BundleHelper.parseInteger(getDefaultString()); | |
150 | } | |
151 | ||
152 | public Integer getColor() { | |
153 | return BundleHelper.parseColor(getString()); | |
154 | } | |
155 | ||
156 | public Integer getDefaultColor() { | |
157 | return BundleHelper.parseColor(getDefaultString()); | |
158 | } | |
159 | ||
160 | public List<String> getList() { | |
161 | return BundleHelper.parseList(getString()); | |
162 | } | |
163 | ||
164 | public List<String> getDefaultList() { | |
165 | return BundleHelper.parseList(getDefaultString()); | |
166 | } | |
167 | ||
168 | /** | |
169 | * The value stored by this item, as a {@link String}. | |
170 | * | |
171 | * @param value | |
172 | * the new value | |
173 | */ | |
174 | public void setString(String value) { | |
175 | this.value = value; | |
176 | } | |
177 | ||
178 | public void setBoolean(boolean value) { | |
179 | setString(BundleHelper.fromBoolean(value)); | |
180 | } | |
181 | ||
182 | public void setCharacter(char value) { | |
183 | setString(BundleHelper.fromCharacter(value)); | |
184 | } | |
185 | ||
186 | public void setInteger(int value) { | |
187 | setString(BundleHelper.fromInteger(value)); | |
188 | } | |
189 | ||
190 | public void setColor(int value) { | |
191 | setString(BundleHelper.fromColor(value)); | |
192 | } | |
193 | ||
194 | public void setList(List<String> value) { | |
195 | setString(BundleHelper.fromList(value)); | |
196 | } | |
197 | ||
198 | /** | |
199 | * Reload the value from the {@link Bundle}. | |
200 | */ | |
201 | public void reload() { | |
202 | value = bundle.getString(id); | |
8517b60c | 203 | for (Runnable listener : reloadedListeners) { |
9e834013 NR |
204 | try { |
205 | listener.run(); | |
206 | } catch (Exception e) { | |
207 | // TODO: error management? | |
208 | e.printStackTrace(); | |
209 | } | |
210 | } | |
211 | } | |
212 | ||
8517b60c NR |
213 | // listeners will be called AFTER reload |
214 | public void addReloadedListener(Runnable listener) { | |
215 | reloadedListeners.add(listener); | |
9e834013 NR |
216 | } |
217 | ||
218 | /** | |
219 | * Save the current value to the {@link Bundle}. | |
220 | */ | |
221 | public void save() { | |
8517b60c NR |
222 | for (Runnable listener : saveListeners) { |
223 | try { | |
224 | listener.run(); | |
225 | } catch (Exception e) { | |
226 | // TODO: error management? | |
227 | e.printStackTrace(); | |
228 | } | |
229 | } | |
9e834013 NR |
230 | bundle.setString(id, value); |
231 | } | |
232 | ||
8517b60c NR |
233 | // listeners will be called BEFORE save |
234 | public void addSaveListener(Runnable listener) { | |
235 | saveListeners.add(listener); | |
236 | } | |
237 | ||
0877d6f5 NR |
238 | @Override |
239 | public Iterator<MetaInfo<E>> iterator() { | |
240 | return children.iterator(); | |
241 | } | |
242 | ||
243 | public List<MetaInfo<E>> getChildren() { | |
244 | return children; | |
245 | } | |
246 | ||
9e834013 NR |
247 | /** |
248 | * Create a list of {@link MetaInfo}, one for each of the item in the given | |
249 | * {@link Bundle}. | |
250 | * | |
251 | * @param <E> | |
252 | * the type of {@link Bundle} to edit | |
253 | * @param type | |
254 | * a class instance of the item type to work on | |
255 | * @param bundle | |
256 | * the {@link Bundle} to sort through | |
257 | * | |
258 | * @return the list | |
259 | */ | |
260 | static public <E extends Enum<E>> List<MetaInfo<E>> getItems(Class<E> type, | |
261 | Bundle<E> bundle) { | |
262 | List<MetaInfo<E>> list = new ArrayList<MetaInfo<E>>(); | |
263 | for (E id : type.getEnumConstants()) { | |
264 | list.add(new MetaInfo<E>(type, bundle, id)); | |
265 | } | |
266 | ||
267 | return list; | |
268 | } | |
269 | ||
8517b60c NR |
270 | // TODO: multiple levels? |
271 | static public <E extends Enum<E>> Map<MetaInfo<E>, List<MetaInfo<E>>> getGroupedItems( | |
272 | Class<E> type, Bundle<E> bundle) { | |
273 | Map<MetaInfo<E>, List<MetaInfo<E>>> map = new TreeMap<MetaInfo<E>, List<MetaInfo<E>>>(); | |
274 | Map<MetaInfo<E>, List<MetaInfo<E>>> map1 = new TreeMap<MetaInfo<E>, List<MetaInfo<E>>>(); | |
275 | ||
276 | List<MetaInfo<E>> ungrouped = new ArrayList<MetaInfo<E>>(); | |
277 | for (MetaInfo<E> info : getItems(type, bundle)) { | |
278 | if (info.meta.group()) { | |
279 | List<MetaInfo<E>> list = new ArrayList<MetaInfo<E>>(); | |
280 | map.put(info, list); | |
281 | map1.put(info, list); | |
282 | } else { | |
283 | ungrouped.add(info); | |
284 | } | |
285 | } | |
286 | ||
287 | for (int i = 0; i < ungrouped.size(); i++) { | |
288 | MetaInfo<E> info = ungrouped.get(i); | |
289 | MetaInfo<E> group = findParent(info, map.keySet()); | |
290 | if (group != null) { | |
291 | map.get(group).add(info); | |
292 | ungrouped.remove(i--); | |
293 | } | |
294 | } | |
295 | ||
296 | if (ungrouped.size() > 0) { | |
297 | map.put(null, ungrouped); | |
298 | } | |
299 | ||
300 | return map; | |
301 | } | |
302 | ||
303 | static private <E extends Enum<E>> MetaInfo<E> findParent(MetaInfo<E> info, | |
304 | Set<MetaInfo<E>> candidates) { | |
305 | MetaInfo<E> group = null; | |
306 | for (MetaInfo<E> pcandidate : candidates) { | |
307 | if (info.id.toString().startsWith(pcandidate.id.toString())) { | |
308 | if (group == null | |
309 | || group.id.toString().length() < pcandidate.id | |
310 | .toString().length()) { | |
311 | group = pcandidate; | |
312 | } | |
313 | } | |
314 | } | |
315 | ||
316 | return group; | |
317 | } | |
9e834013 | 318 | } |