OSDN Git Service

リファクタリング、エラー処理改善など
[midichordhelper/MIDIChordHelper.git] / src / camidion / chordhelper / midieditor / Base64Dialog.java
index 63c9181..3fdca39 100644 (file)
@@ -4,8 +4,8 @@ import java.awt.event.ActionEvent;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.charset.Charset;
 import java.util.Base64;
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import javax.sound.midi.InvalidMidiDataException;
@@ -21,52 +21,80 @@ import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.JTextArea;
+import javax.swing.ListSelectionModel;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
 
 import camidion.chordhelper.ButtonIcon;
 import camidion.chordhelper.ChordHelperApplet;
-import camidion.chordhelper.music.MIDISpec;
 
 /**
  * Base64テキスト入力ダイアログ
  */
-public class Base64Dialog extends JDialog implements DocumentListener {
-       public static final Pattern HEADER_PATTERN = Pattern.compile("^.*:.*$", Pattern.MULTILINE);
+public class Base64Dialog extends JDialog {
        private JTextArea base64TextArea = new JTextArea(8,56);
-       private void error(String message) {
+       private PlaylistTable playlistTable;
+       private void decodeError(String message) {
                JOptionPane.showMessageDialog(base64TextArea, (Object)message,
                                ChordHelperApplet.VersionInfo.NAME, JOptionPane.WARNING_MESSAGE);
                base64TextArea.requestFocusInWindow();
        }
-       private String createHeader(String filename) {
-               return "Content-Type: audio/midi; name=\"" + filename + "\"\n"
-                               + "Content-Transfer-Encoding: base64\n\n";
+       private static String createHeaderOfFilename(String filename) {
+               String header = "Content-Type: audio/midi; name=\"";
+               if( filename != null ) header += filename;
+               header += "\"\nContent-Transfer-Encoding: base64\n\n";
+               return header;
+       }
+       private static String createBase64TextWithHeader(SequenceTrackListTableModel sequenceModel) throws IOException {
+               if( sequenceModel == null ) return null;
+               String text = createHeaderOfFilename(sequenceModel.getFilename());
+               byte[] midiData = sequenceModel.getMIDIdata();
+               if( midiData != null && midiData.length > 0 )
+                       text += Base64.getMimeEncoder().encodeToString(midiData);
+               text += "\n";
+               return text;
+       }
+       private static String bodyOf(String base64TextWithHeader) {
+               // bodyには":"が含まれないのでヘッダと混同する心配なし
+               return Pattern.compile("^.*:.*$", Pattern.MULTILINE).matcher(base64TextWithHeader).replaceAll("");
+       }
+       private static String filenameOf(String base64TextWithHeader) {
+               Matcher m = Pattern.compile("(?i)^Content-Type:.*name=\"(.*)\"$", Pattern.MULTILINE).matcher(base64TextWithHeader);
+               return m.find() ? m.group(1) : "";
        }
-       public PlaylistTable playlistTable;
        /**
         * 入力されたBase64テキストをデコードし、MIDIシーケンスとしてプレイリストに追加します。
         * @return プレイリストに追加されたMIDIシーケンスのインデックス(先頭が0)、追加に失敗した場合は -1
         */
        public int addToPlaylist() {
-               byte[] midiData = null;
+               String base64Text = base64TextArea.getText();
+               byte[] decodedData;
                try {
-                       midiData = getMIDIData();
-               } catch(Exception e) {
-                       error("Base64デコードに失敗しました。\n"+e);
+                       decodedData = Base64.getMimeDecoder().decode(bodyOf(base64Text).getBytes());
+               } catch(Exception ex) {
+                       // 不正なBase64テキストが入力された場合
+                       decodeError("Base64デコードに失敗しました。\n"+ex);
                        return -1;
                }
-               try (InputStream in = new ByteArrayInputStream(midiData)) {
-                       Sequence sequence = MidiSystem.getSequence(in);
-                       Charset charset = MIDISpec.getCharsetOf(sequence);
-                       if( charset == null ) charset = Charset.defaultCharset();
-                       int index = playlistTable.getModel().add(sequence, charset, null);
-                       playlistTable.getSelectionModel().setSelectionInterval(index, index);
-                       return index;
-               } catch( IOException|InvalidMidiDataException e ) {
-                       error("Base64デコードした結果をMIDIシーケンスとして読み込めませんでした。\n"+e);
+               Sequence sequence;
+               try (InputStream in = new ByteArrayInputStream(decodedData)) {
+                       sequence = MidiSystem.getSequence(in);
+               } catch( IOException|InvalidMidiDataException ex ) {
+                       // MIDI以外のデータをエンコードしたBase64テキストが入力された場合
+                       decodeError("Base64デコードした結果をMIDIシーケンスとして読み込めませんでした。\n"+ex);
+                       return -1;
+               }
+               int newIndex;
+               try {
+                       newIndex = playlistTable.getModel().add(sequence, filenameOf(base64Text));
+               } catch(Exception ex) {
+                       // 何らかの理由でプレイリストへの追加ができなかった場合
+                       decodeError("Base64デコードしたMIDIシーケンスをプレイリストに追加できませんでした。\n"+ex);
                        return -1;
                }
+               ListSelectionModel sm = playlistTable.getSelectionModel();
+               if( sm != null ) sm.setSelectionInterval(newIndex, newIndex);
+               return newIndex;
        }
        /**
         * Base64デコードアクション
@@ -84,8 +112,12 @@ public class Base64Dialog extends JDialog implements DocumentListener {
        public Action clearAction = new AbstractAction("Clear", new ButtonIcon(ButtonIcon.X_ICON)) {
                { putValue(Action.SHORT_DESCRIPTION, "Base64テキスト欄を消去"); }
                @Override
-               public void actionPerformed(ActionEvent e) { setText(null); }
+               public void actionPerformed(ActionEvent e) { base64TextArea.setText(null); }
        };
+       private void setActionEnabled(boolean b) {
+               addBase64Action.setEnabled(b);
+               clearAction.setEnabled(b);
+       }
        /**
         * Base64テキスト入力ダイアログを構築します。
         * @param playlistTable Base64デコードされたMIDIシーケンスの追加先プレイリストビュー
@@ -105,73 +137,47 @@ public class Base64Dialog extends JDialog implements DocumentListener {
                }});
                setBounds( 300, 250, 660, 300 );
                base64TextArea.setToolTipText("Paste Base64-encoded MIDI sequence here");
-               base64TextArea.getDocument().addDocumentListener(this);
-               addBase64Action.setEnabled(false);
-               clearAction.setEnabled(false);
-       }
-       @Override
-       public void insertUpdate(DocumentEvent e) {
-               addBase64Action.setEnabled(true);
-               clearAction.setEnabled(true);
-       }
-       @Override
-       public void removeUpdate(DocumentEvent e) {
-               if( e.getDocument().getLength() > 0 ) return;
-               addBase64Action.setEnabled(false);
-               clearAction.setEnabled(false);
+               base64TextArea.getDocument().addDocumentListener(new DocumentListener() {
+                       @Override
+                       public void insertUpdate(DocumentEvent e) {
+                               setActionEnabled(true);
+                       }
+                       @Override
+                       public void removeUpdate(DocumentEvent e) {
+                               if( e.getDocument().getLength() > 0 ) return;
+                               setActionEnabled(false);
+                       }
+                       @Override
+                       public void changedUpdate(DocumentEvent e) { }
+               });
+               setActionEnabled(false);
        }
-       @Override
-       public void changedUpdate(DocumentEvent e) { }
        /**
-        * バイナリー形式でMIDIデータを返します。
-        * @return バイナリー形式のMIDIデータ
-        * @throws IllegalArgumentException 入力されているテキストが有効なBase64スキームになっていない場合
+        * MIDIシーケンスモデルを設定します。
+        * @param sequenceModel MIDIシーケンスモデル
         */
-       public byte[] getMIDIData() {
-               String body = HEADER_PATTERN.matcher(base64TextArea.getText()).replaceAll("");
-               return Base64.getMimeDecoder().decode(body.getBytes());
-       }
-       /**
-        * バイナリー形式のMIDIデータを設定します。
-        * @param midiData バイナリー形式のMIDIデータ
-        */
-       public void setMIDIData(byte[] midiData) { setMIDIData(midiData, null); }
-       /**
-        * バイナリー形式のMIDIデータを、ファイル名をつけて設定します。
-        * @param midiData バイナリー形式のMIDIデータ
-        * @param filename ファイル名
-        */
-       public void setMIDIData(byte[] midiData, String filename) {
-               if( midiData == null || midiData.length == 0 ) return;
-               if( filename == null ) filename = "";
-               setText(createHeader(filename) + Base64.getMimeEncoder().encodeToString(midiData) + "\n");
+       public void setSequenceModel(SequenceTrackListTableModel sequenceModel) {
+               String text;
+               try {
+                       text = createBase64TextWithHeader(sequenceModel);
+               } catch (IOException ioex) {
+                       text = "File[" + sequenceModel.getFilename() + "]:" + ioex;
+               }
+               base64TextArea.setText(text);
                base64TextArea.selectAll();
        }
        /**
-        * Base64形式でMIDIデータを返します。
+        * Base64形式でテキスト化されたMIDIデータを返します。
         * @return  Base64形式のMIDIデータ
         */
-       public String getBase64Data() { return base64TextArea.getText(); }
+       public String getBase64TextData() { return base64TextArea.getText(); }
        /**
-        * Base64形式のMIDIデータを設定します。
-        * @param base64Data Base64形式のMIDIデータ
+        * Base64形式でテキスト化されたMIDIデータを、ヘッダつきで設定します。
+        * @param base64TextData Base64形式のMIDIデータ
+        * @param filename ヘッダに含めるファイル名(nullを指定すると""として設定される)
         */
-       public void setBase64Data(String base64Data) {
-               setText(null);
-               base64TextArea.append(base64Data);
+       public void setBase64TextData(String base64TextData, String filename) {
+               base64TextArea.setText(createHeaderOfFilename(filename));
+               base64TextArea.append(base64TextData);
        }
-       /**
-        * Base64形式のMIDIデータを、ファイル名をつけて設定します。
-        * @param base64Data Base64形式のMIDIデータ
-        * @param filename ファイル名
-        */
-       public void setBase64Data(String base64Data, String filename) {
-               setText(createHeader(filename));
-               base64TextArea.append(base64Data);
-       }
-       /**
-        * テキスト文字列を設定します。
-        * @param text テキスト文字列
-        */
-       public void setText(String text) { base64TextArea.setText(text); }
 }