1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sf.firemox.database;
20
21 import java.awt.Graphics;
22 import java.awt.Image;
23 import java.awt.image.ImageProducer;
24 import java.net.MalformedURLException;
25 import java.net.URL;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30
31 import net.sf.firemox.clickable.target.card.AbstractCard;
32 import net.sf.firemox.clickable.target.card.CardModel;
33 import net.sf.firemox.clickable.target.card.CardModelLazy;
34 import net.sf.firemox.clickable.target.card.MCard;
35 import net.sf.firemox.database.data.TranslatableData;
36 import net.sf.firemox.deckbuilder.MdbLoader;
37 import net.sf.firemox.management.MonitorListener;
38 import net.sf.firemox.management.MonitoredCheckContent;
39 import net.sf.firemox.tools.Log;
40 import net.sf.firemox.tools.MToolKit;
41 import net.sf.firemox.tools.Picture;
42 import net.sf.firemox.xml.XmlParser;
43 import net.sf.firemox.xml.XmlParser.Node;
44 import sun.awt.image.FileImageSource;
45 import sun.awt.image.URLImageSource;
46
47 /***
48 * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
49 * @since 0.90
50 */
51 public class DatabaseCard {
52
53 private Map<String, TranslatableData> data = new HashMap<String, TranslatableData>(
54 5);
55
56 /***
57 * The proxy used to build this object.
58 */
59 private final Proxy dataProxy;
60
61 /***
62 * The proxy used to display picture of this object. If is null,
63 * <code>dataProxy</code> will be used.
64 */
65 private Proxy[] pictureProxies;
66
67 /***
68 * The currently associated image of this card. Is <code>null</code> while
69 * not loaded.
70 */
71 private MonitoredCheckContent image;
72
73 /***
74 * The currently associated scaled image of this card. Is <code>null</code>
75 * while not loaded.
76 */
77 private Image scaledImage;
78
79 /***
80 * The card model of this card.
81 */
82 private CardModel cardModel;
83
84 /***
85 * The consistency of this database.
86 */
87 private boolean consistent;
88
89 /***
90 * Create new instance of DatabaseCard.
91 *
92 * @param cardModel
93 * the card model containing card name used as id
94 * @param dataProxy
95 * the proxy where this data came from. If is <code>null</code>,
96 * the picture would be get using the default art URL.
97 * @param pictureProxies
98 * the proxies used to get image. If is <code>null</code>,
99 * <code>dataProxy</code> would be used.
100 */
101 public DatabaseCard(CardModel cardModel, Proxy dataProxy,
102 Proxy... pictureProxies) {
103 this.cardModel = cardModel;
104 this.dataProxy = dataProxy;
105 setPictureProxies(pictureProxies);
106 }
107
108 /***
109 * Return the card model of this card.
110 *
111 * @return the card model of this card.
112 */
113 public CardModel getCardModel() {
114 return cardModel;
115 }
116
117 /***
118 * Return the proxy used to build this object.
119 *
120 * @return the proxy used to build this object.
121 */
122 public Proxy getDataProxy() {
123 return dataProxy;
124 }
125
126 /***
127 * Return the proxy used to display picture of this object. If is null,
128 * <code>dataProxy</code> will be used.
129 *
130 * @return the proxy used to display picture of this object. If is null,
131 * <code>dataProxy</code> will be used.
132 */
133 public Proxy getPictureProxy() {
134 return pictureProxies[0];
135 }
136
137 /***
138 * Return the proxies used to display picture of this object. If is null,
139 * <code>dataProxy</code> will be used.
140 *
141 * @return the proxies used to display picture of this object. If is null,
142 * <code>dataProxy</code> will be used.
143 */
144 public Proxy[] getPictureProxies() {
145 return pictureProxies;
146 }
147
148 /***
149 * The card name. Is considered as id. Return the English name, not the
150 * localized one.
151 *
152 * @return the card name. Is considered as id.
153 * @see net.sf.firemox.clickable.target.card.CardModel#getCardName()
154 */
155 public String getCardName() {
156 return cardModel.getCardName();
157 }
158
159 /***
160 * Return the localized card's name.
161 *
162 * @return the localized card's name.
163 * @see net.sf.firemox.clickable.target.card.CardModel#getLocalName()
164 */
165 public String getLocalName() {
166 return cardModel.getLocalName();
167 }
168
169 /***
170 * Return XML rule designer of the card.
171 *
172 * @return XML rule designer of the card.
173 * @see net.sf.firemox.clickable.target.card.CardModel#getRulesCredit()
174 */
175 public String getRulesCredit() {
176 return cardModel.getRulesCredit();
177 }
178
179 /***
180 * Add a translatable data to this database.
181 *
182 * @param data
183 * translatable data to add.
184 */
185 public void add(TranslatableData data) {
186 if ("local-name".equalsIgnoreCase(data.getValue())) {
187 cardModel.setLocalName(data.getValue());
188 }
189 this.data.put(data.getPropertyName(), data);
190 }
191
192 /***
193 * Return the card's picture as it would be displayed in the board.
194 *
195 * @param card
196 * the card requesting it's picture.
197 * @return the card's picture as it would be displayed in the board.
198 */
199 public Image getImage(AbstractCard card) {
200 if (!card.isVisibleForYou()) {
201 return DatabaseFactory.backImage;
202 }
203 return getImage((MonitorListener) card);
204 }
205
206 /***
207 * Return the scaled card's picture as it would be displayed in the board.
208 *
209 * @param card
210 * the card requesting it's picture.
211 * @return the scaled card's picture as it would be displayed in the board.
212 */
213 public Image getScaledImage(AbstractCard card) {
214 if (!card.isVisibleForYou()) {
215 return DatabaseFactory.scaledBackImage;
216 }
217 return getScaledImage((MonitorListener) card);
218 }
219
220 /***
221 * Return the card's picture as it would be displayed in the board.
222 *
223 * @param listener
224 * the listener to notify when the picture will be completely loaded.
225 * @return the card's picture as it would be displayed in the board.
226 */
227 public Image getImage(MonitorListener listener) {
228 if (image == null) {
229 loadDatabasePicture(null, listener);
230 } else if (!image.isFinished()) {
231 image.addListener(listener);
232 }
233 return image.getContent();
234 }
235
236 /***
237 * Return the scaled card's picture as it would be displayed in the board.
238 *
239 * @param listener
240 * the listener to notify when the picture will be completely loaded.
241 * @return the scaled card's picture as it would be displayed in the board.
242 */
243 public Image getScaledImage(MonitorListener listener) {
244 if (scaledImage == null) {
245 if (image == null) {
246 getImage(listener);
247 }
248 if (!image.isFinished()) {
249 return image.getContent();
250 }
251 scaledImage = Picture.getScaledImage(image.getContent());
252 }
253 return scaledImage;
254 }
255
256 /***
257 * Invalidate the card picture
258 */
259 public synchronized void invalidateImage() {
260 image = null;
261 scaledImage = null;
262 }
263
264 /***
265 * Return the stream description used to build the picture by this database
266 * object.
267 *
268 * @return the stream description used to build the picture by this database
269 * object.
270 */
271 public String getPictureStream() {
272 if (image != null && DatabaseFactory.sourceFile != null
273 && image.getContent() != null) {
274 final ImageProducer imageProducer = image.getContent().getSource();
275 if (imageProducer instanceof FileImageSource) {
276 try {
277 return DatabaseFactory.sourceFile.get(imageProducer).toString();
278 } catch (Exception e) {
279 return "unavailable stream";
280 }
281 } else if (imageProducer instanceof URLImageSource) {
282 try {
283 return DatabaseFactory.sourceUrl.get(imageProducer).toString();
284 } catch (Exception e) {
285 return "unavailable stream";
286 }
287 }
288 }
289
290 return "unavailable stream";
291 }
292
293 /***
294 * Return the default local picture of this card model. The proxies are not
295 * used to get this image.
296 *
297 * @return <code>null</code> if there is no local image.A non
298 * <code>null</code> object otherwise.
299 * @throws MalformedURLException
300 */
301 private MonitoredCheckContent getDefaultImage() throws MalformedURLException {
302 return Picture.loadImage(MToolKit.getTbsPicture(cardModel.getKeyName()
303 + ".jpg", false), null);
304
305 }
306
307 /***
308 * Load the image considering proxy, properties and the given picture name. If
309 * no proxy is set for this database, the picture is loaded from the
310 * 'tbs/${tbs.name}/images' directory. The picture filename is either the
311 * specified <param>pictureName</param> if not null, either the built from
312 * the card name. The looked for picture type is '.jpg'.<br>
313 * If a proxy is set, then the proxy will build the URL where the picture can
314 * been found first locally, then remotely from the proxy web-site.
315 *
316 * @param pictureName
317 * the alternative picture name.
318 * @param listener
319 * the card requesting this picture.
320 */
321 private synchronized void loadDatabasePicture(String pictureName,
322 MonitorListener listener) {
323 if (image != null) {
324
325 image.addListener(listener);
326 return;
327 }
328 try {
329 if (pictureProxies == null) {
330
331
332
333
334 if (pictureName != null) {
335
336 image = Picture.loadImage(MToolKit
337 .getTbsPicture(pictureName + ".jpg"), null);
338 } else {
339
340 image = Picture.loadImage(MToolKit.getTbsPicture(cardModel
341 .getKeyName()
342 + ".jpg", false), new URL(MdbLoader.getArtURL() + "/"
343 + cardModel.getKeyName() + ".jpg"));
344 }
345 } else {
346
347 /***
348 * The proxy has not been provided, we try to match one. <br>
349 * First with the local one, then with the remote ones.<br>
350 * The picture will be saved in the proxy private location
351 * 'tbs/XXX/images/proxies/${proxy.name}/${picture.URL less
352 * proxy.baseURL}/' place
353 */
354 image = getDefaultImage();
355 if (image != null) {
356 return;
357 }
358
359 List<String> localPaths = new ArrayList<String>();
360 for (Proxy pictureProxy : pictureProxies) {
361 for (String path : pictureProxy.getLocalPictures(cardModel, data)) {
362 if (path != null) {
363 image = Picture.loadImage(path, null);
364 if (image != null) {
365 return;
366 }
367 }
368 localPaths.add(path);
369 }
370 }
371
372 List<String> remotePaths = new ArrayList<String>();
373 for (Proxy pictureProxy : pictureProxies) {
374 remotePaths.addAll(pictureProxy.getRemotePictures(cardModel, data));
375 }
376
377
378 if (pictureName != null) {
379
380 image = Picture.loadImage(MToolKit
381 .getTbsPicture(pictureName + ".jpg"), null);
382 } else {
383
384 localPaths.add(MToolKit.getTbsPicture(
385 cardModel.getKeyName() + ".jpg", false));
386 remotePaths.add(MdbLoader.getArtURL() + "/" + cardModel.getKeyName()
387 + ".jpg");
388 }
389
390 if (image == null) {
391 image = new MonitoredCheckContent(localPaths, remotePaths, listener);
392 image.start();
393 }
394 }
395 } catch (MalformedURLException e) {
396 try {
397 image = Picture.loadImage(MToolKit.getTbsPicture(cardModel.getKeyName()
398 + ".jpg"), null);
399 } catch (MalformedURLException ex) {
400 Log.debug("Error during picture load of " + cardModel.getKeyName(), ex);
401 }
402 }
403
404 if (image == null) {
405
406 image = new MonitoredCheckContent(DatabaseFactory.backImage);
407 }
408
409 }
410
411 /***
412 * Create a node representing this data inside the given node.
413 *
414 * @param inNode
415 * the node where data would be added to.
416 */
417 void updateCache(Node inNode) {
418
419
420 XmlParser.Attribute[] attributes = new XmlParser.Attribute[2];
421 attributes[0] = new XmlParser.Attribute("local-name", getLocalName());
422 attributes[1] = new XmlParser.Attribute("proxy", dataProxy.getName());
423
424
425 Node node = new XmlParser.Node(inNode, cardModel.getKeyName(), null);
426 node.aAttrs = attributes;
427
428
429 for (TranslatableData dataIt : data.values()) {
430 XmlParser.Attribute[] values = new XmlParser.Attribute[2];
431 values[0] = new XmlParser.Attribute("name", dataIt.getPropertyName());
432 values[1] = new XmlParser.Attribute("value", dataIt.getValue());
433 Node property = new XmlParser.Node(node, "property", null);
434 property.aAttrs = values;
435 node.add(0, property);
436 }
437 inNode.aList.add(0, node);
438 }
439
440 /***
441 * Create a new instance of DatabaseCard with a picture different form the
442 * standard one (relative to card name). No proxy would be used for this new
443 * instance.<br>
444 * The given picture is immediately loaded.
445 *
446 * @param pictureName
447 * the picture to use with this DatabaseCard
448 * @return a clone of this instance, without proxy and with the specified
449 * picture loaded immediately.
450 */
451 public DatabaseCard clone(String pictureName) {
452 final DatabaseCard clone = new DatabaseCard(cardModel, null);
453 clone.loadDatabasePicture(pictureName, null);
454 return clone;
455 }
456
457 @Override
458 public DatabaseCard clone() {
459 return clone(cardModel);
460 }
461
462 /***
463 * Return a clone of this object from the given card model.
464 *
465 * @param cardModel
466 * @return a clone of this object from the given card model.
467 */
468 public DatabaseCard clone(CardModel cardModel) {
469 final DatabaseCard clone = new DatabaseCard(cardModel, dataProxy,
470 pictureProxies);
471 return clone;
472 }
473
474 /***
475 * Return the translated data associated to the named property.
476 *
477 * @param property
478 * the property name.
479 * @return the translated data associated to the named property.
480 */
481 public String getProperty(String property) {
482 final TranslatableData data = this.data.get(property);
483 if (data != null) {
484 return data.getTranslatedValue(dataProxy);
485 }
486 return "-";
487 }
488
489 /***
490 * Set the proxy used to display picture of this object. If is null,
491 * <code>dataProxy</code> will be used.
492 *
493 * @param pictureProxies
494 * the new proxies used for picture. Order corresponds to preference.
495 */
496 public void setPictureProxies(Proxy... pictureProxies) {
497 if (this.pictureProxies != pictureProxies) {
498 if (pictureProxies == null) {
499 this.pictureProxies = new Proxy[] { dataProxy };
500 } else {
501 this.pictureProxies = pictureProxies;
502 }
503 image = null;
504 }
505 }
506
507 @Override
508 public String toString() {
509 return cardModel.toString();
510 }
511
512 /***
513 * Set the consistency of this database.
514 *
515 * @param consistent
516 * the new consistency of this database.
517 */
518 public void setConsistent(boolean consistent) {
519 this.consistent = consistent;
520 }
521
522 /***
523 * Return the new score of this database.
524 *
525 * @return true if this database is consistent.
526 */
527 public boolean isConsistent() {
528 return consistent;
529 }
530
531 /***
532 * This method update and paint on the given card, the progress bar of task of
533 * the image attached to this model.
534 *
535 * @param card
536 * the listener to manage.
537 * @param g
538 * the graphics used to paint the progress bar.
539 */
540 public void updatePaintNotification(MCard card, Graphics g) {
541 if (image == null || !image.isFinished()) {
542
543 if (image != null && card.isVisibleForYou()) {
544 image.paintNotification(g);
545 }
546 } else if (image.isFinished()) {
547 image.acknowledgeFinished(card);
548 }
549 }
550
551 /***
552 * This method update and paint on the given listener, the progress bar of
553 * task of the image attached to this model.
554 *
555 * @param listener
556 * the listener to manage.
557 * @param g
558 * the graphics used to paint the progress bar.
559 */
560 public void updatePaintNotification(MonitorListener listener, Graphics g) {
561 if (image == null || !image.isFinished()) {
562
563 if (image != null) {
564 image.paintNotification(g);
565 }
566 } else if (image.isFinished()) {
567 image.acknowledgeFinished(listener);
568 }
569 }
570
571 /***
572 * Reset the buffered data.
573 */
574 public void updateMUI() {
575 scaledImage = null;
576 }
577
578 /***
579 * Update the card model if needed.
580 *
581 * @param cardModel
582 * the new card model.
583 */
584 public void updateCardModel(CardModel cardModel) {
585 if (cardModel != null && this.cardModel instanceof CardModelLazy) {
586 this.cardModel = cardModel;
587 }
588 }
589
590 }