OSDN Git Service

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