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