OSDN Git Service

Merge branch 'develop' into release/v3.0.0
[delesterandomselector/DelesteRandomSelector.git] / src / com / ranfa / main / DelesteRandomSelector.java
index 023d686..bbdcb8e 100644 (file)
@@ -3,17 +3,14 @@ package com.ranfa.main;
 import java.awt.BorderLayout;
 import java.awt.EventQueue;
 import java.awt.Font;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.io.IOException;
 import java.nio.file.Files;
-import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Random;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.function.BiConsumer;
 
 import javax.swing.DefaultComboBoxModel;
 import javax.swing.JButton;
@@ -23,15 +20,22 @@ import javax.swing.JFrame;
 import javax.swing.JLabel;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
+import javax.swing.JScrollPane;
 import javax.swing.JSpinner;
-import javax.swing.JTextPane;
+import javax.swing.JTextArea;
 import javax.swing.border.EmptyBorder;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.jgoodies.forms.layout.ColumnSpec;
 import com.jgoodies.forms.layout.FormLayout;
 import com.jgoodies.forms.layout.FormSpecs;
 import com.jgoodies.forms.layout.RowSpec;
-import com.ranfa.lib.LimitedLog;
+import com.ranfa.lib.CheckVersion;
+import com.ranfa.lib.Easter;
+import com.ranfa.lib.EstimateAlbumTypeCycle;
+import com.ranfa.lib.ManualUpdateThreadImpl;
 import com.ranfa.lib.Scraping;
 import com.ranfa.lib.SettingJSONProperty;
 import com.ranfa.lib.Settings;
@@ -39,10 +43,10 @@ import com.ranfa.lib.Song;
 import com.ranfa.lib.TwitterIntegration;
 import com.ranfa.lib.Version;
 
