1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sf.magicproject.tools;
20
21 import java.awt.Font;
22 import java.awt.Image;
23 import java.awt.MediaTracker;
24 import java.awt.Point;
25 import java.awt.Toolkit;
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.OutputStream;
31 import java.net.HttpURLConnection;
32 import java.net.MalformedURLException;
33 import java.net.URL;
34 import java.net.URLConnection;
35 import java.nio.charset.Charset;
36 import java.util.Properties;
37 import java.util.Random;
38
39 import javax.sound.sampled.AudioFormat;
40 import javax.sound.sampled.AudioInputStream;
41 import javax.sound.sampled.AudioSystem;
42 import javax.sound.sampled.Clip;
43 import javax.sound.sampled.DataLine;
44 import javax.sound.sampled.LineUnavailableException;
45 import javax.sound.sampled.SourceDataLine;
46 import javax.sound.sampled.UnsupportedAudioFileException;
47 import javax.swing.ImageIcon;
48 import javax.swing.JComponent;
49 import javax.swing.JFileChooser;
50 import javax.swing.JFrame;
51 import javax.swing.JOptionPane;
52 import javax.swing.LookAndFeel;
53 import javax.swing.SwingUtilities;
54
55 import net.sf.magicproject.clickable.targetable.card.MCard;
56 import net.sf.magicproject.clickable.targetable.card.SystemCard;
57 import net.sf.magicproject.deckbuilder.MdbLoader;
58 import net.sf.magicproject.token.IdCardColors;
59 import net.sf.magicproject.token.IdCommonToken;
60 import net.sf.magicproject.token.IdConst;
61 import net.sf.magicproject.ui.MagicUIComponents;
62 import net.sf.magicproject.xml.XmlDeckTranslator;
63
64 import org.apache.commons.io.IOUtils;
65 import org.apache.commons.lang.ArrayUtils;
66 import org.apache.commons.lang.StringUtils;
67 import org.jvnet.substance.SubstanceLookAndFeel;
68 import org.jvnet.substance.painter.AlphaControlBackgroundComposite;
69 import org.jvnet.substance.scroll.CoreScrollThumbGripPainters;
70 import org.mortbay.util.Password;
71
72 import sun.misc.BASE64Encoder;
73
74 /***
75 * MToolKit.java Created on 18 d�c. 2003
76 *
77 * @author Fabrice Daugan, brius
78 * @since 0.53
79 * @since 0.54 added "save..." methods
80 */
81 public final class MToolKit {
82
83 /***
84 * Create a new instance of this class.
85 */
86 private MToolKit() {
87 super();
88 }
89
90 /***
91 * Replace occurrences into a string.
92 *
93 * @param data
94 * the string to replace occurrences into
95 * @param from
96 * the occurrence to replace.
97 * @param to
98 * the occurrence to be used as a replacement.
99 * @return the new string with replaced occurrences.
100 */
101 public static String replaceAll(String data, String from, String to) {
102 if (from.length() > 0 && from.length() > 0) {
103 final StringBuilder buf = new StringBuilder(data.length());
104 int pos = -1;
105 int i = 0;
106 while ((pos = data.indexOf(from, i)) != -1) {
107 buf.append(data.substring(i, pos)).append(to);
108 i = pos + from.length();
109 }
110 buf.append(data.substring(i));
111 return buf.toString();
112 }
113 return data;
114 }
115
116 /***
117 * @param idToken
118 * the value to translate.
119 * @return the real value of the specified value. If the value is a constant
120 * the returned value can be positive or negative.
121 */
122 public static int getConstant(int idToken) {
123 if (idToken != IdConst.ALL
124 && (idToken & 0xFF80) == IdConst.NEGATIVE_NUMBER_MASK) {
125
126 return -(idToken & 0x7F);
127 }
128 return idToken;
129 }
130
131 /***
132 * The default charset used by this application.
133 */
134 public static final String CHARSET = Charset.forName("ISO-8859-1").name();
135
136 /***
137 * read a string from inputStream ending with \0. The mximum size of this
138 * string is 200 chars.
139 *
140 * @param inputStream
141 * is the input stream
142 * @return the read string
143 * @throws IOException
144 * If some other I/O error occurs
145 */
146 public static String readString(InputStream inputStream) throws IOException {
147 int count = 0;
148 try {
149 while ((mBuffer[count] = (byte) inputStream.read()) != 0) {
150 count++;
151 }
152 } catch (Exception e) {
153 throw new IOException("Exception '" + e + "' reading string '"
154 + new String(mBuffer, 0, count, CHARSET) + "'\n\tdump : '"
155 + ArrayUtils.toString(mBuffer));
156 }
157
158
159 return new String(mBuffer, 0, count, CHARSET);
160 }
161
162 /***
163 * write a string to output stream ending with \0.
164 *
165 * @param out
166 * is the input stream
167 * @param string
168 * the string to write
169 */
170 public static void writeString(OutputStream out, String string) {
171 try {
172 if (string != null) {
173 out.write(string.getBytes(CHARSET));
174 }
175 out.write(0);
176 } catch (Exception e) {
177 throw new InternalError("writting string in file," + e);
178 }
179 }
180
181 /***
182 * read a string from inputFile The strings read end with \0 with no limit of
183 * size
184 *
185 * @param inputStream
186 * is the input stream containing content
187 * @return the read string from the specified input stream
188 */
189 public static String readText(InputStream inputStream) {
190 StringBuffer string = new StringBuffer(100);
191 try {
192 int intRead;
193 while ((intRead = inputStream.read()) != 0) {
194 string.append((char) intRead);
195 }
196 } catch (Exception e) {
197 throw new InternalError("reading text in file," + e);
198 }
199 return string.toString();
200 }
201
202 /***
203 * Read a byte array 256*256*256 bytes maximum sized from the given stream.
204 *
205 * @param inputStream
206 * is the input stream containing content.
207 * @return the byte array.
208 * @throws IOException
209 * If some other I/O error occurs
210 */
211 public static byte[] readByteArray(InputStream inputStream)
212 throws IOException {
213 final int size = MToolKit.readInt24(inputStream);
214 final byte[] array = new byte[size];
215 int readCounter = 0;
216 while (readCounter != size) {
217 readCounter += inputStream.read(array, readCounter, size - readCounter);
218 }
219 return array;
220 }
221
222 /***
223 * Read an icon from the given stream.
224 *
225 * @param inputStream
226 * is the input stream containing content.
227 * @return the read icon.
228 * @throws IOException
229 * If some other I/O error occurs
230 */
231 public static ImageIcon readImageIcon(InputStream inputStream)
232 throws IOException {
233 return new ImageIcon(readByteArray(inputStream));
234 }
235
236 /***
237 * Read an image from the given stream.
238 *
239 * @param inputStream
240 * is the input stream containing content.
241 * @return the read image.
242 * @throws IOException
243 * If some other I/O error occurs
244 */
245 public static Image readImage(InputStream inputStream) throws IOException {
246 return Toolkit.getDefaultToolkit().createImage(readByteArray(inputStream));
247 }
248
249 /***
250 * Return 0 or 1 at random way
251 *
252 * @return 0 or 1 at random way
253 */
254 public static int getRandom() {
255 return getRandom(2);
256 }
257
258 /***
259 * Return a number [0,length[ at random way
260 *
261 * @param length
262 * maximum+1 value to return.
263 * @return a number [0,length[ at random way
264 */
265 public static int getRandom(int length) {
266 lastRandom = random.nextInt(length);
267 return lastRandom;
268 }
269
270 /***
271 * Copy the src file throw the output stream.
272 *
273 * @param src
274 * the source file.
275 * @param out
276 * the output stream.
277 * @throws IOException
278 */
279 public static void writeFile(File src, OutputStream out) throws IOException {
280 InputStream in = new FileInputStream(src);
281 MToolKit.writeInt24(out, (int) src.length());
282
283
284 final byte[] buf = new byte[1024];
285 int len;
286 while ((len = in.read(buf)) >= 0) {
287 out.write(buf, 0, len);
288 }
289 IOUtils.closeQuietly(in);
290 }
291
292 /***
293 * file filter used for mdb files
294 */
295 private static FileFilterPlus mdbFilter = new FileFilterPlus("mdb",
296 "MDB File For Magic-Project");
297
298 /***
299 * file filter used for deck files
300 */
301 private static FileFilterPlus txtFilter = new FileFilterPlus("txt",
302 "TXT format: CardName;qty");
303
304 /***
305 * file filter used for JPG pictures
306 */
307 private static FileFilterPlus pictureFilter = new FileFilterPlus(
308 new String[] { "jpg", "gif", "png" }, "Images");
309
310 /***
311 * Return the deck file choosen with an "open" dialog.
312 *
313 * @return the file choosen, or the previous one if cancelled.
314 */
315 public static String getDeckFile() {
316 return getDeckFile(JFileChooser.OPEN_DIALOG);
317 }
318
319 /***
320 * Return the deck file choosen
321 *
322 * @param type
323 * open or save option
324 * @return the file choosen, or the previous one if cancelled.
325 */
326 public static String getDeckFile(int type) {
327 return getDeckFile(MagicUIComponents.magicForm, type);
328 }
329
330 /***
331 * Return the deck file choosen
332 *
333 * @param parent
334 * is parent of this dialog
335 * @param type
336 * open or save option
337 * @return the file choosen, or the previous one if cancelled.
338 */
339 public static String getDeckFile(JFrame parent, int type) {
340 String deckFile = Configuration.getString("decks.deck(0)", "");
341 try {
342 File file = MToolKit.getFile(deckFile);
343 if (file == null) {
344 file = showDialogFile("choose your deck file", null, null, 'o', file,
345 txtFilter, parent, type);
346 } else {
347 file = showDialogFile("choose your deck file", null, null, 'o', file
348 .getAbsoluteFile(), txtFilter, parent, type);
349 }
350
351 if (file != null) {
352 deckFile = getShortDeckFile(file.getCanonicalPath());
353 } else
354 return null;
355 return deckFile;
356
357 } catch (IOException e) {
358 JOptionPane.showMessageDialog(parent,
359 "Error occurred reading the specified file", "File problem",
360 JOptionPane.ERROR_MESSAGE);
361 return Configuration.getString("decks.deck(0)", "");
362 } catch (Exception e) {
363
364 return null;
365 }
366 }
367
368 /***
369 * Open a DialogFile dialog and return the choosen mdb file.
370 *
371 * @param oldFile
372 * the old mdb file, will be returned if user cancel operation
373 * @param parent
374 * is parent of this dialog
375 * @return the file choosen, or oldFile if cancelled
376 */
377 public static String getMdbFile(String oldFile, JFrame parent) {
378 try {
379 return showDialogFile("choose your mdb file", null, null, 'o',
380 oldFile == null ? null : MToolKit.getFile(oldFile), mdbFilter,
381 parent, JFileChooser.OPEN_DIALOG).getCanonicalPath();
382 } catch (java.io.IOException e) {
383 javax.swing.JOptionPane.showMessageDialog(parent,
384 "Error occurred reading the specified file", "File problem",
385 JOptionPane.ERROR_MESSAGE);
386 return oldFile;
387 } catch (Exception e) {
388
389 return null;
390 }
391 }
392
393 /***
394 * return the picture file choosen
395 *
396 * @param oldFile
397 * the old picture, will be returned if user cancel operation
398 * @param parent
399 * is parent of this dialog
400 * @return the picture file choosen, or oldFile if cancelled
401 */
402 public static String getPictureFile(String oldFile, JFrame parent) {
403 try {
404 return showDialogFile("choose your a picture", null, null, 'o', null,
405 pictureFilter, parent, JFileChooser.OPEN_DIALOG).getCanonicalPath();
406 } catch (java.io.IOException e) {
407 throw new InternalError("choosing picture file");
408 } catch (Exception e) {
409
410 return null;
411 }
412 }
413
414 /***
415 * Disp a file dialog with many options
416 *
417 * @param titreBoite
418 * title of this dialog box
419 * @param etiquetteBouton
420 * button "yes"
421 * @param infoBulle
422 * tooltip
423 * @param raccourciBouton
424 * shortcut for "yes" button
425 * @param fichier
426 * old file
427 * @param fileFilter
428 * file filter to apply
429 * @param parent
430 * is parent of this dialog
431 * @param type
432 * the dialog type
433 * @return the File object correponding to the user's choice
434 * @see JFileChooser#setDialogType(int)
435 */
436 private static File showDialogFile(String titreBoite, String etiquetteBouton,
437 String infoBulle, char raccourciBouton, File fichier,
438 FileFilterPlus fileFilter, JFrame parent, int type) {
439 return showDialogFile(titreBoite, etiquetteBouton, infoBulle,
440 raccourciBouton, fichier, fileFilter, parent, type,
441 JFileChooser.FILES_AND_DIRECTORIES);
442 }
443
444 /***
445 * Disp a file dialog with many options
446 *
447 * @param titreBoite
448 * title of this dialog box
449 * @param etiquetteBouton
450 * button "yes"
451 * @param infoBulle
452 * tooltip
453 * @param raccourciBouton
454 * shortcut for "yes" button
455 * @param fichier
456 * old file
457 * @param fileFilter
458 * file filter to apply
459 * @param parent
460 * is parent of this dialog
461 * @param type
462 * the dialog type
463 * @param mode
464 * @return the File object correponding to the user's choice
465 * @see JFileChooser#setDialogType(int)
466 */
467 public static File showDialogFile(String titreBoite, String etiquetteBouton,
468 String infoBulle, char raccourciBouton, File fichier,
469 FileFilterPlus fileFilter, JFrame parent, int type, int mode) {
470 if (fileChooser == null) {
471 fileChooser = new JFileChooser();
472 }
473 fileChooser.setDialogType(type);
474 fileChooser.setDialogTitle(titreBoite);
475 fileChooser.setApproveButtonText(etiquetteBouton);
476 fileChooser.setApproveButtonToolTipText(infoBulle);
477 fileChooser.setApproveButtonMnemonic(raccourciBouton);
478 if (fileFilter != null) {
479 fileFilter.addExtension(".");
480 fileChooser.setFileFilter(fileFilter);
481 }
482 fileChooser.setFileSelectionMode(mode);
483 fileChooser.rescanCurrentDirectory();
484 fileChooser.setSelectedFile(fichier);
485
486
487
488
489
490 int resultat = fileChooser.showDialog(parent, null);
491
492 return resultat == JFileChooser.APPROVE_OPTION ? fileChooser
493 .getSelectedFile() : null;
494 }
495
496 /***
497 * An utility function that layers on top of the LookAndFeel's
498 * isSupportedLookAndFeel() method. Returns true if the LookAndFeel is
499 * supported. Returns false if the LookAndFeel is not supported and/or if
500 * there is any kind of error checking if the LookAndFeel is supported. The
501 * L&F menu will use this method to detemine whether the various L&F options
502 * should be active or inactive.
503 *
504 * @param laf
505 * L1F name
506 * @return true if successfully loaded
507 */
508 public static boolean isAvailableLookAndFeel(String laf) {
509 try {
510 Class.forName(laf);
511
512
513
514 return true;
515 } catch (Exception e) {
516
517
518
519
520
521 return false;
522
523 }
524 }
525
526 /***
527 * Return the L&F instance from the givent name.
528 *
529 * @param laf
530 * the L&F class name
531 * @return the L&F instance
532 * @throws InstantiationException
533 * @throws IllegalAccessException
534 * @throws ClassNotFoundException
535 */
536 public static LookAndFeel geLookAndFeel(String laf)
537 throws InstantiationException, IllegalAccessException,
538 ClassNotFoundException {
539 final Class<?> lnfClass = Class.forName(laf);
540 return (LookAndFeel) (lnfClass.newInstance());
541 }
542
543 /***
544 * Read the next 2 bytes from the specified input stream and return the
545 * integer value coded with 32bits
546 *
547 * @param inputFile
548 * is the file containing the short (2bytes)to read
549 * @return the read integer built with the 2 next bytes read from the
550 * specified input stream
551 * @throws IOException
552 * if error occurred during the reading process from the specified
553 * input stream
554 */
555 public static int readInt16(InputStream inputFile) throws IOException {
556 int res = inputFile.read() * 256;
557 return res + inputFile.read();
558 }
559
560 /***
561 * Read the next 3 bytes from the specified input stream and return the
562 * integer value coded with 48bits
563 *
564 * @param inputFile
565 * is the file containing the short (3bytes)to read
566 * @return the read integer built with the 3 next bytes read from the
567 * specified input stream
568 * @throws IOException
569 * if error occurred during the reading process from the specified
570 * input stream
571 */
572 public static int readInt24(InputStream inputFile) throws IOException {
573 int res = inputFile.read() * 256 * 256;
574 res += inputFile.read() * 256;
575 return res + inputFile.read();
576 }
577
578 /***
579 * Write the specified positive short coded on 2 bytes to the specified input
580 * stream
581 *
582 * @param out
583 * is the output stream where the specified integer would be written
584 * @param int16
585 * the 2 bytes integer to write
586 */
587 public static void writeInt16(OutputStream out, int int16) {
588 try {
589 out.write(int16 / 256);
590 out.write(int16 % 256);
591 } catch (Exception e) {
592 throw new InternalError("writting int16 in file," + e);
593 }
594 }
595
596 /***
597 * Write the specified positive int coded on 3 bytes to the specified input
598 * stream
599 *
600 * @param out
601 * is the output stream where the specified integer would be written
602 * @param int24
603 * the 3 bytes integer to write
604 */
605 public static void writeInt24(OutputStream out, int int24) {
606 try {
607 out.write(int24 / 65536);
608 out.write(int24 % 65536 / 256);
609 out.write(int24 % 256);
610 } catch (Exception e) {
611 throw new InternalError("writting int24 in file," + e);
612 }
613 }
614
615 /***
616 * Create, and return the connection etablished with a http server. May use a
617 * http proxy if the settings have been set
618 *
619 * @return the SMTP properties.
620 */
621 public static Properties getSmtpProperties() {
622 if (Configuration.getBoolean("useProxy", false)) {
623
624 System.setProperty("socksProxySet", "true");
625 System.setProperty("socksProxyHost", Configuration.getString("proxyHost",
626 "192.168.0.252"));
627 System.setProperty("socksProxyPort", "25");
628 final String clearLoginPwd = Password.deobfuscate(Configuration
629 .getString("proxyObfuscatedLoginPwd", "anonymous:"));
630 System.setProperty("socksProxyUserName", clearLoginPwd.substring(0,
631 clearLoginPwd.indexOf(':')));
632 System.setProperty("socksProxyPassword", clearLoginPwd
633 .substring(clearLoginPwd.indexOf(':') + 1));
634
635 System.setProperty("socks.useProxy", "true");
636 System.setProperty("socks.proxyHost", System
637 .getProperty("socksProxyHost"));
638 System.setProperty("socks.proxyPort", System
639 .getProperty("socksProxyPort"));
640 System.setProperty("socks.proxyUserName", System
641 .getProperty("socksProxyUserName"));
642 System.setProperty("socks.proxyPassword", System
643 .getProperty("socksProxyPassword"));
644 }
645 return System.getProperties();
646 }
647
648 /***
649 * Create, and return the connection etablished with a http server. May use a
650 * http proxy if the settings have been set
651 *
652 * @param url
653 * the requested url.
654 * @return Http connection.
655 */
656 public static URLConnection getHttpConnection(URL url) {
657 try {
658 if (Configuration.getBoolean("useProxy", false)) {
659
660 Properties systPrp = System.getProperties();
661 systPrp.put("proxySet", "true");
662 systPrp.put("http.proxyHost", Configuration.getString("proxyHost",
663 "192.168.0.252"));
664 systPrp.put("http.proxyPort", Configuration.getInt("proxyPort", 1299));
665 System.setProperties(systPrp);
666
667 HttpURLConnection uc = (HttpURLConnection) url.openConnection();
668 BASE64Encoder encoder = new sun.misc.BASE64Encoder();
669 String encodedUserPwd = encoder.encode(Password.deobfuscate(
670 Configuration.getString("proxyObfuscatedLoginPwd", "")).getBytes());
671 uc.setRequestProperty("Proxy-Authorization", "Basic " + encodedUserPwd);
672 uc.connect();
673 return uc;
674 }
675 return url.openConnection();
676 } catch (IOException e) {
677 return null;
678 }
679 }
680
681 /***
682 * Return the loaded picture from a local place.
683 *
684 * @param localFile
685 * the local file name.
686 * @return the local picture.
687 * @throws InterruptedException
688 */
689 public static Image getLocalPicture(String localFile)
690 throws InterruptedException {
691 final Image result = Toolkit.getDefaultToolkit().getImage(
692 getResource(localFile, true));
693 if (result == null) {
694 throw new InterruptedException("Picture " + localFile
695 + " has not been found");
696 }
697 final MediaTracker tracker = new MediaTracker(MagicUIComponents.magicForm);
698 tracker.addImage(result, 0);
699 tracker.waitForAll();
700 if (tracker.isErrorAny()) {
701 tracker.removeImage(result, 0);
702 tracker.waitForAll();
703 result.flush();
704 throw new InterruptedException("Malformed picture " + localFile);
705 }
706 return result;
707 }
708
709 /***
710 * Return the picture specific to the current TBS
711 *
712 * @param pictureFile
713 * the path of picture to load. This file must not begin with '/'
714 * @return the picture specific to the current TBS
715 */
716 public static String getTbsHtmlPicture(String pictureFile) {
717 return new File(new StringBuilder(IdConst.TBS_DIR).append("/").append(
718 tbsName).append("/").append(IdConst.IMAGES_DIR).append(pictureFile)
719 .toString()).getAbsolutePath().replace('//', '/');
720 }
721
722 /***
723 * Return the picture specific to the current TBS
724 *
725 * @param pictureFile
726 * the path of picture to load. This file must not begin with '/'
727 * @return the picture specific to the current TBS
728 */
729 public static String getTbsPicture(String pictureFile) {
730 return getTbsPicture(pictureFile, true);
731 }
732
733 /***
734 * Return the picture specific to the current TBS
735 *
736 * @param pictureFile
737 * the path of picture to load. This file must not begin with '/'
738 * @param mustExist
739 * <code>true</code> if the ressource to search must exists,
740 * <code>false</code> either
741 * @return the picture specific to the current TBS
742 */
743 public static String getTbsPicture(String pictureFile, boolean mustExist) {
744 return getTbsFile(IdConst.IMAGES_DIR + pictureFile, mustExist);
745 }
746
747 /***
748 * Return the mana picture specific to the current TBS
749 *
750 * @param idColor
751 * the color of mana
752 * @return the mana picture specific to the current TBS
753 */
754 public static ImageIcon getTbsBigManaPicture(int idColor) {
755 if (idColor == 0)
756 return new ImageIcon(getTbsPicture("mana/colorless/big/"
757 + MdbLoader.colorlessBigURL));
758 return new ImageIcon(getTbsPicture("mana/colored/big/"
759 + MdbLoader.coloredBigManas[idColor]));
760 }
761
762 /***
763 * Return the mana picture specific to the current TBS
764 *
765 * @param idColor
766 * the color of mana
767 * @param amount
768 * requested amount.
769 * @return the mana picture specific to the current TBS
770 */
771 public static String getHtmlMana(int idColor, int amount) {
772 if (idColor == IdCommonToken.COLORLESS_MANA) {
773 if (amount >= MdbLoader.colorlessSmlManas.length) {
774 return MdbLoader.colorlessSmlManasHtml[MdbLoader.colorlessSmlManas.length - 1]
775 + getHtmlMana(idColor, amount - MdbLoader.colorlessSmlManas.length
776 + 1);
777 } else if (amount == -1) {
778 return MdbLoader.unknownSmlManaHtml;
779 }
780 return MdbLoader.colorlessSmlManasHtml[amount];
781
782 }
783 if (amount == -1) {
784 return MdbLoader.unknownSmlManaHtml;
785 }
786
787 String res = "";
788 for (int i = amount; i-- > 0;) {
789 res += MdbLoader.coloredSmlManasHtml[idColor];
790 }
791 return res;
792 }
793
794 /***
795 * Return the file specific to the current TBS
796 *
797 * @param file
798 * the path of file to load. This file must not begin with '/'
799 * @return the picture specific to the current TBS
800 */
801 public static String getTbsFile(String file) {
802 return getTbsFile(file, true);
803 }
804
805 /***
806 * Return the file specific to the current TBS
807 *
808 * @param file
809 * the path of file to load. This file must not begin with '/'
810 * @param mustExist
811 * <code>true</code> if the ressource to search must exists,
812 * <code>false</code> either
813 * @return the file specific to the current TBS
814 */
815 public static String getTbsFile(String file, boolean mustExist) {
816 File simpleFile = new File(new StringBuilder(IdConst.TBS_DIR).append("/")
817 .append(tbsName).append("/").append(file).toString());
818 if (simpleFile.exists()) {
819 return simpleFile.getPath();
820 }
821 URL url = getResource(new StringBuilder(IdConst.TBS_DIR).append("/")
822 .append(tbsName + "/").append(file).toString(), mustExist);
823 if (url == null)
824 return null;
825 String filePath = url.getFile();
826 File f = new File(filePath);
827 if (f.exists()) {
828 return filePath;
829 } else if (filePath.startsWith("/")) {
830 filePath = filePath.substring(1);
831 f = new File(filePath);
832 if (f.exists())
833 return filePath;
834 }
835 if (mustExist)
836 return null;
837
838 return filePath;
839 }
840
841 /***
842 * Return the sound specific to the current TBS
843 *
844 * @param soundFile
845 * the path of picture to load. This file must not begin with '/'
846 * @return the sound specific to the current TBS
847 */
848 public static String getSoundFile(String soundFile) {
849 return new StringBuilder(IdConst.TBS_DIR).append("/").append(tbsName + "/")
850 .append(IdConst.SOUNDS_DIR).append(soundFile).toString();
851 }
852
853 /***
854 * loadClip loads the sound-file into a clip.
855 *
856 * @param soundFile
857 * file to be loaded and played.
858 */
859 public static void loadClip(String soundFile) {
860 AudioFormat audioFormat = null;
861 AudioInputStream actionIS = null;
862 try {
863
864 actionIS = AudioSystem.getAudioInputStream(MToolKit.getFile(MToolKit
865 .getSoundFile(soundFile)));
866 AudioFormat.Encoding targetEncoding = AudioFormat.Encoding.PCM_SIGNED;
867 actionIS = AudioSystem.getAudioInputStream(targetEncoding, actionIS);
868 audioFormat = actionIS.getFormat();
869
870 } catch (UnsupportedAudioFileException afex) {
871 Log.error(afex);
872 } catch (IOException ioe) {
873
874 if (ioe.getMessage().equalsIgnoreCase("mark/reset not supported")) {
875 Log.error("IOException ignored.");
876 }
877 Log.error(ioe.getStackTrace());
878 }
879
880
881
882
883
884 DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
885 if (!AudioSystem.isLineSupported(info)) {
886 Log.error("LineCtrl matching " + info + " not supported.");
887 return;
888 }
889
890
891 try {
892 Clip clip = null;
893 try {
894 Clip.Info info2 = new Clip.Info(Clip.class, audioFormat);
895 clip = (Clip) AudioSystem.getLine(info2);
896 clip.open(actionIS);
897 clip.start();
898 } catch (IOException ioe) {
899 Log.error(ioe);
900 }
901 } catch (LineUnavailableException ex) {
902 Log.error("Unable to open the line: " + ex);
903 return;
904 }
905 }
906
907 /***
908 * Represents the default font
909 */
910 public static Font defaultFont;
911
912 /***
913 * Represents the MDB file corresponding to the current TBS name. Is null if
914 * no TBS is currently defined.
915 */
916 public static String mdbFile;
917
918 /***
919 * Represents the default mdb name. This is not the full name of selected TBS.
920 */
921 public static String tbsName;
922
923 /***
924 * represents the file chooser
925 */
926 public static JFileChooser fileChooser;
927
928 private static byte[] mBuffer = new byte[200];
929
930 static int lastRandom = 0;
931
932 /***
933 * The current random sequence. May be initialized at the beginning of each
934 * play.
935 */
936 public static Random random = new Random();
937
938 /***
939 * Return absolute location of given component.
940 *
941 * @param component
942 * the component to locate
943 * @return the absolute POINT location of given component.
944 */
945 public static Point getAbsoluteLocation(JComponent component) {
946 return SwingUtilities.convertPoint(component, 0, 0,
947 MagicUIComponents.magicForm.getContentPane());
948 }
949
950 /***
951 * Parses the string argument as a signed integer in the radix specified by
952 * the second argument. The characters in the string must all be digits of 10
953 * radix (as determined by whether
954 * {@link java.lang.Character#digit(char, int)} returns a nonnegative value),
955 * except that the first character may be an ASCII minus sign <code>'-'</code> (<code>'\u002D'</code>)
956 * to indicate a negative value. The resulting integer value is returned.
957 * <p>
958 * The <code>Integer#MIN_VALUE</code> value is returned if any of the
959 * following situations occurs:
960 * <ul>
961 * <li>The first argument is <code>null</code> or is a string of length
962 * zero.
963 * <li>Any character of the string is not a digit of the specified radix,
964 * except that the first character may be a minus sign <code>'-'</code> (<code>'\u002D'</code>)
965 * provided that the string is longer than length 1.
966 * <li>The value represented by the string is not a value of type
967 * <code>int</code>.
968 * </ul>
969 * <p>
970 * Examples: <blockquote> parseInt("0") returns 0 <br>
971 * parseInt("473") returns 473 <br>
972 * parseInt("-0") returns 0 <br>
973 * parseInt("2147483647") returns 2147483647 <br>
974 * parseInt("-2147483648") returns -2147483648 <br>
975 * parseInt("2147483648") returns Integer#MIN_VALUE <br>
976 * parseInt("Kona") returns Integer#MIN_VALUE <br>
977 * </blockquote>
978 *
979 * @param s
980 * the <code>String</code> containing the integer representation to
981 * be parsed
982 * @return the integer represented by the string argument in the 10 radix.
983 * Return Integer.MIN_VALUE when error
984 * @see Integer#MIN_VALUE
985 */
986 public static int parseInt(String s) {
987 if (s == null) {
988 return Integer.MIN_VALUE;
989 }
990 int result = 0;
991 boolean negative = false;
992 int i = 0;
993 int max = s.length();
994 int limit;
995 int multmin;
996 int digit;
997
998 if (max > 0) {
999 if (s.charAt(0) == '-') {
1000 negative = true;
1001 limit = Integer.MIN_VALUE;
1002 i++;
1003 } else {
1004 limit = -Integer.MAX_VALUE;
1005 }
1006 multmin = limit / 10;
1007 if (i < max) {
1008 digit = Character.digit(s.charAt(i++), 10);
1009 if (digit < 0) {
1010 return Integer.MIN_VALUE;
1011 }
1012 result = -digit;
1013 }
1014 while (i < max) {
1015
1016 digit = Character.digit(s.charAt(i++), 10);
1017 if (digit < 0) {
1018 return Integer.MIN_VALUE;
1019 }
1020 if (result < multmin) {
1021 return Integer.MIN_VALUE;
1022 }
1023 result *= 10;
1024 if (result < limit + digit) {
1025 return Integer.MIN_VALUE;
1026 }
1027 result -= digit;
1028 }
1029 } else {
1030 return Integer.MIN_VALUE;
1031 }
1032 if (negative) {
1033 if (i > 1) {
1034 return result;
1035 }
1036
1037 return Integer.MIN_VALUE;
1038 }
1039 return -result;
1040 }
1041
1042 /***
1043 * Return the canonical path of working directory.
1044 *
1045 * @return the canonical path of working directory.
1046 * @throws IOException
1047 */
1048 public static String getRelativePath() throws IOException {
1049 return new File("").getAbsoluteFile().getCanonicalPath().replace('//', '/');
1050 }
1051
1052 /***
1053 * Return the specified card name without any special char. The returned name
1054 * may be used to build a file. The returned string is in lower case.
1055 *
1056 * @param cardName
1057 * the card name as it is displayed.
1058 * @return the specified card name without any special char.
1059 */
1060 public static String getKeyName(String cardName) {
1061 return getExactKeyName(cardName).toLowerCase();
1062 }
1063
1064 /***
1065 * Return the specified card name without any special char. The returned name
1066 * may be used to build a file.
1067 *
1068 * @param cardName
1069 * the card name as it is displayed.
1070 * @return the specified card name without any special char.
1071 */
1072 public static String getExactKeyName(String cardName) {
1073 if (translator == null) {
1074 try {
1075 translator = new XmlDeckTranslator(MToolKit.tbsName);
1076 } catch (Exception e1) {
1077 e1.printStackTrace();
1078 Log.warn("NO DECK TRANSLATOR FOUND");
1079 }
1080 }
1081 return translator.convert(cardName);
1082 }
1083
1084 /***
1085 * Returns the given path with '\' char replaced by '/' char.
1086 *
1087 * @param canonicalPath
1088 * @return the given path with '\' char replaced by '/' char.
1089 * @throws IOException
1090 */
1091 public static String getRelativePath(String canonicalPath) throws IOException {
1092 if (canonicalPath.replace('//', '/').startsWith(getRelativePath())) {
1093 return canonicalPath.substring(getRelativePath().length() + 1).replace(
1094 '//', '/');
1095 }
1096 return canonicalPath;
1097 }
1098
1099 /***
1100 * Return logging info of the given card.
1101 *
1102 * @param card
1103 * the card to Log.
1104 * @return logging info of the given card.
1105 */
1106 public static String getLogCardInfo(MCard card) {
1107 if (card != null) {
1108 if (card != SystemCard.instance) {
1109 return new StringBuilder(", card=").append(card.getName()).append(
1110 "@" + Integer.toHexString(card.hashCode())).toString();
1111 }
1112 return ", card=" + card.getName();
1113 }
1114 return "";
1115 }
1116
1117 /***
1118 * The translator tranforming a card name into a file-serializable value. Is
1119 * <code>null</code> while not used.
1120 */
1121 public static XmlDeckTranslator translator = null;
1122
1123 /***
1124 * Return the specfied string without any local white spaces. Would be
1125 * replaced when
1126 * {@link org.apache.commons.lang.StringUtils#deleteWhitespace(String)} would
1127 * work.
1128 *
1129 * @param string
1130 * the string to normalize.
1131 * @return the given string without any space char.
1132 * @see Character#isWhitespace(char)
1133 */
1134 public static String replaceWhiteSpaces(String string) {
1135 final StringBuilder workingString = new StringBuilder(StringUtils
1136 .deleteWhitespace(string));
1137 for (int count = workingString.length(); count-- > 0;) {
1138 if (Character.isSpaceChar(workingString.charAt(count))) {
1139
1140 workingString.deleteCharAt(count);
1141 }
1142 }
1143 return workingString.toString();
1144 }
1145
1146 /***
1147 * Return the stream associated to the given resource.
1148 *
1149 * @param resource
1150 * the resource path to search.
1151 * @return the stream associated to the given resource.
1152 */
1153 public static InputStream getResourceAsStream(String resource) {
1154 if (new File(resource).exists())
1155 try {
1156 return new FileInputStream(resource);
1157 } catch (Exception e) {
1158
1159 }
1160 return Thread.currentThread().getContextClassLoader().getResourceAsStream(
1161 resource);
1162 }
1163
1164 /***
1165 * Return the URL associated to the given resource.
1166 *
1167 * @param resource
1168 * the resource path to search.
1169 * @param mustExist
1170 * <code>true</code> if the ressource to search must exists,
1171 * <code>false</code> either
1172 * @return the URL associated to the given resource.
1173 */
1174 public static URL getResource(String resource, boolean mustExist) {
1175 if (new File(resource).exists()) {
1176 try {
1177 return new File(resource).toURI().toURL();
1178 } catch (Exception e) {
1179
1180 }
1181 }
1182 URL url = null;
1183 try {
1184 url = new URL(resource);
1185 } catch (MalformedURLException e0) {
1186
1187 }
1188 if (url != null)
1189 return url;
1190 if (!mustExist) {
1191 try {
1192 return new URL("file:///" + resource);
1193 } catch (MalformedURLException e) {
1194 e.printStackTrace();
1195 }
1196 }
1197 return null;
1198 }
1199
1200 /***
1201 * Return the File associated to the given fileName.
1202 *
1203 * @param fileName
1204 * the file name path to search.
1205 * @return the File associated to the given fileName.
1206 */
1207 public static File getFile(String fileName) {
1208 File simpleFile = new File(fileName);
1209 if (simpleFile.exists())
1210 return simpleFile;
1211 URL url = getResource(fileName, true);
1212 if (url == null) {
1213 return null;
1214 }
1215 String file = url.getFile();
1216 File f = new File(file);
1217 if (f.exists()) {
1218 return f;
1219 } else if (file.startsWith("/")) {
1220 file = file.substring(1);
1221 f = new File(file);
1222 if (f.exists())
1223 return f;
1224 }
1225 return null;
1226 }
1227
1228 /***
1229 * Returns the absolute path of an icon used for the UI.
1230 *
1231 * @param fileName
1232 * the icon path relative to the images dir
1233 * @return the absolute path of an icon used for the UI
1234 */
1235 public static String getIconPath(String fileName) {
1236 File file = getFile(IdConst.IMAGES_DIR + fileName);
1237 if (file == null)
1238 return "";
1239 return file.getAbsolutePath();
1240 }
1241
1242 /***
1243 * Return the given deck file without the current path off application. If the
1244 * given deck is valid, it becomes the new deck reference.
1245 *
1246 * @param deckFile
1247 * the full deck file.
1248 * @return the given deck file without the current path off application.
1249 */
1250 public static String getShortDeckFile(String deckFile) {
1251 String fullDeckFile = deckFile.replace('//', '/');
1252 String shortName = fullDeckFile;
1253 try {
1254 if (fullDeckFile.toUpperCase().startsWith(
1255 MToolKit.getRelativePath().toUpperCase())) {
1256 shortName = fullDeckFile.substring(
1257 MToolKit.getRelativePath().length() + 1).replace('//', '/');
1258 }
1259 } catch (IOException e) {
1260
1261 }
1262 Configuration.addRecentProperty("decks.deck", shortName);
1263 return shortName;
1264 }
1265
1266 /***
1267 * Specifies that a component should have overlay functionality.
1268 *
1269 * @param superPanel
1270 * the panel to overlay.
1271 */
1272 public static void addOverlay(JComponent superPanel) {
1273
1274
1275
1276
1277 superPanel.putClientProperty(SubstanceLookAndFeel.BACKGROUND_COMPOSITE,
1278 new AlphaControlBackgroundComposite(0.3f, 0.5f));
1279 superPanel.putClientProperty(SubstanceLookAndFeel.SCROLLBAR_GRIP_PAINTER,
1280 new CoreScrollThumbGripPainters.DragBumpsScrollThumbGripPainter());
1281 }
1282
1283 /***
1284 * Sum the mana.
1285 *
1286 * @param registers
1287 * teh array of mana.
1288 * @return the sum of mana.
1289 */
1290 public static int manaPool(int[] registers) {
1291 int res = 0;
1292 for (int i = IdCardColors.CARD_COLOR_NAMES.length; i-- > 0;) {
1293 res += registers[i];
1294 }
1295 return res;
1296 }
1297
1298 }