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