View Javadoc

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