-@Version(major = 1, minor = 0, patch = 0)
+@Version(major = 3, minor = 0, patch = 0)
 public class DelesteRandomSelector extends JFrame {
 
-       private static ArrayList<Song> selectedSongsList = new ArrayList<Song>();
+       private static ArrayList<Song> selectedSongsList = new ArrayList<>();
 
        private JPanel contentPane;
        private JPanel panelNorth;
@@ -59,7 +63,6 @@ public class DelesteRandomSelector extends JFrame {
        private JButton btnImport;
        private JButton btnStart;
        private JButton btnExit;
-       private JTextPane textPane;
        private JComboBox comboDifficultySelect;
        private JLabel labelLvCaution;
        private JComboBox comboAttribute;
@@ -67,325 +70,336 @@ public class DelesteRandomSelector extends JFrame {
        private JButton btnTwitterIntegration;
        private String[] integratorArray;
        private boolean integratorBool = false;
+       private JTextArea textArea;
+       private JScrollPane scrollPane;
+       private CompletableFuture<Void> softwareUpdateFuture = null;
+       private CompletableFuture<Void> albumTypeEstimateFuture = null;
+       private String albumType = Messages.MSGAlbumTypeBeingCalculated.toString();
+       private Logger logger = LoggerFactory.getLogger(DelesteRandomSelector.class);
+       private ManualUpdateThreadImpl impl;
+       private Thread manualUpdateThread;
+       private JButton btnManualUpdate;
+       private Easter easter;
 
        /**
         * Launch the application.
         */
        public static void main(String[] args) {
-               EventQueue.invokeLater(new Runnable() {
-                       public void run() {
-                               try {
-                                       DelesteRandomSelector frame = new DelesteRandomSelector();
-                                       frame.setVisible(true);
-                               } catch (Exception e) {
-                                       e.printStackTrace();
-                               }
+               EventQueue.invokeLater(() -> {
+                       try {
+                               DelesteRandomSelector frame = new DelesteRandomSelector();
+                               frame.setVisible(true);
+                       } catch (Exception e) {
+                               e.printStackTrace();
                        }
                });
        }
 
        /**
         * log file prefix:
-        *  "[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[LEVEL]: " +
+        *  this.getClass() + ":[LEVEL]: " +
         */
 
        /**
         * Create the frame.
         */
        public DelesteRandomSelector() {
+               boolean isFirst = !Scraping.databaseExists();
+               if(isFirst) {
+                       JOptionPane.showMessageDialog(this, Messages.MSGDatabaseNotExist.toString());
+                       if(!Scraping.writeToJson(Scraping.getWholeData())) {
+                               JOptionPane.showMessageDialog(this, "Exception:NullPointerException\nCannot Keep up! Please re-download this Application!");
+                               throw new NullPointerException("FATAL: cannot continue!");
+                       }
+               }
+               ExecutorService es = Executors.newWorkStealingPool();
+               CompletableFuture<ArrayList<Song>> getFromJsonFuture = CompletableFuture.supplyAsync(() -> Scraping.getFromJson(), es);
+               CompletableFuture<ArrayList<Song>> getWholeDataFuture = CompletableFuture.supplyAsync(() -> Scraping.getWholeData(), es);
                if(!Settings.fileExists() && !Settings.writeDownJSON()) {
                        JOptionPane.showMessageDialog(this, "Exception:NullPointerException\nCannot Keep up! Please re-download this Application!");
                        throw new NullPointerException("FATAL: cannot continue!");
                }
-               LimitedLog.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[DEBUG]: " + "Loading Settings...");
-               property.setCheckLibraryUpdates(Settings.needToCheckLibraryUpdates());
-               property.setCheckVersion(Settings.needToCheckVersion());
-               property.setWindowWidth(Settings.getWindowWidth());
-               property.setWindowHeight(Settings.getWindowHeight());
-               property.setSongLimit(Settings.getSongsLimit());
-               property.setSaveScoreLog(Settings.saveScoreLog());
-               property.setOutputDebugSentences(Settings.outputDebugSentences());
-               LimitedLog.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[DEBUG]: " + "Loading Settings done."
-                               + "\nVersion Check: " + property.isCheckVersion()
-                               + "\nLibrary Update Check: " + property.isCheckLibraryUpdates()
-                               + "\nWindow Width: " + property.getWindowWidth()
-                               + "\nWindow Height: " + property.getWindowHeight()
-                               + "\nSong Limit: " + property.getSongLimit()
-                               + "\nSaveScoreLog: " + property.isSaveScoreLog()
-                               + "\nOutputDebugSentences: " + property.isOutputDebugSentences());
-               if(!Scraping.databaseExists()) {
-                       JOptionPane.showMessageDialog(this, "楽曲データベースが見つかりませんでした。自動的に作成されます…\n注意:初回起動ではなく、かつ、Jarファイルと同じ階層に\"database.json\"というファイルが存在するにも関わらず\nこのポップアップが出た場合、開発者までご一報ください。\nGithub URL: https://github.com/hizumiaoba/DelesteRandomSelector/issues");
-                       if(!Scraping.writeToJson(Scraping.getWholeData())) {
-                               JOptionPane.showMessageDialog(this, "Exception:NullPointerException\\nCannot Keep up! Please re-download this Application!");
-                               throw new NullPointerException("FATAL: cannot continue!");
+               this.logger.debug("Loading settings...");
+               this.property.setCheckLibraryUpdates(Settings.needToCheckLibraryUpdates());
+               this.property.setCheckVersion(Settings.needToCheckVersion());
+               this.property.setWindowWidth(Settings.getWindowWidth());
+               this.property.setWindowHeight(Settings.getWindowHeight());
+               this.property.setSongLimit(Settings.getSongsLimit());
+               this.property.setSaveScoreLog(Settings.saveScoreLog());
+               this.logger.debug("Load settings done.");
+               this.logger.debug("Version check: {}", this.property.isCheckVersion());
+               this.logger.debug("Library update check: {}", this.property.isCheckLibraryUpdates());
+               this.logger.debug("Window Width: {}", this.property.getWindowWidth());
+               this.logger.debug("Window Height: {}", this.property.getWindowHeight());
+               this.logger.debug("Song Limit: {}", this.property.getSongLimit());
+               this.logger.debug("SaveScoreLog: {}", this.property.isSaveScoreLog());
+               EstimateAlbumTypeCycle.Initialization();
+               if(Files.exists(Paths.get("generated/albumCycle.json"))) {
+                       this.albumType = EstimateAlbumTypeCycle.getCurrentCycle();
+               }
+               if(this.property.isCheckVersion()) {
+                       this.softwareUpdateFuture = CompletableFuture.runAsync(() -> CheckVersion.needToBeUpdated(), es);
+               }
+               BiConsumer<ArrayList<Song>, ArrayList<Song>> updateConsumer = (list1, list2) -> {
+                       this.logger.info("Checking database updates...");
+                       if(list1.size() > list2.size()) {
+                               long time = System.currentTimeMillis();
+                               this.logger.info("{} Update detected.", (list1.size() - list2.size()));
+                               Scraping.writeToJson(list1);
+                               this.logger.info("Update completed in {} ms", (System.currentTimeMillis() - time));
+                               this.logger.info("Updated database size: {}", list1.size());
+                       } else {
+                               this.logger.info("database is up-to-date.");
                        }
-               } else if(Scraping.getFromJson().size() < Scraping.getWholeData().size()) {
-                       LimitedLog.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[INFO]: " + "Update detected.Initiate update process...");
-                       Path path = Paths.get(Scraping.getDBPath());
+               };
+               Runnable setEnabled = () -> {
                        try {
-                               Files.delete(path);
-                       } catch (IOException e1) {
-                               LimitedLog.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[FATAL]: " + "Exception while updating library.\n" + e1.getLocalizedMessage());
-                               JOptionPane.showMessageDialog(null, "データベースファイルをアップデートできませんでした。ファイルの削除権限があるかどうか確認してください。\n" + e1.getLocalizedMessage());
+                               Thread.sleep(3 * 1000L);
+                       } catch (InterruptedException e1) {
+                               this.logger.error("Thread has been interrupted during waiting cooldown.", e1);
                        }
-                       Scraping.writeToJson(Scraping.getWholeData());
-                       LimitedLog.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[INFO]: " + "Library update completed.");
+                       this.btnImport.setEnabled(true);
+                       this.btnImport.setText(Messages.MSGNarrowingDownSongs.toString());
+               };
+               getWholeDataFuture.thenAcceptAsync(list -> this.logger.info("Scraping data size:" + list.size()), es);
+               getFromJsonFuture.thenAcceptAsync(list -> this.logger.info("Currently database size:" + list.size()), es);
+               if(this.property.isCheckLibraryUpdates()) {
+                       CompletableFuture<Void> updatedFuture = getWholeDataFuture.thenAcceptBothAsync(getFromJsonFuture, updateConsumer, es);
+                       updatedFuture.thenRunAsync(setEnabled, es);
                }
-               ExecutorService es = Executors.newWorkStealingPool();
-               CompletableFuture<ArrayList<Song>> getFromJsonFuture = CompletableFuture.supplyAsync(() -> Scraping.getFromJson(), es);
-               CompletableFuture<ArrayList<Song>> getWholeDataFuture = CompletableFuture.supplyAsync(() -> Scraping.getWholeData(), es);
-               getWholeDataFuture.thenAcceptAsync(list -> LimitedLog.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[INFO]: Scraping data size:" + list.size()), es);
-               getFromJsonFuture.thenAcceptAsync(list -> LimitedLog.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[INFO]: Currently database size:" + list.size()), es);
-               LimitedLog.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[DEBUG]: " + "Version:" + getVersion());
-               setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
-               setBounds(100, 100, 640, 360);
-               contentPane = new JPanel();
-               contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
-               setContentPane(contentPane);
-               contentPane.setLayout(new BorderLayout(0, 0));
-
-               panelNorth = new JPanel();
-               contentPane.add(panelNorth, BorderLayout.NORTH);
-               panelNorth.setLayout(new FormLayout(new ColumnSpec[] {
+               this.easter = new Easter();
+               this.setTitle(this.easter.getTodaysBirth());
+               this.logger.debug("Version: {}", CheckVersion.getVersion());
+               this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+               // this.setBounds(100, 100, this.property.getWindowWidth(), this.property.getWindowHeight());
+               this.setBounds(100, 100, 640, 360);
+               this.contentPane = new JPanel();
+               this.contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
+               this.setContentPane(this.contentPane);
+               this.contentPane.setLayout(new BorderLayout(0, 0));
+
+               this.panelNorth = new JPanel();
+               this.contentPane.add(this.panelNorth, BorderLayout.NORTH);
+               this.panelNorth.setLayout(new FormLayout(new ColumnSpec[] {
                                ColumnSpec.decode("max(302dlu;default)"),
                                FormSpecs.RELATED_GAP_COLSPEC,
                                ColumnSpec.decode("40px"),},
-                       new RowSpec[] {
-                               RowSpec.decode("20px"),}));
+                               new RowSpec[] {
+                                               RowSpec.decode("20px"),}));
 
-               labelTitle = new JLabel("デレステ課題曲セレクター");
-               labelTitle.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 16));
-               panelNorth.add(labelTitle, "1, 1, center, top");
+               this.labelTitle = new JLabel(Messages.MSGTitle.toString());
+               this.labelTitle.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 16));
+               this.panelNorth.add(this.labelTitle, "1, 1, center, top");
 
-               labelVersion = new JLabel(getVersion());
-               labelVersion.setFont(new Font("SansSerif", Font.BOLD, 12));
-               panelNorth.add(labelVersion, "3, 1, right, top");
+               this.labelVersion = new JLabel(CheckVersion.getVersion());
+               this.labelVersion.setFont(new Font("SansSerif", Font.BOLD, 12));
+               this.panelNorth.add(this.labelVersion, "3, 1, right, top");
 
-               panelWest = new JPanel();
-               contentPane.add(panelWest, BorderLayout.WEST);
-               panelWest.setLayout(new FormLayout(new ColumnSpec[] {
+               this.panelWest = new JPanel();
+               this.contentPane.add(this.panelWest, BorderLayout.WEST);
+               this.panelWest.setLayout(new FormLayout(new ColumnSpec[] {
                                FormSpecs.LABEL_COMPONENT_GAP_COLSPEC,
                                ColumnSpec.decode("112px:grow"),},
-                       new RowSpec[] {
-                               FormSpecs.LINE_GAP_ROWSPEC,
-                               RowSpec.decode("19px"),
-                               FormSpecs.RELATED_GAP_ROWSPEC,
-                               RowSpec.decode("max(12dlu;default)"),
-                               FormSpecs.RELATED_GAP_ROWSPEC,
-                               RowSpec.decode("max(12dlu;default)"),
-                               FormSpecs.RELATED_GAP_ROWSPEC,
-                               RowSpec.decode("12dlu"),
-                               FormSpecs.RELATED_GAP_ROWSPEC,
-                               RowSpec.decode("max(12dlu;default)"),
-                               FormSpecs.RELATED_GAP_ROWSPEC,
-                               RowSpec.decode("max(12dlu;default)"),
-                               FormSpecs.RELATED_GAP_ROWSPEC,
-                               RowSpec.decode("max(12dlu;default)"),
-                               FormSpecs.RELATED_GAP_ROWSPEC,
-                               RowSpec.decode("max(52dlu;default)"),}));
-
-               labelDifficulty = new JLabel("難易度選択");
-               labelDifficulty.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
-               panelWest.add(labelDifficulty, "2, 2, center, default");
-
-               comboDifficultySelect = new JComboBox();
-               comboDifficultySelect.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
-               comboDifficultySelect.setModel(new DefaultComboBoxModel(new String[] {"指定なし", "DEBUT", "REGULAR", "PRO", "MASTER", "MASTER+", "ⓁMASTER+", "LIGHT", "TRICK", "PIANO", "FORTE", "WITCH"}));
-               panelWest.add(comboDifficultySelect, "2, 4, fill, default");
-
-                               comboAttribute = new JComboBox();
-                               comboAttribute.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
-                               comboAttribute.setModel(new DefaultComboBoxModel(new String[] {"指定なし", "全タイプ", "キュート", "クール", "パッション"}));
-                               panelWest.add(comboAttribute, "2, 6, fill, default");
-
-                               labelLevel = new JLabel("楽曲Lv");
-                               labelLevel.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
-                               panelWest.add(labelLevel, "2, 8, center, default");
-
-                               spinnerLevel = new JSpinner();
-                               panelWest.add(spinnerLevel, "2, 10");
-
-                               checkLessLv = new JCheckBox("指定Lv以下");
-                               checkLessLv.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
-                               panelWest.add(checkLessLv, "2, 12");
-
-                               checkMoreLv = new JCheckBox("指定Lv以上");
-                               checkMoreLv.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
-                               panelWest.add(checkMoreLv, "2, 14");
-
-                               labelLvCaution = new JLabel("<html><body>※以下以上両方にチェックをつけることで指定レベルのみ絞り込むことができます</body></html>");
-                               labelLvCaution.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
-                               panelWest.add(labelLvCaution, "2, 16, fill, fill");
-
-               panelEast = new JPanel();
-               contentPane.add(panelEast, BorderLayout.EAST);
-               panelEast.setLayout(new FormLayout(new ColumnSpec[] {
+                               new RowSpec[] {
+                                               FormSpecs.LINE_GAP_ROWSPEC,
+                                               RowSpec.decode("19px"),
+                                               FormSpecs.RELATED_GAP_ROWSPEC,
+                                               RowSpec.decode("max(12dlu;default)"),
+                                               FormSpecs.RELATED_GAP_ROWSPEC,
+                                               RowSpec.decode("max(12dlu;default)"),
+                                               FormSpecs.RELATED_GAP_ROWSPEC,
+                                               RowSpec.decode("12dlu"),
+                                               FormSpecs.RELATED_GAP_ROWSPEC,
+                                               RowSpec.decode("max(12dlu;default)"),
+                                               FormSpecs.RELATED_GAP_ROWSPEC,
+                                               RowSpec.decode("max(12dlu;default)"),
+                                               FormSpecs.RELATED_GAP_ROWSPEC,
+                                               RowSpec.decode("max(12dlu;default)"),
+                                               FormSpecs.RELATED_GAP_ROWSPEC,
+                                               RowSpec.decode("max(52dlu;default)"),}));
+
+               this.labelDifficulty = new JLabel(Messages.MSGSelectDifficulty.toString());
+               this.labelDifficulty.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
+               this.panelWest.add(this.labelDifficulty, "2, 2, center, default");
+
+               this.comboDifficultySelect = new JComboBox();
+               this.comboDifficultySelect.setFont(new Font("Dialog", Font.BOLD, 12));
+               this.comboDifficultySelect.setModel(new DefaultComboBoxModel(new String[] {Messages.MSGNonSelected.toString(), "DEBUT", "REGULAR", "PRO", "MASTER", "MASTER+", "ⓁMASTER+", "LIGHT", "TRICK", "PIANO", "FORTE", "WITCH"}));
+               this.panelWest.add(this.comboDifficultySelect, "2, 4, fill, default");
+
+               this.comboAttribute = new JComboBox();
+               this.comboAttribute.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
+               this.comboAttribute.setModel(new DefaultComboBoxModel(new String[] {Messages.MSGNonSelected.toString(), "全タイプ", "キュート", "クール", "パッション"}));
+               this.panelWest.add(this.comboAttribute, "2, 6, fill, default");
+
+               this.labelLevel = new JLabel(Messages.MSGSongLevel.toString());
+               this.labelLevel.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
+               this.panelWest.add(this.labelLevel, "2, 8, center, default");
+
+               this.spinnerLevel = new JSpinner();
+               this.panelWest.add(this.spinnerLevel, "2, 10");
+
+               this.checkLessLv = new JCheckBox(Messages.MSGBelowSpecificLevel.toString());
+               this.checkLessLv.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
+               this.panelWest.add(this.checkLessLv, "2, 12");
+
+               this.checkMoreLv = new JCheckBox(Messages.MSGOverSpecificLevel.toString());
+               this.checkMoreLv.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
+               this.panelWest.add(this.checkMoreLv, "2, 14");
+
+               this.labelLvCaution = new JLabel(Messages.MSGLevelCheckboxInfo.toString());
+               this.labelLvCaution.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
+               this.panelWest.add(this.labelLvCaution, "2, 16, fill, fill");
+
+               this.panelEast = new JPanel();
+               this.contentPane.add(this.panelEast, BorderLayout.EAST);
+               this.panelEast.setLayout(new FormLayout(new ColumnSpec[] {
                                ColumnSpec.decode("98px"),},
-                       new RowSpec[] {
-                               RowSpec.decode("26px"),
-                               FormSpecs.RELATED_GAP_ROWSPEC,
-                               RowSpec.decode("max(36dlu;default)"),
-                               FormSpecs.RELATED_GAP_ROWSPEC,
-                               FormSpecs.DEFAULT_ROWSPEC,
-                               FormSpecs.RELATED_GAP_ROWSPEC,
-                               RowSpec.decode("max(30dlu;default)"),
-                               FormSpecs.RELATED_GAP_ROWSPEC,
-                               RowSpec.decode("max(15dlu;default)"),
-                               FormSpecs.RELATED_GAP_ROWSPEC,
-                               RowSpec.decode("max(11dlu;default)"),
-                               FormSpecs.RELATED_GAP_ROWSPEC,
-                               FormSpecs.DEFAULT_ROWSPEC,
-                               FormSpecs.RELATED_GAP_ROWSPEC,
-                               FormSpecs.DEFAULT_ROWSPEC,
-                               FormSpecs.RELATED_GAP_ROWSPEC,
-                               FormSpecs.DEFAULT_ROWSPEC,}));
-
-               btnImport = new JButton("<html><body>楽曲<br>絞り込み</body></html>");
-               btnImport.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e) {
-                               ArrayList<Song> fromJson = Scraping.getFromJson();
-                                       ArrayList<Song> specificlevelList = Scraping.getSpecificLevelSongs(fromJson, (Integer)spinnerLevel.getValue(), checkLessLv.isSelected(), checkMoreLv.isSelected());
-                                       ArrayList<Song> specificDifficultyList = Scraping.getSpecificDifficultySongs(specificlevelList, comboDifficultySelect.getSelectedItem().toString());
-                                       ArrayList<Song> specificAttributeList = Scraping.getSpecificAttributeSongs(specificDifficultyList, comboAttribute.getSelectedItem().toString());
-                                       if(!selectedSongsList.isEmpty())
-                                       selectedSongsList.clear();
-                               selectedSongsList.addAll(specificAttributeList);
-                               LimitedLog.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[INFO]: " +"Songs are selected.We are Ready to go.");
-                               JOptionPane.showMessageDialog(null, "絞り込み完了!「開始」をクリックすることで選曲できます!");
+                               new RowSpec[] {
+                                               RowSpec.decode("26px"),
+                                               FormSpecs.RELATED_GAP_ROWSPEC,
+                                               RowSpec.decode("max(36dlu;default)"),
+                                               FormSpecs.RELATED_GAP_ROWSPEC,
+                                               FormSpecs.DEFAULT_ROWSPEC,
+                                               FormSpecs.RELATED_GAP_ROWSPEC,
+                                               RowSpec.decode("max(30dlu;default)"),
+                                               FormSpecs.RELATED_GAP_ROWSPEC,
+                                               RowSpec.decode("max(15dlu;default)"),
+                                               FormSpecs.RELATED_GAP_ROWSPEC,
+                                               RowSpec.decode("max(11dlu;default)"),
+                                               FormSpecs.RELATED_GAP_ROWSPEC,
+                                               FormSpecs.DEFAULT_ROWSPEC,
+                                               FormSpecs.RELATED_GAP_ROWSPEC,
+                                               FormSpecs.DEFAULT_ROWSPEC,
+                                               FormSpecs.RELATED_GAP_ROWSPEC,
+                                               FormSpecs.DEFAULT_ROWSPEC,}));
+
+               this.btnImport = new JButton(Messages.MSGUpdatingDatabase.toString());
+               this.btnImport.setEnabled(false);
+               this.btnImport.addActionListener(e -> {
+                       if(this.impl != null) {
+                               if(!this.impl.getFlag()) {
+                                       JOptionPane.showMessageDialog(null, Messages.MSGManualUpdateNotCompleteYet.toString());
+                               }
+                       }
+                       ArrayList<Song> fromJson = Scraping.getFromJson();
+                       ArrayList<Song> specificlevelList = Scraping.getSpecificLevelSongs(fromJson, (Integer)DelesteRandomSelector.this.spinnerLevel.getValue(), DelesteRandomSelector.this.checkLessLv.isSelected(), DelesteRandomSelector.this.checkMoreLv.isSelected());
+                       ArrayList<Song> specificDifficultyList = Scraping.getSpecificDifficultySongs(specificlevelList, DelesteRandomSelector.this.comboDifficultySelect.getSelectedItem().toString());
+                       ArrayList<Song> specificAttributeList = Scraping.getSpecificAttributeSongs(specificDifficultyList, DelesteRandomSelector.this.comboAttribute.getSelectedItem().toString());
+                       ArrayList<Song> specificTypeList = Scraping.getSpecificAlbumTypeSongs(specificAttributeList, EstimateAlbumTypeCycle.getCurrentCycle());
+                       if(!selectedSongsList.isEmpty()) {
+                               selectedSongsList.clear();
                        }
+                       selectedSongsList.addAll((DelesteRandomSelector.this.comboDifficultySelect.getSelectedItem().equals(Scraping.MASTERPLUS) || DelesteRandomSelector.this.comboDifficultySelect.getSelectedItem().equals(Scraping.LEGACYMASTERPLUS)) ? specificTypeList : specificAttributeList);
+                       DelesteRandomSelector.this.logger.info("Songs are selected.We are Ready to go.");
+                       JOptionPane.showMessageDialog(null, Messages.MSGCompleteNarrowDown.toString());
                });
-               btnImport.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
-               panelEast.add(btnImport, "1, 3, fill, fill");
-
-               btnStart = new JButton("開始!");
-               btnStart.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e) {
-                               Random random = new Random(System.currentTimeMillis());
-                               String[] tmp = new String[property.getSongLimit()];
-                               integratorArray = new String[property.getSongLimit()];
-                               for(int i = 0; i < property.getSongLimit(); i++) {
-                                       int randomInt = random.nextInt(selectedSongsList.size());
-                                       tmp[i] = (i + 1) + "曲目: " + selectedSongsList.get(randomInt).getAttribute() + " [" + selectedSongsList.get(randomInt).getDifficulty() + "]「" + selectedSongsList.get(randomInt).getName() + "」!(Lv:" + selectedSongsList.get(randomInt).getLevel() + ")\n\n";
-                                       integratorArray[i] = selectedSongsList.get(randomInt).getName() + "\n";
+               this.btnImport.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
+               this.panelEast.add(this.btnImport, "1, 3, fill, fill");
+
+               this.btnStart = new JButton(Messages.MSGCalcStart.toString());
+               this.btnStart.addActionListener(e -> {
+                       Random random = new Random(System.currentTimeMillis());
+                       String paneString = "";
+                       DelesteRandomSelector.this.integratorArray = new String[DelesteRandomSelector.this.property.getSongLimit()];
+                       for(int i = 0; i < DelesteRandomSelector.this.property.getSongLimit(); i++) {
+                               int randomInt = random.nextInt(selectedSongsList.size());
+                               String typeString = DelesteRandomSelector.this.comboDifficultySelect.getSelectedItem().equals(Scraping.MASTERPLUS) || DelesteRandomSelector.this.comboDifficultySelect.getSelectedItem().equals(Scraping.LEGACYMASTERPLUS) ? EstimateAlbumTypeCycle.getCurrentCycle() : "";
+                               paneString = paneString + (i + 1) + Messages.MSGNumberOfSongs.toString() + " " + selectedSongsList.get(randomInt).getAttribute() + " [" + selectedSongsList.get(randomInt).getDifficulty() + "]「" + selectedSongsList.get(randomInt).getName() + "」!(Lv:" + selectedSongsList.get(randomInt).getLevel() + " " + typeString + ")\n\n";
+                               DelesteRandomSelector.this.integratorArray[i] = selectedSongsList.get(randomInt).getName() + "(Lv" + selectedSongsList.get(randomInt).getLevel() + ")\n";
+                       }
+                       paneString = paneString + Messages.MSGThisPhrase.toString() + DelesteRandomSelector.this.property.getSongLimit() + Messages.MSGPlayPhrase.toString();
+                       DelesteRandomSelector.this.textArea.setText(paneString);
+                       DelesteRandomSelector.this.integratorBool = true;
+                       DelesteRandomSelector.this.logger.info("show up completed.");
+               });
+               this.btnStart.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
+               this.panelEast.add(this.btnStart, "1, 7, fill, fill");
+
+               this.btnTwitterIntegration = new JButton(Messages.MSGTwitterIntegration.toString());
+               this.btnTwitterIntegration.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 11));
+               this.btnTwitterIntegration.addActionListener(e -> {
+                       boolean authorizationStatus = TwitterIntegration.authorization();
+                       String updatedStatus = Messages.MSGUsingThisAppPhrase.toString();
+                       int lengthLimit = updatedStatus.length();
+                       boolean isBroken = false;
+                       if(!DelesteRandomSelector.this.integratorBool) {
+                               JOptionPane.showMessageDialog(null, Messages.MSGNotPlayYet.toString());
+                               return;
+                       }
+                       for (String element : DelesteRandomSelector.this.integratorArray) {
+                               updatedStatus = updatedStatus + element;
+                               lengthLimit += element.length();
+                               if(lengthLimit > 69) {
+                                       isBroken = true;
+                                       break;
                                }
-                               String paneString = "";
-                               for (int i = 0; i < tmp.length; i++) {
-                                       paneString = paneString + tmp[i];
+                       }
+                       if(isBroken) {
+                               updatedStatus = updatedStatus + Messages.MSGTwitterPlayOtherwisePhrase.toString() + "\n#DelesteRandomSelector #デレステ ";
+                       } else {
+                               updatedStatus = updatedStatus + Messages.MSGTwitterPlayOnlyPhrase.toString() + "\n#DelesteRandomSelector #デレステ ";
+                       }
+                       DelesteRandomSelector.this.logger.info("status message constructed.");
+                       lengthLimit = updatedStatus.length();
+                       if(authorizationStatus) {
+                               int option = JOptionPane.showConfirmDialog(null, Messages.MSGTwitterIntegrationConfirm.toString() + updatedStatus + Messages.MSGStringLength.toString() + lengthLimit);
+                               DelesteRandomSelector.this.logger.info("user seletced: " + option);
+                               switch(option) {
+                               case JOptionPane.OK_OPTION:
+                                       TwitterIntegration.PostTwitter(updatedStatus);
+                                       DelesteRandomSelector.this.logger.info("Success to update the status.");
+                                       JOptionPane.showMessageDialog(null, Messages.MSGCompletePost.toString());
+                                       break;
+                               case JOptionPane.NO_OPTION:
+                                       DelesteRandomSelector.this.logger.info("There is no will to post.");
+                                       break;
+                               case JOptionPane.CANCEL_OPTION:
+                                       DelesteRandomSelector.this.logger.info("The Operation was canceled by user.");
+                                       break;
+                               default:
+                                       break;
                                }
-                               paneString = paneString + "この" + tmp.length + "曲をプレイしましょう!!!";
-                               textPane.setText(paneString);
-                               integratorBool = true;
-                               LimitedLog.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[INFO]: " + "show up completed.");
+                       } else {
+                               DelesteRandomSelector.this.logger.info("seems to reject the permission.it should need try again.");
                        }
                });
-               btnStart.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
-               panelEast.add(btnStart, "1, 7, fill, fill");
-
-                               btnTwitterIntegration = new JButton("Twitter連携");
-                               btnTwitterIntegration.addActionListener(new ActionListener() {
-                                       public void actionPerformed(ActionEvent e) {
-                                               System.out.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[INFO]: " + "Twitter Integration requested.Verify permission status.");
-                                               boolean authorizationStatus = TwitterIntegration.authorization();
-                                               System.out.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[INFO]: " + "Permission Verifying completed.\nStatus: " + authorizationStatus);
-                                               System.out.print("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[INFO]: " + "Construction status message...");
-                                               String updatedStatus = "デレステ課題曲セレクターで\n";
-                                               int lengthLimit = updatedStatus.length();
-                                               boolean isBroken = false;
-                                               if(!integratorBool) {
-                                                       JOptionPane.showMessageDialog(null, "ちひろ「まだプレイを始めていないみたいですね」");
-                                                       System.out.println();
-                                                       return;
-                                               }
-                                               for(int i = 0; i < integratorArray.length; i++) {
-                                                       updatedStatus = updatedStatus + integratorArray[i];
-                                                       lengthLimit += integratorArray[i].length();
-                                                       if(lengthLimit > 69) {
-                                                               isBroken = true;
-                                                               break;
-                                                       }
-                                               }
-                                               if(isBroken) {
-                                                       updatedStatus = updatedStatus + "…その他数曲\nをプレイしました!\n#DelesteRandomSelector #デレステ ";
-                                               } else {
-                                                       updatedStatus = updatedStatus + "をプレイしました!\n#DelesteRandomSelector #デレステ ";
-                                               }
-                                               System.out.println("completed.\n" + updatedStatus);
-                                               lengthLimit = updatedStatus.length();
-                                               if(authorizationStatus) {
-                                                       int option = JOptionPane.showConfirmDialog(null, "Twitterへ以下の内容を投稿します。よろしいですか?\n\n" + updatedStatus + "\n\n文字数:" + lengthLimit);
-                                                       System.out.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[INFO]: " + "User selected " + option);
-                                                       switch(option) {
-                                                               case JOptionPane.OK_OPTION:
-                                                                       TwitterIntegration.PostTwitter(updatedStatus);
-                                                                       System.out.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[INFO]: " + "Success to update the status.");
-                                                                       JOptionPane.showMessageDialog(null, "投稿が完了しました。");
-                                                                       break;
-                                                               case JOptionPane.NO_OPTION:
-                                                                       System.out.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[INFO]: " + "There is no will to post.");
-                                                                       break;
-                                                               case JOptionPane.CANCEL_OPTION:
-                                                                       System.out.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[INFO]: " + "The Operation was canceled by user.");
-                                                                       break;
-                                                               default:
-                                                                       break;
-                                                       }
-                                               } else {
-                                                       System.out.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[WARN]: " + "seems to reject the permission.it should need try again.");
-                                               }
-                                       }
-                               });
-                               panelEast.add(btnTwitterIntegration, "1, 11");
-
-                                                               btnExit = new JButton("終了");
-                                                               btnExit.addActionListener(new ActionListener() {
-                                                                       public void actionPerformed(ActionEvent e) {
-                                                                               LimitedLog.println("[" + Thread.currentThread().toString() + "]:" + this.getClass() + ":[INFO]: " +"Requested Exit by Button");
-                                                                               if(getWholeDataFuture.isDone()) {
-                                                                                       System.exit(0);
-                                                                               } else {
-                                                                                       JOptionPane.showMessageDialog(null, "非同期処理が完了していません。少し時間が経ってからやり直してください。");
-                                                                               }
-                                                                       }
-                                                               });
-                                                               btnExit.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
-                                                               panelEast.add(btnExit, "1, 13");
-
-               panelCentre = new JPanel();
-               contentPane.add(panelCentre, BorderLayout.CENTER);
-               panelCentre.setLayout(new BorderLayout(0, 0));
-
-               textPane = new JTextPane();
-               textPane.setText("楽曲選択の手順\r\n1.難易度、属性、レベルを選択する\r\n2.「楽曲取り込み」ボタンを押す!\r\n3.「開始」ボタンを押す!\r\n4.選択された楽曲がここに表示されます!\r\n現在設定されている楽曲選択の最大数:" + property.getSongLimit());
-               textPane.setEditable(false);
-               panelCentre.add(textPane);
-       }
 
+               this.btnManualUpdate = new JButton(Messages.MSGManualUpdate.toString());
+               this.btnManualUpdate.addActionListener(e -> {
+                       this.impl = new ManualUpdateThreadImpl();
+                       es.submit(this.impl);
+               });
+               this.panelEast.add(this.btnManualUpdate, "1, 9");
+               this.panelEast.add(this.btnTwitterIntegration, "1, 11");
+
+               this.btnExit = new JButton(Messages.MSGTerminate.toString());
+               this.btnExit.addActionListener(e -> {
+                       if(DelesteRandomSelector.this.softwareUpdateFuture.isDone() || DelesteRandomSelector.this.albumTypeEstimateFuture.isDone() || !this.impl.getFlag()) {
+                               DelesteRandomSelector.this.logger.info("Requested Exit by Button.");
+                               this.logger.info("Shut down thread pool.");
+                               es.shutdown();
+                               System.exit(0);
+                       } else {
+                               JOptionPane.showMessageDialog(null, Messages.MSGInternalYpdateNotDoneYet.toString());
+                       }
+               });
+               this.btnExit.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
+               this.panelEast.add(this.btnExit, "1, 13");
 
-       /**
-        * アノテーションで記載されているバージョンを取得します
-        * @since v1.0.0
-        * @return アノテーションで定義されているバージョン
-        */
-       public static String getVersion() {
-               String value = "v"
-                               + getMajorVersion() + "."
-                               + getMinorVersion() + "."
-                               + getPatchVersion();
-               return value;
-       }
+               this.panelCentre = new JPanel();
+               this.contentPane.add(this.panelCentre, BorderLayout.CENTER);
+               this.panelCentre.setLayout(new BorderLayout(0, 0));
 
-       public static int getMajorVersion() {
-               Version version = (Version) DelesteRandomSelector.class.getAnnotation(Version.class);
-               return version.major();
-       }
+               this.textArea = new JTextArea();
+               this.textArea.setText(Messages.MSGNarrowDownProcedure.toString() + this.property.getSongLimit() + Messages.MSGCurrentAlbumType.toString() + this.albumType);
+               this.textArea.setEditable(false);
 
-       public static int getMinorVersion() {
-               Version version = (Version) DelesteRandomSelector.class.getAnnotation(Version.class);
-               return version.minor();
+               this.scrollPane = new JScrollPane(this.textArea);
+               this.panelCentre.add(this.scrollPane, BorderLayout.CENTER);
+               if(isFirst || !this.property.isCheckLibraryUpdates()) {
+                       setEnabled.run();
+               }
        }
 
-       public static int getPatchVersion() {
-               Version version = (Version) DelesteRandomSelector.class.getAnnotation(Version.class);
-               return version.patch();
-       }
 }