OSDN Git Service

fix: fix crash logic
[delesterandomselector/DelesteRandomSelector.git] / src / com / ranfa / main / DelesteRandomSelector.java
1 /*
2  * Copyright 2022 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.ranfa.main;
17
18 import java.awt.BorderLayout;
19 import java.awt.CardLayout;
20 import java.awt.Desktop;
21 import java.awt.EventQueue;
22 import java.awt.Font;
23 import java.awt.event.ActionEvent;
24 import java.awt.event.ActionListener;
25 import java.io.IOException;
26 import java.net.URI;
27 import java.net.URISyntaxException;
28 import java.nio.file.Files;
29 import java.nio.file.Paths;
30 import java.text.Normalizer;
31 import java.util.ArrayList;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Random;
36 import java.util.concurrent.CompletableFuture;
37 import java.util.concurrent.ExecutorService;
38 import java.util.concurrent.Executors;
39 import java.util.function.BiConsumer;
40 import java.util.stream.Collectors;
41
42 import javax.swing.DefaultComboBoxModel;
43 import javax.swing.JButton;
44 import javax.swing.JCheckBox;
45 import javax.swing.JComboBox;
46 import javax.swing.JFrame;
47 import javax.swing.JLabel;
48 import javax.swing.JOptionPane;
49 import javax.swing.JPanel;
50 import javax.swing.JProgressBar;
51 import javax.swing.JScrollPane;
52 import javax.swing.JSpinner;
53 import javax.swing.JTabbedPane;
54 import javax.swing.JTextArea;
55 import javax.swing.JTextField;
56 import javax.swing.border.EmptyBorder;
57
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 import com.jgoodies.forms.layout.ColumnSpec;
62 import com.jgoodies.forms.layout.FormLayout;
63 import com.jgoodies.forms.layout.FormSpecs;
64 import com.jgoodies.forms.layout.RowSpec;
65 import com.ranfa.lib.CheckVersion;
66 import com.ranfa.lib.Easter;
67 import com.ranfa.lib.ManualUpdateThreadImpl;
68 import com.ranfa.lib.SettingJSONProperty;
69 import com.ranfa.lib.Settings;
70 import com.ranfa.lib.Suffix;
71 import com.ranfa.lib.TwitterIntegration;
72 import com.ranfa.lib.Version;
73 import com.ranfa.lib.calc.FanCalc;
74 import com.ranfa.lib.concurrent.CountedThreadFactory;
75 import com.ranfa.lib.database.EstimateAlbumTypeCycle;
76 import com.ranfa.lib.database.Scraping;
77 import com.ranfa.lib.database.Song;
78 import com.ranfa.lib.handler.CrashHandler;
79 import com.ranfa.lib.songinfo.FetchFromAPI;
80
81 /**
82  * メイン処理クラス。
83  * <p>
84  * メインスレッドでは基本的に何もしない。
85  * 
86  * @author hizum
87  *
88  * @since 0.0.1
89  */
90 @Version(major = 4, minor = 0, patch = 3, suffix = Suffix.BETA)
91 public class DelesteRandomSelector extends JFrame {
92
93         /**
94          * ランダムに選択された楽曲を格納するためのList。
95          * <p>
96          * 選ばれた楽曲を使用する処理は基本的に全てここから取得する。
97          */
98     private static ArrayList<Song> selectedSongsList = new ArrayList<>();
99
100     private JPanel contentPane;
101     private SettingJSONProperty property = new SettingJSONProperty();
102     private String[] integratorArray;
103     private boolean integratorBool = false;
104     private CompletableFuture<Void> softwareUpdateFuture = null;
105     private CompletableFuture<Void> albumTypeEstimateFuture = null;
106     private String albumType = Messages.MSGAlbumTypeBeingCalculated.toString();
107     private Logger logger = LoggerFactory.getLogger(DelesteRandomSelector.class);
108     private ManualUpdateThreadImpl impl;
109     private List<Song> toolIntegrateList;
110     private FetchFromAPI fetchData;
111     private List<Map<String, String>> listToolMapData;
112     private CompletableFuture<List<Map<String, String>>> listToolMapDataFuture;
113     private Easter easter;
114     private JPanel panelMain;
115     private JPanel panelNorthMain;
116     private JLabel labelTitle;
117     private JLabel labelVersion;
118     private JPanel panelWestMain;
119     private JLabel labelDifficulty;
120     private JComboBox<String[]> comboDifficultySelect;
121     private JComboBox<String[]> comboAttribute;
122     private JLabel labelLevel;
123     private JSpinner spinnerLevel;
124     private JCheckBox checkLessLv;
125     private JCheckBox checkMoreLv;
126     private JLabel labelLvCaution;
127     private JPanel panelEastMain;
128     private JButton btnImport;
129     private JButton btnConfig;
130     private JButton btnStart;
131     private JButton btnManualUpdate;
132     private JButton btnTwitterIntegration;
133     private JButton btnExit;
134     private JPanel panelCenterMain;
135     private JScrollPane scrollPane;
136     private JTextArea textArea;
137     private JTabbedPane tabbedPane;
138     private JPanel panelInfo;
139     private JPanel panelNorthTool;
140     private JLabel labelSubToolTitle;
141     private JLabel labelVersionTool;
142     private JPanel panelCenterTool;
143     private JLabel labelInfoPlaySongs;
144     private JLabel labelSongNameToolTitle;
145     private JLabel labelSongNameToolTip;
146     private JLabel labelAttributeToolTitle;
147     private JLabel labelAttributeToolTip;
148     private JLabel labelDifficultyToolTitle;
149     private JLabel labelDifficultyToolTip;
150     private JLabel labelLevelToolTitle;
151     private JLabel labelLevelToolTip;
152     private JLabel labelNotesToolTitle;
153     private JLabel labelNotesToolTip;
154     private JButton btnPrevSongTool;
155     private JButton btnNextSongTool;
156     private JLabel labelSlashTool;
157     private JLabel labelCurrentSongOrderTool;
158     private JLabel labelSongLimitTool;
159     private JLabel labelLyricToolTitle;
160     private JLabel labelLyricToolTip;
161     private JLabel labelComposerToolTitle;
162     private JLabel labelArrangeToolTitle;
163     private JLabel labelComposerToolTip;
164     private JLabel labelArrangeToolTip;
165     private JLabel labelMemberToolTitle;
166     private JLabel labelMemberToolTip;
167     private JButton btnMoreInfoTool;
168     
169     /**
170      * アップデート処理用の{@link BiConsumer}。
171      * <p>
172      * 実際は{@link CompletableFuture}によって使用される。
173      */
174     BiConsumer<ArrayList<Song>, ArrayList<Song>> updateConsumer = (list1, list2) -> {
175             this.logger.info("Checking database updates...");
176             if(list1.size() > list2.size()) {
177                 long time = System.currentTimeMillis();
178                 this.logger.info("{} Update detected.", (list1.size() - list2.size()));
179                 Scraping.writeToJson(list1);
180                 this.logger.info("Update completed in {} ms", (System.currentTimeMillis() - time));
181                 this.logger.info("Updated database size: {}", list1.size());
182             } else {
183                 this.logger.info("database is up-to-date.");
184             }
185         };
186         
187         /**
188          * ライブラリ関連の処理完了後に{@link JButton}を有効化するための{@link Runnable}。
189          * <p>
190          * <ul>
191          * <li>{@link JButton}の有効化</li>
192          * <li>表示するテキストの更新</li>
193          * </ul>
194          * を行う
195          */
196         Runnable setEnabled = () -> {
197             try {
198                 Thread.sleep(3 * 1000L);
199             } catch (InterruptedException e1) {
200                 this.logger.error("Thread has been interrupted during waiting cooldown.", e1);
201             }
202             this.btnImport.setEnabled(true);
203             this.btnImport.setText(Messages.MSGNarrowingDownSongs.toString());
204         };
205         private JLabel labelToolProgress;
206         public static JProgressBar progressTool;
207         public static JLabel labelInfoProgressSongName;
208         private JPanel panelScore;
209         private JPanel panelScoreNorth;
210         private JPanel panelScoreCenter;
211         private JLabel labelScoreTitle;
212         private JLabel labelScoreVersion;
213         private JLabel labelScoreUserPlayed;
214         private JTextField fieldScoreUserPlayed;
215         private JLabel labelScoreEarnedFan;
216         private JTextField fieldScoreEarnedFan;
217         private JLabel lblSongname;
218         private JLabel labelScoreAttribute;
219         private JLabel labelScoreDifficulty;
220         private JLabel labelScoreLevel;
221         private JLabel labelScoreNotes;
222         private JLabel labelScoreSongnameDynamic;
223         private JLabel labelScoreAttributeDynamic;
224         private JLabel labelScoreDifficultyDynamic;
225         private JLabel labelScoreLevelDynamic;
226         private JLabel labelScoreNotesDynamic;
227         private JButton btnScorePrev;
228         private JButton btnScoreNext;
229         private JLabel labelScoreCurrentSongOrder;
230         private JLabel labelScoreSlash;
231         private JLabel labelScoreOrderMax;
232         private JLabel labelPlayerScore;
233         private JLabel labelPlayerFan;
234         private JLabel labelPlayerPRP;
235         private JLabel labelPlayerScoreDynamic;
236         private JLabel labelPlayerFanDynamic;
237         private JLabel labelPlayerPRPDynamic;
238         private JTextField fieldScoreRoom;
239         private JTextField fieldScoreCenter;
240         private JTextField fieldScoreProduce;
241         private JTextField fieldScorePremium;
242         private JLabel label;
243         private JLabel lblRoom;
244         private JLabel lblCenter;
245         private JLabel lblProduce;
246         private JLabel lblPremium;
247         private JButton button;
248
249     /**
250      * Launch the application.
251      */
252     public static void main(String[] args) {
253         EventQueue.invokeLater(() -> {
254             try {
255                 DelesteRandomSelector frame = new DelesteRandomSelector();
256                 frame.setVisible(true);
257             } catch (Exception e) {
258                 e.printStackTrace();
259             }
260
261         });
262     }
263
264     /**
265      * Create the frame.
266      */
267     public DelesteRandomSelector() {
268     ExecutorService es = Executors.newCachedThreadPool(new CountedThreadFactory(() -> "DRS", "AsyncEventInquerier", false));
269         this.contentPane = new JPanel();
270         // output system info phase
271         CompletableFuture.runAsync(() -> {
272                 CrashHandler handle = new CrashHandler();
273                 handle.outSystemInfo();
274         }, es).whenCompleteAsync((ret, ex) -> {
275                 if(ex != null) {
276                         logger.error("Exception was thrown during concurrent process", ex);
277                         CrashHandler handle = new CrashHandler(ex);
278                         if(ex instanceof NullPointerException) {
279                                 handle.execute();
280                         }
281                         handle = new CrashHandler(new IllegalStateException(ex));
282                         handle.execute();
283                 }
284         }, es);
285         boolean isFirst = !Scraping.databaseExists();
286         // database check phase
287         CompletableFuture.runAsync(() -> {
288                 if(isFirst) {
289                     JOptionPane.showMessageDialog(this, Messages.MSGDatabaseNotExist.toString());
290                     if(!Scraping.writeToJson(Scraping.getWholeData())) {
291                         JOptionPane.showMessageDialog(this, "Exception:NullPointerException\nCannot Keep up! Please re-download this Application!");
292                         throw new NullPointerException("FATAL: cannot continue!");
293                     }
294                 }
295         }, es).whenCompleteAsync((ret, ex) -> {
296                 if(ex != null) {
297                         logger.error("Exception was thrown during concurrent process", ex);
298                         CrashHandler handle = new CrashHandler(ex.getMessage(), ex);
299                         if(ex instanceof NullPointerException) {
300                                 handle.execute();
301                         }
302                         handle = new CrashHandler(ex.getMessage(), new IllegalStateException(ex));
303                         handle.execute();
304                 }
305         }, es);
306         CompletableFuture<ArrayList<Song>> getFromJsonFuture = CompletableFuture.supplyAsync(() -> Scraping.getFromJson(), es);
307         CompletableFuture<ArrayList<Song>> getWholeDataFuture = CompletableFuture.supplyAsync(() -> Scraping.getWholeData(), es);
308         // setting check phase
309         CompletableFuture.runAsync(() -> {
310                 if(!Settings.fileExists() && !Settings.writeDownJSON()) {
311                     JOptionPane.showMessageDialog(this, "Exception:NullPointerException\nPlease see crash report for more detail.");
312                     CrashHandler handle = new CrashHandler("Failed to generate setting file.", new NullPointerException("FATAL: cannot continue!"));
313                     handle.execute();
314                 }
315         }, es).whenCompleteAsync((ret, ex) -> {
316                 if(ex != null) {
317                         logger.error("Exception was thrown during concurrent process", ex);
318                         CrashHandler handle = new CrashHandler(ex.getMessage(), ex);
319                         if(ex instanceof NullPointerException) {
320                                 handle.execute();
321                         }
322                         handle = new CrashHandler(ex.getMessage(), new IllegalStateException(ex));
323                         handle.execute();
324                 }
325                 this.logger.debug("Loading settings...");
326                 this.property.setCheckLibraryUpdates(Settings.needToCheckLibraryUpdates());
327                 this.property.setCheckVersion(Settings.needToCheckVersion());
328                 this.property.setWindowWidth(Settings.getWindowWidth());
329                 this.property.setWindowHeight(Settings.getWindowHeight());
330                 this.property.setSongLimit(Settings.getSongsLimit());
331                 this.property.setSaveScoreLog(Settings.saveScoreLog());
332                 this.logger.debug("Load settings done.");
333                 this.logger.debug("Version check: {}", this.property.isCheckVersion());
334                 this.logger.debug("Library update check: {}", this.property.isCheckLibraryUpdates());
335                 this.logger.debug("Window Width: {}", this.property.getWindowWidth());
336                 this.logger.debug("Window Height: {}", this.property.getWindowHeight());
337                 this.logger.debug("Song Limit: {}", this.property.getSongLimit());
338                 this.logger.debug("SaveScoreLog: {}", this.property.isSaveScoreLog());
339                 this.setBounds(100, 100, this.property.getWindowWidth(), this.property.getWindowHeight());
340                 if(this.property.isCheckVersion()) {
341                     this.softwareUpdateFuture = CompletableFuture.runAsync(() -> CheckVersion.needToBeUpdated(), es);
342                 }
343                 if(this.property.isCheckLibraryUpdates()) {
344                     CompletableFuture<Void> updatedFuture = getWholeDataFuture.thenAcceptBothAsync(getFromJsonFuture, updateConsumer, es);
345                     updatedFuture.thenRunAsync(setEnabled, es);
346                 }
347         }, es);
348         CompletableFuture.runAsync(() -> {
349                 EstimateAlbumTypeCycle.Initialization();
350                 if(Files.exists(Paths.get("generated/albumCycle.json"))) {
351                     this.albumType = EstimateAlbumTypeCycle.getCurrentCycle();
352                 }
353         }, es).whenCompleteAsync((ret, ex) -> {
354                 if(ex != null) {
355                         logger.error("Exception was thrown during concurrent process", ex);
356                         CrashHandler handle = new CrashHandler(ex.getMessage(), new IllegalStateException(ex));
357                         handle.execute();
358                 }
359         }, es);
360         getWholeDataFuture.thenAcceptAsync(list -> this.logger.info("Scraping data size:" + list.size()), es);
361         getFromJsonFuture.thenAcceptAsync(list -> this.logger.info("Currently database size:" + list.size()), es);
362         // easter phase
363         CompletableFuture.runAsync(() -> {
364                 this.easter = new Easter();
365                 this.setTitle(this.easter.getTodaysBirth());
366         }, es);
367         this.logger.debug("Version: {}", CheckVersion.getVersion());
368         this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
369         this.setBounds(100, 100, 960, 643);
370         this.contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
371         this.setContentPane(this.contentPane);
372         contentPane.setLayout(new CardLayout(0, 0));
373         
374         panelMain = new JPanel();
375         panelMain.setLayout(new BorderLayout(0, 0));
376         
377         panelNorthMain = new JPanel();
378         panelMain.add(panelNorthMain, BorderLayout.NORTH);
379         panelNorthMain.setLayout(new FormLayout(new ColumnSpec[] {
380                         ColumnSpec.decode("829px"),
381                         FormSpecs.LABEL_COMPONENT_GAP_COLSPEC,
382                         ColumnSpec.decode("center:94px"),},
383                 new RowSpec[] {
384                         RowSpec.decode("20px"),}));
385         
386         labelTitle = new JLabel(Messages.MSGTitle.toString());
387         labelTitle.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 16));
388         panelNorthMain.add(labelTitle, "1, 1, center, top");
389         
390         labelVersion = new JLabel(CheckVersion.getVersion());
391         labelVersion.setFont(new Font("SansSerif", Font.BOLD, 12));
392         panelNorthMain.add(labelVersion, "3, 1, center, center");
393         
394         panelWestMain = new JPanel();
395         panelMain.add(panelWestMain, BorderLayout.WEST);
396         panelWestMain.setLayout(new FormLayout(new ColumnSpec[] {
397                         FormSpecs.LABEL_COMPONENT_GAP_COLSPEC,
398                         ColumnSpec.decode("120px"),},
399                 new RowSpec[] {
400                         FormSpecs.LINE_GAP_ROWSPEC,
401                         RowSpec.decode("25px"),
402                         FormSpecs.RELATED_GAP_ROWSPEC,
403                         FormSpecs.DEFAULT_ROWSPEC,
404                         FormSpecs.RELATED_GAP_ROWSPEC,
405                         FormSpecs.DEFAULT_ROWSPEC,
406                         FormSpecs.RELATED_GAP_ROWSPEC,
407                         FormSpecs.DEFAULT_ROWSPEC,
408                         FormSpecs.RELATED_GAP_ROWSPEC,
409                         FormSpecs.DEFAULT_ROWSPEC,
410                         FormSpecs.RELATED_GAP_ROWSPEC,
411                         FormSpecs.DEFAULT_ROWSPEC,
412                         FormSpecs.RELATED_GAP_ROWSPEC,
413                         FormSpecs.DEFAULT_ROWSPEC,
414                         FormSpecs.RELATED_GAP_ROWSPEC,
415                         RowSpec.decode("max(41dlu;default)"),}));
416         
417         labelDifficulty = new JLabel(Messages.MSGSelectDifficulty.toString());
418         labelDifficulty.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
419         panelWestMain.add(labelDifficulty, "2, 2, center, center");
420         
421         comboDifficultySelect = new JComboBox<>();
422         comboDifficultySelect.setModel(new DefaultComboBoxModel(new String[] {Messages.MSGNonSelected.toString(), "DEBUT", "REGULAR", "PRO", "MASTER", "MASTER+", "ⓁMASTER+", "LIGHT", "TRICK", "PIANO", "FORTE", "WITCH"}));
423         comboDifficultySelect.setFont(new Font("Dialog", Font.BOLD, 12));
424         panelWestMain.add(comboDifficultySelect, "2, 4, fill, fill");
425         
426         comboAttribute = new JComboBox<>();
427         comboAttribute.setModel(new DefaultComboBoxModel(new String[] {Messages.MSGNonSelected.toString(), "全タイプ", "キュート", "クール", "パッション"}));
428         comboAttribute.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
429         panelWestMain.add(comboAttribute, "2, 6, fill, top");
430         
431         labelLevel = new JLabel(Messages.MSGSongLevel.toString());
432         labelLevel.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
433         panelWestMain.add(labelLevel, "2, 8, center, center");
434         
435         spinnerLevel = new JSpinner();
436         panelWestMain.add(spinnerLevel, "2, 10, fill, center");
437         
438         checkLessLv = new JCheckBox(Messages.MSGBelowSpecificLevel.toString());
439         checkLessLv.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
440         panelWestMain.add(checkLessLv, "2, 12, left, top");
441         
442         checkMoreLv = new JCheckBox(Messages.MSGOverSpecificLevel.toString());
443         checkMoreLv.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
444         panelWestMain.add(checkMoreLv, "2, 14, left, top");
445         
446         labelLvCaution = new JLabel(Messages.MSGLevelCheckboxInfo.toString());
447         labelLvCaution.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
448         panelWestMain.add(labelLvCaution, "2, 16, left, center");
449         
450         panelEastMain = new JPanel();
451         panelMain.add(panelEastMain, BorderLayout.EAST);
452         panelEastMain.setLayout(new FormLayout(new ColumnSpec[] {
453                         FormSpecs.LABEL_COMPONENT_GAP_COLSPEC,
454                         ColumnSpec.decode("100px"),},
455                 new RowSpec[] {
456                         FormSpecs.LINE_GAP_ROWSPEC,
457                         RowSpec.decode("77px"),
458                         FormSpecs.RELATED_GAP_ROWSPEC,
459                         FormSpecs.DEFAULT_ROWSPEC,
460                         FormSpecs.RELATED_GAP_ROWSPEC,
461                         FormSpecs.DEFAULT_ROWSPEC,
462                         FormSpecs.RELATED_GAP_ROWSPEC,
463                         FormSpecs.DEFAULT_ROWSPEC,
464                         FormSpecs.RELATED_GAP_ROWSPEC,
465                         RowSpec.decode("max(36dlu;default)"),
466                         FormSpecs.RELATED_GAP_ROWSPEC,
467                         FormSpecs.DEFAULT_ROWSPEC,}));
468         
469         btnImport = new JButton(Messages.MSGUpdatingDatabase.toString());
470         btnImport.addActionListener(e -> {
471                 CompletableFuture.runAsync(() -> {
472                         if(impl != null) {
473                                 if(!impl.getFlag()) {
474                                         JOptionPane.showMessageDialog(null, Messages.MSGManualUpdateNotCompleteYet.toString());
475                                 }
476                         }
477                         ArrayList<Song> fromJson = Scraping.getFromJson();
478                         ArrayList<Song> specificlevelList = Scraping.getSpecificLevelSongs(fromJson, (Integer)DelesteRandomSelector.this.spinnerLevel.getValue(), DelesteRandomSelector.this.checkLessLv.isSelected(), DelesteRandomSelector.this.checkMoreLv.isSelected());
479                         ArrayList<Song> specificDifficultyList = Scraping.getSpecificDifficultySongs(specificlevelList, DelesteRandomSelector.this.comboDifficultySelect.getSelectedItem().toString());
480                         ArrayList<Song> specificAttributeList = Scraping.getSpecificAttributeSongs(specificDifficultyList, DelesteRandomSelector.this.comboAttribute.getSelectedItem().toString());
481                         ArrayList<Song> specificTypeList = Scraping.getSpecificAlbumTypeSongs(specificAttributeList, EstimateAlbumTypeCycle.getCurrentCycle());
482                         if(!selectedSongsList.isEmpty()) {
483                                 selectedSongsList.clear();
484                         }
485                         selectedSongsList.addAll((DelesteRandomSelector.this.comboDifficultySelect.getSelectedItem().equals(Scraping.MASTERPLUS) || DelesteRandomSelector.this.comboDifficultySelect.getSelectedItem().equals(Scraping.LEGACYMASTERPLUS)) ? specificTypeList : specificAttributeList);
486                         DelesteRandomSelector.this.logger.info("Songs are selected.We are Ready to go.");
487                         JOptionPane.showMessageDialog(null, Messages.MSGCompleteNarrowDown.toString());
488                 }, es).whenCompleteAsync((ret, ex) -> {
489                         if(ex != null) {
490                                 logger.error("Exception was thrown during concurrent process", ex);
491                                 if(ex instanceof IllegalArgumentException)
492                                         return; // ignore
493                                 CrashHandler handle = new CrashHandler(ex.getMessage(), new IllegalStateException(ex));
494                                 handle.execute();
495                         }
496                 }, es);
497         });
498         btnImport.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
499         btnImport.setEnabled(false);
500         panelEastMain.add(btnImport, "2, 2, fill, fill");
501         
502         btnConfig = new JButton(Messages.MSGConfigurations.toString());
503         btnConfig.addActionListener(e -> {
504                 CompletableFuture.runAsync(() -> {
505                         ProcessBuilder builder = new ProcessBuilder("java", "-jar", "Configurations.jar");
506                         try {
507                                 builder.start();
508                         } catch (IOException e1) {
509                                 logger.error("Exception was thrown during concurrent process", e1);
510                                 CrashHandler handle = new CrashHandler(new IllegalStateException(e1));
511                                 handle.execute();
512                         }
513                 }, es).whenCompleteAsync((ret, ex) -> {
514                         if(ex != null) {
515                                 logger.error("Exception was thrown during concurrent process", ex);
516                                 CrashHandler handle = new CrashHandler(ex.getMessage(), new IllegalStateException(ex));
517                                 handle.execute();
518                         }
519                 }, es);
520         });
521         panelEastMain.add(btnConfig, "2, 6, fill, fill");
522         
523         btnStart = new JButton(Messages.MSGCalcStart.toString());
524         btnStart.addActionListener(e -> {
525                 CompletableFuture.runAsync(() -> {
526                         if(selectedSongsList.isEmpty()) {
527                                 logger.warn("User has not started playing yet.");
528                                 return;
529                         }
530                         Random random = new Random(System.currentTimeMillis());
531                         toolIntegrateList = new ArrayList<>();
532                         StringBuilder paneBuilder = new StringBuilder();
533                         DelesteRandomSelector.this.integratorArray = new String[DelesteRandomSelector.this.property.getSongLimit()];
534                         for(int i = 0; i < DelesteRandomSelector.this.property.getSongLimit(); i++) {
535                                 int randomInt = random.nextInt(selectedSongsList.size());
536                                 String typeString = DelesteRandomSelector.this.comboDifficultySelect.getSelectedItem().equals(Scraping.MASTERPLUS) || DelesteRandomSelector.this.comboDifficultySelect.getSelectedItem().equals(Scraping.LEGACYMASTERPLUS) ? EstimateAlbumTypeCycle.getCurrentCycle() : "";
537                                 paneBuilder.append(i + 1)
538                                         .append(Messages.MSGNumberOfSongs.toString())
539                                         .append(" ")
540                                         .append(selectedSongsList.get(randomInt).getAttribute())
541                                         .append("[")
542                                         .append(selectedSongsList.get(randomInt).getDifficulty())
543                                         .append("]「")
544                                         .append(selectedSongsList.get(randomInt).getName())
545                                         .append("」!(Lv:")
546                                         .append(selectedSongsList.get(randomInt).getLevel())
547                                         .append(" ")
548                                         .append(typeString)
549                                         .append(")\n\n");
550                                 this.integratorArray[i] = selectedSongsList.get(randomInt).getName() + "(Lv" + selectedSongsList.get(randomInt).getLevel() + ")\n";
551                                 toolIntegrateList.add(selectedSongsList.get(randomInt));
552                         }
553                                 paneBuilder.append(Messages.MSGThisPhrase.toString())
554                                         .append(this.property.getSongLimit())
555                                         .append(Messages.MSGPlayPhrase.toString());
556                         DelesteRandomSelector.this.textArea.setText(paneBuilder.toString());
557                         DelesteRandomSelector.this.integratorBool = true;
558                         DelesteRandomSelector.this.logger.info("show up completed.");
559                         labelCurrentSongOrderTool.setText("null");
560                         progressTool.setValue(0);
561                         listToolMapDataFuture = CompletableFuture.supplyAsync(() -> {
562                                 List<String> data = toolIntegrateList.stream()
563                                                 .map(s -> s.getName())
564                                                 .collect(Collectors.toList());
565                                 fetchData = new FetchFromAPI(data.toArray(new String[0]));
566                                 return fetchData.getInformation();
567                         }, es);
568                         logger.debug("api fetch inquery published");
569                 }, es).whenCompleteAsync((ret, ex) -> {
570                         if(ex != null) {
571                                 logger.error("Exception was thrown during concurrent process", ex);
572                                 CrashHandler handle = new CrashHandler(ex.getMessage(), new IllegalStateException(ex));
573                                 handle.execute();
574                         }
575                 }, es);
576         });
577         btnStart.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
578         panelEastMain.add(btnStart, "2, 4, fill, fill");
579         
580         btnManualUpdate = new JButton(Messages.MSGManualUpdate.toString());
581         btnManualUpdate.addActionListener(e -> {
582                 impl = new ManualUpdateThreadImpl();
583                 CompletableFuture.runAsync(impl, es).whenCompleteAsync((t, u) -> {
584                         if(u != null) {
585                                 logger.warn("Exception while processing update manually.", u);
586                                 CrashHandler handle = new CrashHandler(u.getMessage(), new IllegalStateException(u));
587                                 handle.execute();
588                                 JOptionPane.showMessageDialog(null, "There was a problem during processing library update manually.\nIf this appears repeatedly, please contact developer with your app log.");
589                         }
590                 }, es);
591         });
592         panelEastMain.add(btnManualUpdate, "2, 8, fill, fill");
593         
594         btnTwitterIntegration = new JButton(Messages.MSGTwitterIntegration.toString());
595         btnTwitterIntegration.addActionListener(e -> {
596                 CompletableFuture.runAsync(() -> {
597                         boolean authorizationStatus = TwitterIntegration.authorization();
598                         String updatedStatus = Messages.MSGUsingThisAppPhrase.toString();
599                         int lengthLimit = updatedStatus.length();
600                         boolean isBroken = false;
601                         if(!DelesteRandomSelector.this.integratorBool) {
602                                 JOptionPane.showMessageDialog(null, Messages.MSGNotPlayYet.toString());
603                                 return;
604                         }
605                         for (String element : DelesteRandomSelector.this.integratorArray) {
606                                 updatedStatus = updatedStatus + element;
607                                 lengthLimit += element.length();
608                                 if(lengthLimit > 69) {
609                                         isBroken = true;
610                                         break;
611                                 }
612                         }
613                         if(isBroken) {
614                                 updatedStatus = updatedStatus + Messages.MSGTwitterPlayOtherwisePhrase.toString() + "\n#DelesteRandomSelector #デレステ ";
615                         } else {
616                                 updatedStatus = updatedStatus + Messages.MSGTwitterPlayOnlyPhrase.toString() + "\n#DelesteRandomSelector #デレステ ";
617                         }
618                         DelesteRandomSelector.this.logger.info("status message constructed.");
619                         lengthLimit = updatedStatus.length();
620                         if(authorizationStatus) {
621                                 int option = JOptionPane.showConfirmDialog(null, Messages.MSGTwitterIntegrationConfirm.toString() + updatedStatus + Messages.MSGStringLength.toString() + lengthLimit);
622                                 DelesteRandomSelector.this.logger.info("user seletced: " + option);
623                                 switch(option) {
624                                 case JOptionPane.OK_OPTION:
625                                         TwitterIntegration.PostTwitter(updatedStatus);
626                                         DelesteRandomSelector.this.logger.info("Success to update the status.");
627                                         JOptionPane.showMessageDialog(null, Messages.MSGCompletePost.toString());
628                                         break;
629                                 case JOptionPane.NO_OPTION:
630                                         DelesteRandomSelector.this.logger.info("There is no will to post.");
631                                         break;
632                                 case JOptionPane.CANCEL_OPTION:
633                                         DelesteRandomSelector.this.logger.info("The Operation was canceled by user.");
634                                         break;
635                                 default:
636                                         break;
637                                 }
638                         } else {
639                                 DelesteRandomSelector.this.logger.info("seems to reject the permission.it should need try again.");
640                         }
641                 }, es).whenCompleteAsync((ret, ex) -> {
642                         if(ex != null) {
643                                 logger.error("Exception was thrown during concurrent process", ex);
644                                 CrashHandler handle = new CrashHandler(ex.getMessage(), new IllegalStateException(ex));
645                                 handle.execute();
646                         }
647                 }, es);
648         });
649         btnTwitterIntegration.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 11));
650         panelEastMain.add(btnTwitterIntegration, "2, 10, fill, fill");
651         
652         btnExit = new JButton(Messages.MSGTerminate.toString());
653         btnExit.addActionListener(e -> {
654                 if(DelesteRandomSelector.this.softwareUpdateFuture.isDone() || DelesteRandomSelector.this.albumTypeEstimateFuture.isDone() || !this.impl.getFlag()) {
655                         DelesteRandomSelector.this.logger.info("Requested Exit by Button.");
656                         logger.info("Shut down thread pool.");
657                         es.shutdown();
658                         System.exit(0);
659                 } else {
660                         JOptionPane.showMessageDialog(null, Messages.MSGInternalYpdateNotDoneYet.toString());
661                 }
662         });
663         btnExit.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
664         panelEastMain.add(btnExit, "2, 12, fill, fill");
665         
666         panelCenterMain = new JPanel();
667         panelMain.add(panelCenterMain, BorderLayout.CENTER);
668         panelCenterMain.setLayout(new BorderLayout(0, 0));
669         
670         scrollPane = new JScrollPane();
671         panelCenterMain.add(scrollPane);
672         
673         textArea = new JTextArea();
674         textArea.setText(Messages.MSGNarrowDownProcedure.toString() + property.getSongLimit() + Messages.MSGCurrentAlbumType.toString() + albumType);
675         textArea.setEditable(false);
676         scrollPane.setViewportView(textArea);
677         
678         tabbedPane = new JTabbedPane(JTabbedPane.TOP);
679         tabbedPane.addChangeListener(e -> {
680                 CompletableFuture.runAsync(() -> {
681                         labelToolProgress.setText(Messages.MSGAPIWaitAPIFetch.toString());
682                         String currentTabName = tabbedPane.getTitleAt(tabbedPane.getSelectedIndex());
683                         if(currentTabName.equals("SongInfo") && labelCurrentSongOrderTool.getText().equals("null")) {
684                                 logger.info("Detected switching tool tab");
685                                 if(listToolMapDataFuture == null) {
686                                         logger.warn("Async task has not initialized yet. Aborting...");
687                                         return;
688                                 }
689                                 if(toolIntegrateList == null) {
690                                         return;
691                                 }
692                                 listToolMapData = listToolMapDataFuture.join();
693                                 Song firstSong = toolIntegrateList.get(0);
694                                 Map<String, String> fetchMap = new HashMap<>();
695                                 for(Map<String, String> tmpMap : listToolMapData) {
696                                         String normalizeApiName = Normalizer.normalize(tmpMap.get("songname").toString(), Normalizer.Form.NFKD);
697                                         String normalizeLocalName = Normalizer.normalize(firstSong.getName(), Normalizer.Form.NFKD);
698                                         if(normalizeApiName.equals(normalizeLocalName)) {
699                                                 fetchMap = tmpMap;
700                                                 break;
701                                         }
702                                 }
703                                 labelSongNameToolTip.setText(firstSong.getName());
704                                 labelAttributeToolTip.setText(firstSong.getAttribute());
705                                 labelDifficultyToolTip.setText(firstSong.getDifficulty());
706                                 labelLevelToolTip.setText(String.valueOf(firstSong.getLevel()));
707                                 labelNotesToolTip.setText(String.valueOf(firstSong.getNotes()));
708                                 labelCurrentSongOrderTool.setText("1");
709                                 labelLyricToolTip.setText(fetchMap.get("lyric"));
710                                 labelComposerToolTip.setText(fetchMap.get("composer"));
711                                 labelArrangeToolTip.setText(fetchMap.get("arrange"));
712                                 labelMemberToolTip.setText("<html><body>" + fetchMap.get("member") + "</html></body>");
713                         }
714                         if(currentTabName.equals("Scores") && labelScoreCurrentSongOrder.getText().equals("null")) {
715                                 logger.info("Detected switching score tab");
716                                 if(toolIntegrateList == null)
717                                         return;
718                                 Song firstSong = toolIntegrateList.get(0);
719                                 labelScoreCurrentSongOrder.setText("1");
720                                 labelScoreSongnameDynamic.setText("<html><body>" + firstSong.getName() + "</body></html>");
721                                 labelScoreAttributeDynamic.setText(firstSong.getAttribute());
722                                 labelScoreDifficultyDynamic.setText(firstSong.getDifficulty());
723                                 labelScoreLevelDynamic.setText(String.valueOf(firstSong.getLevel()));
724                                 labelScoreNotesDynamic.setText(String.valueOf(firstSong.getNotes()));
725                         }
726                 }, es).whenCompleteAsync((ret, ex) -> {
727                         labelToolProgress.setText("Information parse Complete.");
728                         if(ex != null) {
729                                 logger.error("Exception was thrown during concurrent process", ex);
730                                 CrashHandler handle = new CrashHandler(ex.getMessage(), new IllegalStateException(ex));
731                                 handle.execute();
732                         }
733                 }, es);
734         });
735         tabbedPane.addTab("Main", null, panelMain, null);
736         contentPane.add(tabbedPane, "name_307238585319500");
737         
738         panelInfo = new JPanel();
739         tabbedPane.addTab("SongInfo", null, panelInfo, null);
740         panelInfo.setLayout(new BorderLayout(0, 0));
741         
742         panelNorthTool = new JPanel();
743         panelInfo.add(panelNorthTool, BorderLayout.NORTH);
744         panelNorthTool.setLayout(new FormLayout(new ColumnSpec[] {
745                         ColumnSpec.decode("center:max(524dlu;default)"),
746                         FormSpecs.RELATED_GAP_COLSPEC,
747                         ColumnSpec.decode("max(30dlu;default)"),},
748                 new RowSpec[] {
749                         RowSpec.decode("max(16dlu;default)"),}));
750         
751         labelSubToolTitle = new JLabel("楽曲情報");
752         labelSubToolTitle.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 16));
753         panelNorthTool.add(labelSubToolTitle, "1, 1");
754         
755         labelVersionTool = new JLabel(CheckVersion.getVersion());
756         labelVersionTool.setFont(new Font("SansSerif", Font.BOLD, 12));
757         panelNorthTool.add(labelVersionTool, "3, 1");
758         
759         panelCenterTool = new JPanel();
760         panelInfo.add(panelCenterTool, BorderLayout.CENTER);
761         panelCenterTool.setLayout(new FormLayout(new ColumnSpec[] {
762                         FormSpecs.RELATED_GAP_COLSPEC,
763                         ColumnSpec.decode("max(40dlu;default)"),
764                         FormSpecs.RELATED_GAP_COLSPEC,
765                         ColumnSpec.decode("10dlu"),
766                         FormSpecs.RELATED_GAP_COLSPEC,
767                         ColumnSpec.decode("10dlu"),
768                         FormSpecs.RELATED_GAP_COLSPEC,
769                         ColumnSpec.decode("max(12dlu;default)"),
770                         FormSpecs.RELATED_GAP_COLSPEC,
771                         ColumnSpec.decode("max(90dlu;default)"),
772                         FormSpecs.RELATED_GAP_COLSPEC,
773                         ColumnSpec.decode("max(14dlu;default)"),
774                         FormSpecs.RELATED_GAP_COLSPEC,
775                         FormSpecs.RELATED_GAP_COLSPEC,
776                         ColumnSpec.decode("max(14dlu;default)"),
777                         FormSpecs.RELATED_GAP_COLSPEC,
778                         ColumnSpec.decode("max(90dlu;default)"),
779                         FormSpecs.RELATED_GAP_COLSPEC,
780                         FormSpecs.DEFAULT_COLSPEC,
781                         FormSpecs.RELATED_GAP_COLSPEC,
782                         FormSpecs.DEFAULT_COLSPEC,
783                         FormSpecs.RELATED_GAP_COLSPEC,
784                         FormSpecs.DEFAULT_COLSPEC,
785                         FormSpecs.RELATED_GAP_COLSPEC,
786                         ColumnSpec.decode("max(90dlu;default)"),},
787                 new RowSpec[] {
788                         FormSpecs.RELATED_GAP_ROWSPEC,
789                         FormSpecs.DEFAULT_ROWSPEC,
790                         FormSpecs.RELATED_GAP_ROWSPEC,
791                         FormSpecs.DEFAULT_ROWSPEC,
792                         FormSpecs.RELATED_GAP_ROWSPEC,
793                         FormSpecs.DEFAULT_ROWSPEC,
794                         FormSpecs.RELATED_GAP_ROWSPEC,
795                         FormSpecs.DEFAULT_ROWSPEC,
796                         FormSpecs.RELATED_GAP_ROWSPEC,
797                         FormSpecs.DEFAULT_ROWSPEC,
798                         FormSpecs.RELATED_GAP_ROWSPEC,
799                         FormSpecs.DEFAULT_ROWSPEC,
800                         FormSpecs.RELATED_GAP_ROWSPEC,
801                         FormSpecs.DEFAULT_ROWSPEC,
802                         FormSpecs.RELATED_GAP_ROWSPEC,
803                         FormSpecs.DEFAULT_ROWSPEC,
804                         FormSpecs.RELATED_GAP_ROWSPEC,
805                         FormSpecs.DEFAULT_ROWSPEC,
806                         FormSpecs.RELATED_GAP_ROWSPEC,
807                         FormSpecs.DEFAULT_ROWSPEC,
808                         FormSpecs.RELATED_GAP_ROWSPEC,
809                         FormSpecs.DEFAULT_ROWSPEC,
810                         FormSpecs.RELATED_GAP_ROWSPEC,
811                         FormSpecs.DEFAULT_ROWSPEC,
812                         FormSpecs.RELATED_GAP_ROWSPEC,
813                         FormSpecs.DEFAULT_ROWSPEC,
814                         FormSpecs.RELATED_GAP_ROWSPEC,
815                         FormSpecs.DEFAULT_ROWSPEC,
816                         FormSpecs.RELATED_GAP_ROWSPEC,
817                         FormSpecs.DEFAULT_ROWSPEC,
818                         FormSpecs.RELATED_GAP_ROWSPEC,
819                         FormSpecs.DEFAULT_ROWSPEC,
820                         FormSpecs.RELATED_GAP_ROWSPEC,
821                         FormSpecs.DEFAULT_ROWSPEC,
822                         FormSpecs.RELATED_GAP_ROWSPEC,
823                         FormSpecs.DEFAULT_ROWSPEC,
824                         FormSpecs.RELATED_GAP_ROWSPEC,
825                         FormSpecs.DEFAULT_ROWSPEC,
826                         FormSpecs.RELATED_GAP_ROWSPEC,
827                         FormSpecs.DEFAULT_ROWSPEC,
828                         FormSpecs.RELATED_GAP_ROWSPEC,
829                         FormSpecs.DEFAULT_ROWSPEC,}));
830         
831         labelInfoPlaySongs = new JLabel(Messages.MSGInfoPlayedSongs.toString());
832         labelInfoPlaySongs.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
833         panelCenterTool.add(labelInfoPlaySongs, "2, 2, center, default");
834         
835         labelSongNameToolTitle = new JLabel(Messages.MSGInfoSongName.toString());
836         labelSongNameToolTitle.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
837         panelCenterTool.add(labelSongNameToolTitle, "2, 6, center, default");
838         
839         labelSongNameToolTip = new JLabel(Messages.MSGInfoWait.toString());
840         labelSongNameToolTip.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
841         panelCenterTool.add(labelSongNameToolTip, "10, 6, center, default");
842         
843         labelLyricToolTitle = new JLabel(Messages.MSGInfoLyricsBy.toString());
844         labelLyricToolTitle.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
845         panelCenterTool.add(labelLyricToolTitle, "17, 6, center, default");
846         
847         labelLyricToolTip = new JLabel(Messages.MSGInfoWait.toString());
848         labelLyricToolTip.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
849         panelCenterTool.add(labelLyricToolTip, "25, 6, center, default");
850         
851         labelAttributeToolTitle = new JLabel(Messages.MSGInfoSongAttribute.toString());
852         labelAttributeToolTitle.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
853         panelCenterTool.add(labelAttributeToolTitle, "2, 10, center, default");
854         
855         labelAttributeToolTip = new JLabel(Messages.MSGInfoWait.toString());
856         labelAttributeToolTip.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
857         panelCenterTool.add(labelAttributeToolTip, "10, 10, center, default");
858         
859         labelComposerToolTitle = new JLabel(Messages.MSGInfoComposedBy.toString());
860         labelComposerToolTitle.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
861         panelCenterTool.add(labelComposerToolTitle, "17, 10, center, default");
862         
863         labelComposerToolTip = new JLabel(Messages.MSGInfoWait.toString());
864         labelComposerToolTip.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
865         panelCenterTool.add(labelComposerToolTip, "25, 10, center, default");
866         
867         labelDifficultyToolTitle = new JLabel(Messages.MSGInfoSongDifficulty.toString());
868         labelDifficultyToolTitle.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
869         panelCenterTool.add(labelDifficultyToolTitle, "2, 14, center, default");
870         
871         labelDifficultyToolTip = new JLabel(Messages.MSGInfoWait.toString());
872         labelDifficultyToolTip.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
873         panelCenterTool.add(labelDifficultyToolTip, "10, 14, center, default");
874         
875         labelArrangeToolTitle = new JLabel(Messages.MSGInfoArrangedBy.toString());
876         labelArrangeToolTitle.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
877         panelCenterTool.add(labelArrangeToolTitle, "17, 14, center, default");
878         
879         labelArrangeToolTip = new JLabel(Messages.MSGInfoWait.toString());
880         labelArrangeToolTip.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
881         panelCenterTool.add(labelArrangeToolTip, "25, 14, center, default");
882         
883         labelLevelToolTitle = new JLabel(Messages.MSGInfoSongLevel.toString());
884         labelLevelToolTitle.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
885         panelCenterTool.add(labelLevelToolTitle, "2, 18, center, default");
886         
887         labelLevelToolTip = new JLabel(Messages.MSGInfoWait.toString());
888         labelLevelToolTip.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
889         panelCenterTool.add(labelLevelToolTip, "10, 18, center, default");
890         
891         labelMemberToolTitle = new JLabel(Messages.MSGInfoMember.toString());
892         labelMemberToolTitle.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
893         panelCenterTool.add(labelMemberToolTitle, "17, 18, center, default");
894         
895         labelMemberToolTip = new JLabel(Messages.MSGInfoWait.toString());
896         labelMemberToolTip.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
897         panelCenterTool.add(labelMemberToolTip, "25, 18, center, default");
898         
899         labelNotesToolTitle = new JLabel(Messages.MSGInfoSongNotes.toString());
900         labelNotesToolTitle.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
901         panelCenterTool.add(labelNotesToolTitle, "2, 22, center, default");
902         
903         labelNotesToolTip = new JLabel(Messages.MSGInfoWait.toString());
904         labelNotesToolTip.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
905         panelCenterTool.add(labelNotesToolTip, "10, 22, center, default");
906         
907         btnNextSongTool = new JButton("next");
908         btnNextSongTool.addActionListener(e -> {
909                 CompletableFuture.runAsync(() -> {
910                         int currentIndex = Integer.parseInt(labelCurrentSongOrderTool.getText()) - 1;
911                         if(currentIndex != property.getSongLimit() - 1) {
912                                 Song nextSong = toolIntegrateList.get(currentIndex + 1);
913                                 logger.info("currently : {} Next: {}", currentIndex + 1, currentIndex + 2);
914                                 logger.info("nextSong: {}", nextSong);
915                                 Map<String, String> fetchMap = new HashMap<>();
916                                 for(Map<String, String> tmpMap : listToolMapData) {
917                                         String normalizeApiName = Normalizer.normalize(tmpMap.get("songname").toString(), Normalizer.Form.NFKD);
918                                         String normalizeLocalName = Normalizer.normalize(nextSong.getName(), Normalizer.Form.NFKD);
919                                         if(normalizeApiName.equals(normalizeLocalName)) {
920                                                 fetchMap = tmpMap;
921                                                 break;
922                                         }
923                                 }
924                                 labelSongNameToolTip.setText(nextSong.getName());
925                                 labelAttributeToolTip.setText(nextSong.getAttribute());
926                                 labelDifficultyToolTip.setText(nextSong.getDifficulty());
927                                 labelLevelToolTip.setText(String.valueOf(nextSong.getLevel()));
928                                 labelNotesToolTip.setText(String.valueOf(nextSong.getNotes()));
929                                 labelCurrentSongOrderTool.setText(String.valueOf(currentIndex + 2));
930                                 labelLyricToolTip.setText(fetchMap.get("lyric"));
931                                 labelComposerToolTip.setText(fetchMap.get("composer"));
932                                 labelArrangeToolTip.setText(fetchMap.get("arrange"));
933                                 labelMemberToolTip.setText("<html><body>" + fetchMap.get("member") + "</html></body>");
934                         }
935                 }, es).whenCompleteAsync((ret, ex) -> {
936                         if(ex != null) {
937                                 logger.error("Exception was thrown during concurrent process", ex);
938                         }
939                 }, es);
940         });
941         
942         btnPrevSongTool = new JButton("prev");
943         btnPrevSongTool.addActionListener(e -> {
944                 CompletableFuture.runAsync(() -> {
945                         int currentIndex = Integer.parseInt(labelCurrentSongOrderTool.getText()) - 1;
946                         if(currentIndex != 0) {
947                                 Song prevSong = toolIntegrateList.get(currentIndex - 1);
948                                 logger.info("currently : {} Next: {}", currentIndex + 1, currentIndex);
949                                 logger.info("prevSong: {}", prevSong);
950                                 Map<String, String> fetchMap = new HashMap<>();
951                                 for(Map<String, String> tmpMap : listToolMapData) {
952                                         String normalizeApiName = Normalizer.normalize(tmpMap.get("songname").toString(), Normalizer.Form.NFKD);
953                                         String normalizeLocalName = Normalizer.normalize(prevSong.getName(), Normalizer.Form.NFKD);
954                                         if(normalizeApiName.equals(normalizeLocalName)) {
955                                                 fetchMap = tmpMap;
956                                                 break;
957                                         }
958                                 }
959                                 labelSongNameToolTip.setText(prevSong.getName());
960                                 labelAttributeToolTip.setText(prevSong.getAttribute());
961                                 labelDifficultyToolTip.setText(prevSong.getDifficulty());
962                                 labelLevelToolTip.setText(String.valueOf(prevSong.getLevel()));
963                                 labelNotesToolTip.setText(String.valueOf(prevSong.getNotes()));
964                                 labelCurrentSongOrderTool.setText(String.valueOf(currentIndex));
965                                 labelLyricToolTip.setText(fetchMap.get("lyric"));
966                                 labelComposerToolTip.setText(fetchMap.get("composer"));
967                                 labelArrangeToolTip.setText(fetchMap.get("arrange"));
968                                 labelMemberToolTip.setText("<html><body>" + fetchMap.get("member") + "</html></body>");
969                         }
970                 }, es).whenCompleteAsync((ret, ex) -> {
971                         if(ex != null) {
972                                 logger.error("Exception was thrown during concurrent process", ex);
973                         }
974                 }, es);
975         });
976         btnPrevSongTool.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
977         panelCenterTool.add(btnPrevSongTool, "10, 28");
978         
979         labelCurrentSongOrderTool = new JLabel("null");
980         panelCenterTool.add(labelCurrentSongOrderTool, "12, 28");
981         
982         labelSlashTool = new JLabel("/");
983         panelCenterTool.add(labelSlashTool, "14, 28");
984         
985         labelSongLimitTool = new JLabel(String.valueOf(this.property.getSongLimit()));
986         panelCenterTool.add(labelSongLimitTool, "15, 28");
987         btnNextSongTool.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
988         panelCenterTool.add(btnNextSongTool, "17, 28");
989         
990         btnMoreInfoTool = new JButton(Messages.MSGInfoOpenBrowser.toString());
991         btnMoreInfoTool.addActionListener(e -> {
992                 CompletableFuture.runAsync(() -> {
993                         int currentIndex = Integer.parseInt(labelCurrentSongOrderTool.getText()) - 1;
994                         Song currentSong = toolIntegrateList.get(currentIndex);
995                         Map<String, String> fetchMap = new HashMap<>();
996                         for(Map<String, String> tmpMap : listToolMapData) {
997                                 String normalizeApiName = Normalizer.normalize(tmpMap.get("songname").toString(), Normalizer.Form.NFKD);
998                                 String normalizeLocalName = Normalizer.normalize(currentSong.getName(), Normalizer.Form.NFKD);
999                                 if(normalizeApiName.equals(normalizeLocalName)) {
1000                                         fetchMap = tmpMap;
1001                                         break;
1002                                 }
1003                         }
1004                         Desktop desk = Desktop.getDesktop();
1005                         String api = fetchMap.get("link");
1006                         URI uri;
1007                         try {
1008                                 uri = new URI(api);
1009                                 logger.info("Opening default browser with : {}", uri);
1010                                 desk.browse(uri);
1011                         } catch (URISyntaxException | IOException e1) {
1012                                 JOptionPane.showMessageDialog(null, "このメッセージは仮です。Exception : " + e1.getClass().getSimpleName());
1013                                 logger.error("Exception while opening default browser.", e1);
1014                         }
1015                 }, es).whenCompleteAsync((ret, ex) -> {
1016                         if(ex != null) {
1017                                 logger.warn("Exception was thrown during action events.", ex);
1018                         }
1019                 }, es);
1020         });
1021         btnMoreInfoTool.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
1022         panelCenterTool.add(btnMoreInfoTool, "25, 28");
1023         
1024         labelToolProgress = new JLabel(Messages.MSGAPIWaitAPIFetch.toString());
1025         labelToolProgress.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
1026         panelCenterTool.add(labelToolProgress, "10, 32, center, default");
1027         
1028         progressTool = new JProgressBar();
1029         progressTool.setStringPainted(true);
1030         progressTool.setValue(0);
1031         progressTool.setMaximum(property.getSongLimit());
1032         panelCenterTool.add(progressTool, "17, 32");
1033         
1034         labelInfoProgressSongName = new JLabel("Processing:");
1035         labelInfoProgressSongName.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 12));
1036         panelCenterTool.add(labelInfoProgressSongName, "10, 34");
1037         
1038         panelScore = new JPanel();
1039         tabbedPane.addTab("Scores", null, panelScore, null);
1040         panelScore.setLayout(new BorderLayout(0, 0));
1041         
1042         panelScoreNorth = new JPanel();
1043         panelScore.add(panelScoreNorth, BorderLayout.NORTH);
1044         panelScoreNorth.setLayout(new FormLayout(new ColumnSpec[] {
1045                         ColumnSpec.decode("828px"),
1046                         FormSpecs.RELATED_GAP_COLSPEC,
1047                         ColumnSpec.decode("max(53dlu;default)"),},
1048                 new RowSpec[] {
1049                         RowSpec.decode("20px"),}));
1050         
1051         labelScoreTitle = new JLabel("スコア、ファン計算");
1052         labelScoreTitle.setFont(new Font("UD デジタル 教科書体 NP-B", Font.PLAIN, 16));
1053         panelScoreNorth.add(labelScoreTitle, "1, 1, center, center");
1054         
1055         labelScoreVersion = new JLabel(CheckVersion.getVersion());
1056         labelScoreVersion.setFont(new Font("SansSerif", Font.BOLD, 12));
1057         panelScoreNorth.add(labelScoreVersion, "3, 1, center, default");
1058         
1059         panelScoreCenter = new JPanel();
1060         panelScore.add(panelScoreCenter, BorderLayout.CENTER);
1061         panelScoreCenter.setLayout(new FormLayout(new ColumnSpec[] {
1062                         FormSpecs.RELATED_GAP_COLSPEC,
1063                         ColumnSpec.decode("max(60dlu;default)"),
1064                         FormSpecs.RELATED_GAP_COLSPEC,
1065                         ColumnSpec.decode("max(60dlu;default)"),
1066                         FormSpecs.RELATED_GAP_COLSPEC,
1067                         ColumnSpec.decode("max(60dlu;default):grow"),
1068                         FormSpecs.RELATED_GAP_COLSPEC,
1069                         ColumnSpec.decode("max(68dlu;default)"),
1070                         FormSpecs.RELATED_GAP_COLSPEC,
1071                         ColumnSpec.decode("max(59dlu;default)"),
1072                         FormSpecs.RELATED_GAP_COLSPEC,
1073                         ColumnSpec.decode("center:max(97dlu;default)"),
1074                         FormSpecs.RELATED_GAP_COLSPEC,
1075                         ColumnSpec.decode("max(25dlu;default)"),
1076                         FormSpecs.RELATED_GAP_COLSPEC,
1077                         ColumnSpec.decode("max(9dlu;default)"),
1078                         FormSpecs.RELATED_GAP_COLSPEC,
1079                         ColumnSpec.decode("max(25dlu;default)"),
1080                         FormSpecs.RELATED_GAP_COLSPEC,
1081                         ColumnSpec.decode("40dlu:grow"),},
1082                 new RowSpec[] {
1083                         FormSpecs.RELATED_GAP_ROWSPEC,
1084                         FormSpecs.DEFAULT_ROWSPEC,
1085                         FormSpecs.RELATED_GAP_ROWSPEC,
1086                         FormSpecs.DEFAULT_ROWSPEC,
1087                         FormSpecs.RELATED_GAP_ROWSPEC,
1088                         FormSpecs.DEFAULT_ROWSPEC,
1089                         FormSpecs.RELATED_GAP_ROWSPEC,
1090                         FormSpecs.DEFAULT_ROWSPEC,
1091                         FormSpecs.RELATED_GAP_ROWSPEC,
1092                         FormSpecs.DEFAULT_ROWSPEC,
1093                         FormSpecs.RELATED_GAP_ROWSPEC,
1094                         FormSpecs.DEFAULT_ROWSPEC,
1095                         FormSpecs.RELATED_GAP_ROWSPEC,
1096                         FormSpecs.DEFAULT_ROWSPEC,
1097                         FormSpecs.RELATED_GAP_ROWSPEC,
1098                         RowSpec.decode("max(13dlu;default)"),
1099                         FormSpecs.RELATED_GAP_ROWSPEC,
1100                         FormSpecs.DEFAULT_ROWSPEC,
1101                         FormSpecs.RELATED_GAP_ROWSPEC,
1102                         FormSpecs.DEFAULT_ROWSPEC,
1103                         FormSpecs.RELATED_GAP_ROWSPEC,
1104                         FormSpecs.DEFAULT_ROWSPEC,
1105                         FormSpecs.RELATED_GAP_ROWSPEC,
1106                         FormSpecs.DEFAULT_ROWSPEC,
1107                         FormSpecs.RELATED_GAP_ROWSPEC,
1108                         FormSpecs.DEFAULT_ROWSPEC,
1109                         FormSpecs.RELATED_GAP_ROWSPEC,
1110                         FormSpecs.DEFAULT_ROWSPEC,
1111                         FormSpecs.RELATED_GAP_ROWSPEC,
1112                         FormSpecs.DEFAULT_ROWSPEC,
1113                         FormSpecs.RELATED_GAP_ROWSPEC,
1114                         FormSpecs.DEFAULT_ROWSPEC,
1115                         FormSpecs.RELATED_GAP_ROWSPEC,
1116                         FormSpecs.DEFAULT_ROWSPEC,
1117                         FormSpecs.RELATED_GAP_ROWSPEC,
1118                         FormSpecs.DEFAULT_ROWSPEC,
1119                         FormSpecs.RELATED_GAP_ROWSPEC,
1120                         FormSpecs.DEFAULT_ROWSPEC,
1121                         FormSpecs.RELATED_GAP_ROWSPEC,
1122                         RowSpec.decode("default:grow"),}));
1123         
1124         labelScoreUserPlayed = new JLabel("Your score");
1125         panelScoreCenter.add(labelScoreUserPlayed, "4, 10, center, default");
1126         
1127         fieldScoreUserPlayed = new JTextField();
1128         panelScoreCenter.add(fieldScoreUserPlayed, "6, 10, fill, default");
1129         fieldScoreUserPlayed.setColumns(10);
1130         
1131         lblSongname = new JLabel("Songname");
1132         panelScoreCenter.add(lblSongname, "10, 10, center, default");
1133         
1134         labelScoreSongnameDynamic = new JLabel("<dynamic>");
1135         panelScoreCenter.add(labelScoreSongnameDynamic, "12, 10, center, default");
1136         
1137         labelScoreEarnedFan = new JLabel("Earned Fan");
1138         panelScoreCenter.add(labelScoreEarnedFan, "4, 14, center, default");
1139         
1140         fieldScoreEarnedFan = new JTextField();
1141         panelScoreCenter.add(fieldScoreEarnedFan, "6, 14, fill, default");
1142         fieldScoreEarnedFan.setColumns(10);
1143         
1144         labelScoreAttribute = new JLabel("Attribute");
1145         panelScoreCenter.add(labelScoreAttribute, "10, 14, center, default");
1146         
1147         labelScoreAttributeDynamic = new JLabel("<dynamic>");
1148         panelScoreCenter.add(labelScoreAttributeDynamic, "12, 14");
1149         
1150         lblRoom = new JLabel("room");
1151         panelScoreCenter.add(lblRoom, "4, 18, center, default");
1152         
1153         fieldScoreRoom = new JTextField();
1154         panelScoreCenter.add(fieldScoreRoom, "6, 18, fill, default");
1155         fieldScoreRoom.setColumns(10);
1156         
1157         labelScoreDifficulty = new JLabel("Difficulty");
1158         panelScoreCenter.add(labelScoreDifficulty, "10, 18, center, default");
1159         
1160         labelScoreDifficultyDynamic = new JLabel("<dynamic>");
1161         panelScoreCenter.add(labelScoreDifficultyDynamic, "12, 18");
1162         
1163         lblCenter = new JLabel("center");
1164         panelScoreCenter.add(lblCenter, "4, 22, center, default");
1165         
1166         fieldScoreCenter = new JTextField();
1167         panelScoreCenter.add(fieldScoreCenter, "6, 22, fill, default");
1168         fieldScoreCenter.setColumns(10);
1169         
1170         labelScoreLevel = new JLabel("Level");
1171         panelScoreCenter.add(labelScoreLevel, "10, 22, center, default");
1172         
1173         labelScoreLevelDynamic = new JLabel("<dynamic>");
1174         panelScoreCenter.add(labelScoreLevelDynamic, "12, 22");
1175         
1176         lblProduce = new JLabel("produce");
1177         panelScoreCenter.add(lblProduce, "4, 26, center, default");
1178         
1179         fieldScoreProduce = new JTextField();
1180         panelScoreCenter.add(fieldScoreProduce, "6, 26, fill, default");
1181         fieldScoreProduce.setColumns(10);
1182         
1183         labelScoreNotes = new JLabel("Notes");
1184         panelScoreCenter.add(labelScoreNotes, "10, 26, center, default");
1185         
1186         labelScoreNotesDynamic = new JLabel("<dynamic>");
1187         panelScoreCenter.add(labelScoreNotesDynamic, "12, 26");
1188         
1189         lblPremium = new JLabel("premium");
1190         panelScoreCenter.add(lblPremium, "4, 30, center, default");
1191         
1192         fieldScorePremium = new JTextField();
1193         panelScoreCenter.add(fieldScorePremium, "6, 30, fill, default");
1194         fieldScorePremium.setColumns(10);
1195         
1196         labelPlayerScore = new JLabel("Estimated Score");
1197         panelScoreCenter.add(labelPlayerScore, "10, 30, center, default");
1198         
1199         labelPlayerScoreDynamic = new JLabel("<dynamic>");
1200         panelScoreCenter.add(labelPlayerScoreDynamic, "12, 30");
1201         
1202         labelScoreCurrentSongOrder = new JLabel("null");
1203         panelScoreCenter.add(labelScoreCurrentSongOrder, "14, 30, center, default");
1204         
1205         labelScoreSlash = new JLabel("/");
1206         panelScoreCenter.add(labelScoreSlash, "16, 30, center, default");
1207         
1208         labelScoreOrderMax = new JLabel(String.valueOf(property.getSongLimit()));
1209         panelScoreCenter.add(labelScoreOrderMax, "18, 30, center, default");
1210         
1211         btnScorePrev = new JButton("Prev");
1212         btnScorePrev.addActionListener(new ActionListener() {
1213                 @Override
1214                 public void actionPerformed(ActionEvent e) {
1215                         CompletableFuture.runAsync(() -> {
1216                                 int currentIndex = Integer.parseInt(labelScoreCurrentSongOrder.getText()) - 1;
1217                                 if(currentIndex != 0) {
1218                                         Song prevSong = toolIntegrateList.get(currentIndex - 1);
1219                                         logger.info("currently : {} Next: {}", currentIndex + 1, currentIndex);
1220                                         logger.info("prevSong: {}", prevSong);
1221                                         labelScoreSongnameDynamic.setText("<html><body>" + prevSong.getName() + "</body></html>");
1222                                         labelScoreAttributeDynamic.setText(prevSong.getAttribute());
1223                                         labelScoreDifficultyDynamic.setText(prevSong.getDifficulty());
1224                                         labelScoreLevelDynamic.setText(String.valueOf(prevSong.getLevel()));
1225                                         labelScoreNotesDynamic.setText(String.valueOf(prevSong.getNotes()));
1226                                         labelScoreCurrentSongOrder.setText(String.valueOf(currentIndex));
1227                                 }
1228                         }, es);
1229                 }
1230         });
1231         panelScoreCenter.add(btnScorePrev, "14, 32");
1232         
1233         btnScoreNext = new JButton("Next");
1234         btnScoreNext.addActionListener(new ActionListener() {
1235                 @Override
1236                 public void actionPerformed(ActionEvent e) {
1237                         CompletableFuture.runAsync(() -> {
1238                                 int currentIndex = Integer.parseInt(labelScoreCurrentSongOrder.getText()) - 1;
1239                                 if(currentIndex != property.getSongLimit() - 1) {
1240                                         Song nextSong = toolIntegrateList.get(currentIndex + 1);
1241                                         logger.info("currently : {} Next: {}", currentIndex + 1, currentIndex + 2);
1242                                         logger.info("nextSong: {}", nextSong);
1243                                         labelScoreSongnameDynamic.setText("<html><body>" + nextSong.getName() + "</body></html>");
1244                                         labelScoreAttributeDynamic.setText(nextSong.getAttribute());
1245                                         labelScoreDifficultyDynamic.setText(nextSong.getDifficulty());
1246                                         labelScoreLevelDynamic.setText(String.valueOf(nextSong.getLevel()));
1247                                         labelScoreNotesDynamic.setText(String.valueOf(nextSong.getNotes()));
1248                                         labelScoreCurrentSongOrder.setText(String.valueOf(currentIndex + 2));
1249                                 }
1250                         }, es);
1251                 }
1252         });
1253         panelScoreCenter.add(btnScoreNext, "18, 32");
1254         
1255         labelPlayerFan = new JLabel("Estimated Fan");
1256         panelScoreCenter.add(labelPlayerFan, "10, 34, center, default");
1257         
1258         labelPlayerFanDynamic = new JLabel("<dynamic>");
1259         panelScoreCenter.add(labelPlayerFanDynamic, "12, 34");
1260         
1261         button = new JButton("自動計算");
1262         button.addActionListener(new ActionListener() {
1263                 @Override
1264                 public void actionPerformed(ActionEvent e) {
1265                         CompletableFuture.runAsync(() -> {
1266                                 String scoreStr = fieldScoreUserPlayed.getText();
1267                                 String fanStr = fieldScoreEarnedFan.getText();
1268                                 if(scoreStr.isEmpty() && fanStr.isEmpty()) {
1269                                         logger.warn("there is no data to calculate.");
1270                                         JOptionPane.showMessageDialog(null, "計算できるデータが存在しません。スコアかファン数のどちらかは必ず入力してください。");
1271                                         return;
1272                                 }
1273                                 labelPlayerScoreDynamic.setText("Calculating...");
1274                                 labelPlayerFanDynamic.setText("Calculating...");
1275                                 String roomStr = fieldScoreRoom.getText();
1276                                 String centerStr = fieldScoreCenter.getText();
1277                                 String produceStr = fieldScoreProduce.getText();
1278                                 String premiumStr = fieldScorePremium.getText();
1279                                 if(!scoreStr.isEmpty()) {
1280                                         int score = Integer.parseInt(scoreStr);
1281                                         int room = roomStr.isEmpty() ? 100 : Integer.parseInt(roomStr);
1282                                         int center = centerStr.isEmpty() ? 100 : Integer.parseInt(centerStr);
1283                                         int produce = produceStr.isEmpty() ? 100 : Integer.parseInt(produceStr);
1284                                         int premium = premiumStr.isEmpty() ? 100 : Integer.parseInt(premiumStr);
1285                                         int res = FanCalc.fanAsync(score, room, center, produce, premium).join();
1286                                         labelPlayerScoreDynamic.setText(String.valueOf(res));
1287                                         labelPlayerFanDynamic.setText(scoreStr);
1288                                 } else {
1289                                         int fan = Integer.parseInt(fanStr);
1290                                         int room = roomStr.isEmpty() ? 100 : Integer.parseInt(fanStr);
1291                                         int center = centerStr.isEmpty() ? 100 : Integer.parseInt(centerStr);
1292                                         int produce = produceStr.isEmpty() ? 100 : Integer.parseInt(produceStr);
1293                                         int premium = premiumStr.isEmpty() ? 100 : Integer.parseInt(premiumStr);
1294                                         int res = FanCalc.scoreAsync(fan, 1, room, center, produce, premium).join();
1295                                         labelPlayerFanDynamic.setText(String.valueOf(res));
1296                                         labelPlayerScoreDynamic.setText(scoreStr);
1297                                 }
1298                         }, es).whenComplete((ret, ex) -> {
1299                                 if(ex != null) {
1300                                         logger.error("Exception was thrown during concurrent process.", ex);
1301                                         JOptionPane.showMessageDialog(null, "イベント処理中に例外が発生しました。" + ex.getLocalizedMessage());
1302                                 }
1303                         });
1304                 }
1305         });
1306         panelScoreCenter.add(button, "18, 36");
1307         
1308         labelPlayerPRP = new JLabel("Estimated PRP");
1309         panelScoreCenter.add(labelPlayerPRP, "10, 38, center, default");
1310         
1311         labelPlayerPRPDynamic = new JLabel("<dynamic>");
1312         panelScoreCenter.add(labelPlayerPRPDynamic, "12, 38");
1313         
1314         label = new JLabel("<html><body>デレステに表示されている百分率をそのまま入力してください</body></html>");
1315         panelScoreCenter.add(label, "6, 40");
1316         if(isFirst || !this.property.isCheckLibraryUpdates()) {
1317             setEnabled.run();
1318         }
1319     }
1320 }