1 package com.ranfa.main;
3 import java.awt.BorderLayout;
4 import java.awt.EventQueue;
6 import java.awt.event.ActionEvent;
7 import java.awt.event.ActionListener;
8 import java.util.ArrayList;
9 import java.util.Random;
10 import java.util.concurrent.CompletableFuture;
11 import java.util.concurrent.ExecutorService;
12 import java.util.concurrent.Executors;
13 import java.util.function.BiConsumer;
15 import javax.swing.DefaultComboBoxModel;
16 import javax.swing.JButton;
17 import javax.swing.JCheckBox;
18 import javax.swing.JComboBox;
19 import javax.swing.JFrame;
20 import javax.swing.JLabel;
21 import javax.swing.JOptionPane;
22 import javax.swing.JPanel;
23 import javax.swing.JScrollPane;
24 import javax.swing.JSpinner;
25 import javax.swing.JTextArea;
26 import javax.swing.border.EmptyBorder;
28 import com.jgoodies.forms.layout.ColumnSpec;
29 import com.jgoodies.forms.layout.FormLayout;
30 import com.jgoodies.forms.layout.FormSpecs;
31 import com.jgoodies.forms.layout.RowSpec;
32 import com.ranfa.lib.LimitedLog;
33 import com.ranfa.lib.Scraping;
34 import com.ranfa.lib.SettingJSONProperty;
35 import com.ranfa.lib.Settings;
36 import com.ranfa.lib.Song;
37 import com.ranfa.lib.TwitterIntegration;
38 import com.ranfa.lib.Version;
40 @Version(major = 1, minor = 2, patch = 2)
41 public class DelesteRandomSelector extends JFrame {
43 private static ArrayList<Song> selectedSongsList = new ArrayList<Song>();
45 private JPanel contentPane;
46 private JPanel panelNorth;
47 private JPanel panelWest;
48 private JLabel labelVersion;
49 private JLabel labelTitle;
50 private JLabel labelDifficulty;
51 private JLabel labelLevel;
52 private JSpinner spinnerLevel;
53 private JCheckBox checkMoreLv;
54 private JCheckBox checkLessLv;
55 private JPanel panelEast;
56 private JPanel panelCentre;
57 private JButton btnImport;
58 private JButton btnStart;
59 private JButton btnExit;
60 private JComboBox comboDifficultySelect;
61 private JLabel labelLvCaution;
62 private JComboBox comboAttribute;
63 private SettingJSONProperty property = new SettingJSONProperty();
64 private JButton btnTwitterIntegration;
65 private String[] integratorArray;
66 private boolean integratorBool = false;
67 private JTextArea textArea;
68 private JScrollPane scrollPane;
71 * Launch the application.
73 public static void main(String[] args) {
74 EventQueue.invokeLater(new Runnable() {
77 DelesteRandomSelector frame = new DelesteRandomSelector();
78 frame.setVisible(true);
79 } catch (Exception e) {
88 * this.getClass() + ":[LEVEL]: " +
94 public DelesteRandomSelector() {
95 boolean isFirst = !Scraping.databaseExists();
97 JOptionPane.showMessageDialog(this, "楽曲データベースが見つかりませんでした。自動的に作成されます…\n注意:初回起動ではなく、かつ、Jarファイルと同じ階層に\"database.json\"というファイルが存在するにも関わらず\nこのポップアップが出た場合、開発者までご一報ください。\nGithub URL: https://github.com/hizumiaoba/DelesteRandomSelector/issues");
98 if(!Scraping.writeToJson(Scraping.getWholeData())) {
99 JOptionPane.showMessageDialog(this, "Exception:NullPointerException\\nCannot Keep up! Please re-download this Application!");
100 throw new NullPointerException("FATAL: cannot continue!");
103 ExecutorService es = Executors.newWorkStealingPool();
104 CompletableFuture<ArrayList<Song>> getFromJsonFuture = CompletableFuture.supplyAsync(() -> Scraping.getFromJson(), es);
105 CompletableFuture<ArrayList<Song>> getWholeDataFuture = CompletableFuture.supplyAsync(() -> Scraping.getWholeData(), es);
106 if(!Settings.fileExists() && !Settings.writeDownJSON()) {
107 JOptionPane.showMessageDialog(this, "Exception:NullPointerException\nCannot Keep up! Please re-download this Application!");
108 throw new NullPointerException("FATAL: cannot continue!");
110 LimitedLog.println(this.getClass() + ":[DEBUG]: " + "Loading Settings...");
111 property.setCheckLibraryUpdates(Settings.needToCheckLibraryUpdates());
112 property.setCheckVersion(Settings.needToCheckVersion());
113 property.setWindowWidth(Settings.getWindowWidth());
114 property.setWindowHeight(Settings.getWindowHeight());
115 property.setSongLimit(Settings.getSongsLimit());
116 property.setSaveScoreLog(Settings.saveScoreLog());
117 property.setOutputDebugSentences(Settings.outputDebugSentences());
118 LimitedLog.println(this.getClass() + ":[DEBUG]: " + "Loading Settings done."
119 + "\nVersion Check: " + property.isCheckVersion()
120 + "\nLibrary Update Check: " + property.isCheckLibraryUpdates()
121 + "\nWindow Width: " + property.getWindowWidth()
122 + "\nWindow Height: " + property.getWindowHeight()
123 + "\nSong Limit: " + property.getSongLimit()
124 + "\nSaveScoreLog: " + property.isSaveScoreLog()
125 + "\nOutputDebugSentences: " + property.isOutputDebugSentences());
126 BiConsumer<ArrayList<Song>, ArrayList<Song>> updateConsumer = (list1, list2) -> {
127 LimitedLog.println(this.getClass() + ":[INFO]: " + "Checking database updates...");
128 if(list1.size() > list2.size()) {
129 long time = System.currentTimeMillis();
130 LimitedLog.println(this.getClass() + ":[INFO]: " + "Update detected.");
131 Scraping.writeToJson(list1);
132 LimitedLog.println(this.getClass() + ":[INFO]: " + "Update completed in " + (System.currentTimeMillis() - time) + "ms");
133 LimitedLog.println(this.getClass() + ":[INFO]: " + "Updated database size: " + list1.size());
135 LimitedLog.println(this.getClass() + ":[INFO]: " + "database is up-to-date.");
138 Runnable setEnabled = () -> {
139 btnImport.setEnabled(true);
140 btnImport.setText("<html><body>楽曲<br>絞り込み</body></html>");
142 getWholeDataFuture.thenAcceptAsync(list -> LimitedLog.println(this.getClass() + ":[INFO]: Scraping data size:" + list.size()), es);
143 getFromJsonFuture.thenAcceptAsync(list -> LimitedLog.println(this.getClass() + ":[INFO]: Currently database size:" + list.size()), es);
144 CompletableFuture<Void> updatedFuture = getWholeDataFuture.thenAcceptBothAsync(getFromJsonFuture, updateConsumer, es);
145 updatedFuture.thenRunAsync(setEnabled, es);
146 LimitedLog.println(this.getClass() + ":[DEBUG]: " + "Version:" + getVersion());
147 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
148 setBounds(100, 100, 640, 360);
149 contentPane = new JPanel();
150 contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
151 setContentPane(contentPane);
152 contentPane.setLayout(new BorderLayout(0, 0));
154 panelNorth = new JPanel();
155 contentPane.add(panelNorth, BorderLayout.NORTH);
156 panelNorth.setLayout(new FormLayout(new ColumnSpec[] {
157 ColumnSpec.decode("max(302dlu;default)"),
158 FormSpecs.RELATED_GAP_COLSPEC,
159 ColumnSpec.decode("40px"),},
161 RowSpec.decode("20px"),}));
163 labelTitle = new JLabel("デレステ課題曲セレクター");
164 labelTitle.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 16));
165 panelNorth.add(labelTitle, "1, 1, center, top");
167 labelVersion = new JLabel(getVersion());
168 labelVersion.setFont(new Font("SansSerif", Font.BOLD, 12));
169 panelNorth.add(labelVersion, "3, 1, right, top");
171 panelWest = new JPanel();
172 contentPane.add(panelWest, BorderLayout.WEST);
173 panelWest.setLayout(new FormLayout(new ColumnSpec[] {
174 FormSpecs.LABEL_COMPONENT_GAP_COLSPEC,
175 ColumnSpec.decode("112px:grow"),},
177 FormSpecs.LINE_GAP_ROWSPEC,
178 RowSpec.decode("19px"),
179 FormSpecs.RELATED_GAP_ROWSPEC,
180 RowSpec.decode("max(12dlu;default)"),
181 FormSpecs.RELATED_GAP_ROWSPEC,
182 RowSpec.decode("max(12dlu;default)"),
183 FormSpecs.RELATED_GAP_ROWSPEC,
184 RowSpec.decode("12dlu"),
185 FormSpecs.RELATED_GAP_ROWSPEC,
186 RowSpec.decode("max(12dlu;default)"),
187 FormSpecs.RELATED_GAP_ROWSPEC,
188 RowSpec.decode("max(12dlu;default)"),
189 FormSpecs.RELATED_GAP_ROWSPEC,
190 RowSpec.decode("max(12dlu;default)"),
191 FormSpecs.RELATED_GAP_ROWSPEC,
192 RowSpec.decode("max(52dlu;default)"),}));
194 labelDifficulty = new JLabel("難易度選択");
195 labelDifficulty.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
196 panelWest.add(labelDifficulty, "2, 2, center, default");
198 comboDifficultySelect = new JComboBox();
199 comboDifficultySelect.setFont(new Font("Dialog", Font.BOLD, 12));
200 comboDifficultySelect.setModel(new DefaultComboBoxModel(new String[] {"指定なし", "DEBUT", "REGULAR", "PRO", "MASTER", "MASTER+", "ⓁMASTER+", "LIGHT", "TRICK", "PIANO", "FORTE", "WITCH"}));
201 panelWest.add(comboDifficultySelect, "2, 4, fill, default");
203 comboAttribute = new JComboBox();
204 comboAttribute.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
205 comboAttribute.setModel(new DefaultComboBoxModel(new String[] {"指定なし", "全タイプ", "キュート", "クール", "パッション"}));
206 panelWest.add(comboAttribute, "2, 6, fill, default");
208 labelLevel = new JLabel("楽曲Lv");
209 labelLevel.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
210 panelWest.add(labelLevel, "2, 8, center, default");
212 spinnerLevel = new JSpinner();
213 panelWest.add(spinnerLevel, "2, 10");
215 checkLessLv = new JCheckBox("指定Lv以下");
216 checkLessLv.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
217 panelWest.add(checkLessLv, "2, 12");
219 checkMoreLv = new JCheckBox("指定Lv以上");
220 checkMoreLv.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
221 panelWest.add(checkMoreLv, "2, 14");
223 labelLvCaution = new JLabel("<html><body>※以下以上両方にチェックをつけることで指定レベルのみ絞り込むことができます</body></html>");
224 labelLvCaution.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
225 panelWest.add(labelLvCaution, "2, 16, fill, fill");
227 panelEast = new JPanel();
228 contentPane.add(panelEast, BorderLayout.EAST);
229 panelEast.setLayout(new FormLayout(new ColumnSpec[] {
230 ColumnSpec.decode("98px"),},
232 RowSpec.decode("26px"),
233 FormSpecs.RELATED_GAP_ROWSPEC,
234 RowSpec.decode("max(36dlu;default)"),
235 FormSpecs.RELATED_GAP_ROWSPEC,
236 FormSpecs.DEFAULT_ROWSPEC,
237 FormSpecs.RELATED_GAP_ROWSPEC,
238 RowSpec.decode("max(30dlu;default)"),
239 FormSpecs.RELATED_GAP_ROWSPEC,
240 RowSpec.decode("max(15dlu;default)"),
241 FormSpecs.RELATED_GAP_ROWSPEC,
242 RowSpec.decode("max(11dlu;default)"),
243 FormSpecs.RELATED_GAP_ROWSPEC,
244 FormSpecs.DEFAULT_ROWSPEC,
245 FormSpecs.RELATED_GAP_ROWSPEC,
246 FormSpecs.DEFAULT_ROWSPEC,
247 FormSpecs.RELATED_GAP_ROWSPEC,
248 FormSpecs.DEFAULT_ROWSPEC,}));
250 btnImport = new JButton("<html><body>データベース<br>更新中…</body></html>");
251 btnImport.setEnabled(false);
252 btnImport.addActionListener(new ActionListener() {
253 public void actionPerformed(ActionEvent e) {
254 ArrayList<Song> fromJson = Scraping.getFromJson();
255 ArrayList<Song> specificlevelList = Scraping.getSpecificLevelSongs(fromJson, (Integer)spinnerLevel.getValue(), checkLessLv.isSelected(), checkMoreLv.isSelected());
256 ArrayList<Song> specificDifficultyList = Scraping.getSpecificDifficultySongs(specificlevelList, comboDifficultySelect.getSelectedItem().toString());
257 ArrayList<Song> specificAttributeList = Scraping.getSpecificAttributeSongs(specificDifficultyList, comboAttribute.getSelectedItem().toString());
258 if(!selectedSongsList.isEmpty())
259 selectedSongsList.clear();
260 selectedSongsList.addAll(specificAttributeList);
261 LimitedLog.println(this.getClass() + ":[INFO]: " +"Songs are selected.We are Ready to go.");
262 JOptionPane.showMessageDialog(null, "絞り込み完了!「開始」をクリックすることで選曲できます!");
265 btnImport.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
266 panelEast.add(btnImport, "1, 3, fill, fill");
268 btnStart = new JButton("開始!");
269 btnStart.addActionListener(new ActionListener() {
270 public void actionPerformed(ActionEvent e) {
271 Random random = new Random(System.currentTimeMillis());
272 String[] tmp = new String[property.getSongLimit()];
273 integratorArray = new String[property.getSongLimit()];
274 for(int i = 0; i < property.getSongLimit(); i++) {
275 int randomInt = random.nextInt(selectedSongsList.size());
276 tmp[i] = (i + 1) + "曲目: " + selectedSongsList.get(randomInt).getAttribute() + " [" + selectedSongsList.get(randomInt).getDifficulty() + "]「" + selectedSongsList.get(randomInt).getName() + "」!(Lv:" + selectedSongsList.get(randomInt).getLevel() + ")\n\n";
277 integratorArray[i] = selectedSongsList.get(randomInt).getName() + "(Lv" + selectedSongsList.get(randomInt).getLevel() + ")\n";
279 String paneString = "";
280 for (int i = 0; i < tmp.length; i++) {
281 paneString = paneString + tmp[i];
283 paneString = paneString + "この" + tmp.length + "曲をプレイしましょう!!!";
284 textArea.setText(paneString);
285 integratorBool = true;
286 LimitedLog.println(this.getClass() + ":[INFO]: " + "show up completed.");
289 btnStart.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
290 panelEast.add(btnStart, "1, 7, fill, fill");
292 btnTwitterIntegration = new JButton("Twitter連携");
293 btnTwitterIntegration.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 11));
294 btnTwitterIntegration.addActionListener(new ActionListener() {
295 public void actionPerformed(ActionEvent e) {
296 LimitedLog.println(this.getClass() + ":[INFO]: " + "Twitter Integration requested.Verify permission status.");
297 boolean authorizationStatus = TwitterIntegration.authorization();
298 LimitedLog.println(this.getClass() + ":[INFO]: " + "Permission Verifying completed.\nStatus: " + authorizationStatus);
299 LimitedLog.print(this.getClass() + ":[INFO]: " + "Construction status message...");
300 String updatedStatus = "デレステ課題曲セレクターで\n";
301 int lengthLimit = updatedStatus.length();
302 boolean isBroken = false;
303 if(!integratorBool) {
304 JOptionPane.showMessageDialog(null, "ちひろ「まだプレイを始めていないみたいですね」");
305 LimitedLog.println();
308 for(int i = 0; i < integratorArray.length; i++) {
309 updatedStatus = updatedStatus + integratorArray[i];
310 lengthLimit += integratorArray[i].length();
311 if(lengthLimit > 69) {
317 updatedStatus = updatedStatus + "…その他数曲\nをプレイしました!\n#DelesteRandomSelector #デレステ ";
319 updatedStatus = updatedStatus + "をプレイしました!\n#DelesteRandomSelector #デレステ ";
321 LimitedLog.println("completed.\n" + updatedStatus);
322 lengthLimit = updatedStatus.length();
323 if(authorizationStatus) {
324 int option = JOptionPane.showConfirmDialog(null, "Twitterへ以下の内容を投稿します。よろしいですか?\n\n" + updatedStatus + "\n\n文字数:" + lengthLimit);
325 LimitedLog.println(this.getClass() + ":[INFO]: " + "User selected " + option);
327 case JOptionPane.OK_OPTION:
328 TwitterIntegration.PostTwitter(updatedStatus);
329 LimitedLog.println(this.getClass() + ":[INFO]: " + "Success to update the status.");
330 JOptionPane.showMessageDialog(null, "投稿が完了しました。");
332 case JOptionPane.NO_OPTION:
333 LimitedLog.println(this.getClass() + ":[INFO]: " + "There is no will to post.");
335 case JOptionPane.CANCEL_OPTION:
336 LimitedLog.println(this.getClass() + ":[INFO]: " + "The Operation was canceled by user.");
342 LimitedLog.println(this.getClass() + ":[WARN]: " + "seems to reject the permission.it should need try again.");
346 panelEast.add(btnTwitterIntegration, "1, 11");
348 btnExit = new JButton("終了");
349 btnExit.addActionListener(new ActionListener() {
350 public void actionPerformed(ActionEvent e) {
351 LimitedLog.println(this.getClass() + ":[INFO]: " +"Requested Exit by Button");
352 if(updatedFuture.isDone()) {
355 JOptionPane.showMessageDialog(null, "非同期処理が完了していません。少し時間が経ってからやり直してください。");
359 btnExit.setFont(new Font("UD デジタル 教科書体 NP-B", Font.BOLD, 13));
360 panelEast.add(btnExit, "1, 13");
362 panelCentre = new JPanel();
363 contentPane.add(panelCentre, BorderLayout.CENTER);
364 panelCentre.setLayout(new BorderLayout(0, 0));
366 textArea = new JTextArea();
367 textArea.setText("楽曲選択の手順\r\n1.難易度、属性、レベルを選択する\r\n2.「楽曲取り込み」ボタンを押す!\r\n3.「開始」ボタンを押す!\r\n4.選択された楽曲がここに表示されます!\r\n現在設定されている楽曲選択の最大数:" + property.getSongLimit());
368 textArea.setEditable(false);
370 scrollPane = new JScrollPane(textArea);
371 panelCentre.add(scrollPane, BorderLayout.CENTER);
378 * アノテーションで記載されているバージョンを取得します
380 * @return アノテーションで定義されているバージョン
382 public static String getVersion() {
384 + getMajorVersion() + "."
385 + getMinorVersion() + "."
390 public static int getMajorVersion() {
391 Version version = (Version) DelesteRandomSelector.class.getAnnotation(Version.class);
392 return version.major();
395 public static int getMinorVersion() {
396 Version version = (Version) DelesteRandomSelector.class.getAnnotation(Version.class);
397 return version.minor();
400 public static int getPatchVersion() {
401 Version version = (Version) DelesteRandomSelector.class.getAnnotation(Version.class);
402 return version.patch();