8058115: Some of MidiDeviceProviders do not follow the specification
authorserb
Tue, 30 Sep 2014 17:39:04 +0400
changeset 27061 3afa6296f688
parent 27060 cb0fdfacdd25
child 27062 1ec810a4c3ec
8058115: Some of MidiDeviceProviders do not follow the specification Reviewed-by: prr, azvegint
jdk/src/java.desktop/share/classes/com/sun/media/sound/AbstractMidiDeviceProvider.java
jdk/src/java.desktop/share/classes/com/sun/media/sound/MidiUtils.java
jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencer.java
jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencerProvider.java
jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftProvider.java
jdk/src/java.desktop/share/classes/javax/sound/midi/MidiSystem.java
jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiDeviceProvider.java
jdk/test/javax/sound/midi/MidiDeviceProvider/NullInfo.java
jdk/test/javax/sound/midi/MidiDeviceProvider/UnsupportedInfo.java
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/AbstractMidiDeviceProvider.java	Tue Sep 30 14:51:33 2014 +0400
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/AbstractMidiDeviceProvider.java	Tue Sep 30 17:39:04 2014 +0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -117,7 +117,7 @@
         }
     }
 
-
+    @Override
     public final MidiDevice.Info[] getDeviceInfo() {
         readDeviceInfos();
         Info[] infos = getInfoCache();
@@ -126,7 +126,7 @@
         return localArray;
     }
 
-
+    @Override
     public final MidiDevice getDevice(MidiDevice.Info info) {
         if (info instanceof Info) {
             readDeviceInfos();
@@ -143,9 +143,7 @@
                 }
             }
         }
-
-        throw new IllegalArgumentException("MidiDevice " + info.toString()
-                                           + " not supported by this provider.");
+        throw MidiUtils.unsupportedDevice(info);
     }
 
 
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/MidiUtils.java	Tue Sep 30 14:51:33 2014 +0400
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/MidiUtils.java	Tue Sep 30 17:39:04 2014 +0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,9 +25,15 @@
 
 package com.sun.media.sound;
 
-import javax.sound.midi.*;
 import java.util.ArrayList;
 
+import javax.sound.midi.MetaMessage;
+import javax.sound.midi.MidiDevice;
+import javax.sound.midi.MidiEvent;
+import javax.sound.midi.MidiMessage;
+import javax.sound.midi.Sequence;
+import javax.sound.midi.Track;
+
 // TODO:
 // - define and use a global symbolic constant for 60000000 (see convertTempo)
 
@@ -48,6 +54,17 @@
     private MidiUtils() {
     }
 
+    /**
+     * Returns an exception which should be thrown if MidiDevice is unsupported.
+     *
+     * @param  info an info object that describes the desired device
+     * @return an exception instance
+     */
+    static RuntimeException unsupportedDevice(final MidiDevice.Info info) {
+        return new IllegalArgumentException(String.format(
+                "MidiDevice %s not supported by this provider", info));
+    }
+
     /** return true if the passed message is Meta End Of Track */
     public static boolean isMetaEndOfTrack(MidiMessage midiMsg) {
         // first check if it is a META message at all
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencer.java	Tue Sep 30 14:51:33 2014 +0400
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencer.java	Tue Sep 30 17:39:04 2014 +0400
@@ -64,7 +64,7 @@
     /**
      * All RealTimeSequencers share this info object.
      */
-    static final RealTimeSequencerInfo info = new RealTimeSequencerInfo();
+    static final MidiDevice.Info info = new RealTimeSequencerInfo();
 
 
     private static final Sequencer.SyncMode[] masterSyncModes = { Sequencer.SyncMode.INTERNAL_CLOCK };
@@ -154,7 +154,7 @@
 
     /* ****************************** CONSTRUCTOR ****************************** */
 
-    RealTimeSequencer() throws MidiUnavailableException {
+    RealTimeSequencer(){
         super(info);
 
         if (Printer.trace) Printer.trace(">> RealTimeSequencer CONSTRUCTOR");
@@ -1088,7 +1088,7 @@
         private static final String description = "Software sequencer";
         private static final String version = "Version 1.0";
 
-        private RealTimeSequencerInfo() {
+        RealTimeSequencerInfo() {
             super(name, vendor, description, version);
         }
     } // class Info
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencerProvider.java	Tue Sep 30 14:51:33 2014 +0400
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencerProvider.java	Tue Sep 30 17:39:04 2014 +0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,6 @@
 package com.sun.media.sound;
 
 import javax.sound.midi.MidiDevice;
-import javax.sound.midi.MidiUnavailableException;
 import javax.sound.midi.spi.MidiDeviceProvider;
 
 /**
@@ -36,23 +35,16 @@
  */
 public final class RealTimeSequencerProvider extends MidiDeviceProvider {
 
-
+    @Override
     public MidiDevice.Info[] getDeviceInfo() {
-
-        MidiDevice.Info[] localArray = { RealTimeSequencer.info };
-        return localArray;
+        return new MidiDevice.Info[]{RealTimeSequencer.info};
     }
 
-
-    public MidiDevice getDevice(MidiDevice.Info info) {
-        if ((info != null) && (!info.equals(RealTimeSequencer.info))) {
-            return null;
+    @Override
+    public MidiDevice getDevice(final MidiDevice.Info info) {
+        if (RealTimeSequencer.info.equals(info)) {
+            return new RealTimeSequencer();
         }
-
-        try {
-            return new RealTimeSequencer();
-        } catch (MidiUnavailableException e) {
-            return null;
-        }
+        throw MidiUtils.unsupportedDevice(info);
     }
 }
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftProvider.java	Tue Sep 30 14:51:33 2014 +0400
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftProvider.java	Tue Sep 30 17:39:04 2014 +0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,11 +22,10 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
+
 package com.sun.media.sound;
 
-import java.util.Arrays;
 import javax.sound.midi.MidiDevice;
-import javax.sound.midi.MidiDevice.Info;
 import javax.sound.midi.spi.MidiDeviceProvider;
 
 /**
@@ -36,17 +35,16 @@
  */
 public final class SoftProvider extends MidiDeviceProvider {
 
-    static final Info softinfo = SoftSynthesizer.info;
-    private static final Info[] softinfos = {softinfo};
-
+    @Override
     public MidiDevice.Info[] getDeviceInfo() {
-        return Arrays.copyOf(softinfos, softinfos.length);
+        return new MidiDevice.Info[]{SoftSynthesizer.info};
     }
 
-    public MidiDevice getDevice(MidiDevice.Info info) {
-        if (info == softinfo) {
+    @Override
+    public MidiDevice getDevice(final MidiDevice.Info info) {
+        if (SoftSynthesizer.info.equals(info)) {
             return new SoftSynthesizer();
         }
-        return null;
+        throw MidiUtils.unsupportedDevice(info);
     }
 }
--- a/jdk/src/java.desktop/share/classes/javax/sound/midi/MidiSystem.java	Tue Sep 30 14:51:33 2014 +0400
+++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/MidiSystem.java	Tue Sep 30 17:39:04 2014 +0400
@@ -31,6 +31,7 @@
 import java.io.OutputStream;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -162,18 +163,11 @@
      *         of length 0 is returned.
      */
     public static MidiDevice.Info[] getMidiDeviceInfo() {
-        List<MidiDevice.Info> allInfos = new ArrayList<>();
-        List<MidiDeviceProvider> providers = getMidiDeviceProviders();
-
-        for(int i = 0; i < providers.size(); i++) {
-            MidiDeviceProvider provider = providers.get(i);
-            MidiDevice.Info[] tmpinfo = provider.getDeviceInfo();
-            for (int j = 0; j < tmpinfo.length; j++) {
-                allInfos.add( tmpinfo[j] );
-            }
+        final List<MidiDevice.Info> allInfos = new ArrayList<>();
+        for (final MidiDeviceProvider provider : getMidiDeviceProviders()) {
+            Collections.addAll(allInfos, provider.getDeviceInfo());
         }
-        MidiDevice.Info[] infosArray = allInfos.toArray(new MidiDevice.Info[0]);
-        return infosArray;
+        return allInfos.toArray(new MidiDevice.Info[allInfos.size()]);
     }
 
     /**
@@ -187,17 +181,15 @@
      *         MIDI device installed on the system
      * @see #getMidiDeviceInfo
      */
-    public static MidiDevice getMidiDevice(MidiDevice.Info info) throws MidiUnavailableException {
-        List<MidiDeviceProvider> providers = getMidiDeviceProviders();
-
-        for(int i = 0; i < providers.size(); i++) {
-            MidiDeviceProvider provider = providers.get(i);
+    public static MidiDevice getMidiDevice(final MidiDevice.Info info)
+            throws MidiUnavailableException {
+        for (final MidiDeviceProvider provider : getMidiDeviceProviders()) {
             if (provider.isDeviceSupported(info)) {
-                MidiDevice device = provider.getDevice(info);
-                return device;
+                return provider.getDevice(info);
             }
         }
-        throw new IllegalArgumentException("Requested device not installed: " + info);
+        throw new IllegalArgumentException(String.format(
+                "Requested device not installed: %s", info));
     }
 
     /**
--- a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiDeviceProvider.java	Tue Sep 30 14:51:33 2014 +0400
+++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiDeviceProvider.java	Tue Sep 30 17:39:04 2014 +0400
@@ -25,6 +25,8 @@
 
 package javax.sound.midi.spi;
 
+import java.util.Arrays;
+
 import javax.sound.midi.MidiDevice;
 
 /**
@@ -45,16 +47,8 @@
      * @return {@code true} if the specified device is supported, otherwise
      *         {@code false}
      */
-    public boolean isDeviceSupported(MidiDevice.Info info) {
-
-        MidiDevice.Info infos[] = getDeviceInfo();
-
-        for(int i=0; i<infos.length; i++) {
-            if( info.equals( infos[i] ) ) {
-                return true;
-            }
-        }
-        return false;
+    public boolean isDeviceSupported(final MidiDevice.Info info) {
+        return Arrays.asList(getDeviceInfo()).contains(info);
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/sound/midi/MidiDeviceProvider/NullInfo.java	Tue Sep 30 17:39:04 2014 +0400
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Collection;
+import java.util.HashSet;
+
+import javax.sound.midi.MidiDevice;
+import javax.sound.midi.MidiSystem;
+import javax.sound.midi.MidiUnavailableException;
+import javax.sound.midi.spi.MidiDeviceProvider;
+
+import static java.util.ServiceLoader.load;
+
+/**
+ * @test
+ * @bug 8058115
+ * @summary MidiDeviceProvider shouldn't returns incorrect results or throw NPE
+ *          in case of null MidiDevice.Info
+ * @author Sergey Bylokhov
+ */
+public final class NullInfo {
+
+    public static void main(final String[] args) {
+        // MidiSystem API
+        try {
+            MidiSystem.getMidiDevice(null);
+            throw new RuntimeException("IllegalArgumentException expected");
+        } catch (final MidiUnavailableException e) {
+            throw new RuntimeException("IllegalArgumentException expected", e);
+        } catch (final IllegalArgumentException ignored) {
+            // expected
+        }
+        // MidiDeviceProvider API
+        final Collection<String> errors = new HashSet<>();
+        for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) {
+            try {
+                if (mdp.isDeviceSupported(null)) {
+                    throw new RuntimeException("null is supported");
+                }
+                final MidiDevice device = mdp.getDevice(null);
+                System.err.println("MidiDevice: " + device);
+                throw new RuntimeException("IllegalArgumentException expected");
+            } catch (final IllegalArgumentException e) {
+                errors.add(e.getMessage());
+            }
+        }
+        if (errors.size() != 1) {
+            throw new RuntimeException("Wrong number of messages:" + errors);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/sound/midi/MidiDeviceProvider/UnsupportedInfo.java	Tue Sep 30 17:39:04 2014 +0400
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import javax.sound.midi.MidiDevice;
+import javax.sound.midi.MidiSystem;
+import javax.sound.midi.spi.MidiDeviceProvider;
+
+import static java.util.ServiceLoader.load;
+
+/**
+ * @test
+ * @bug 8058115
+ * @summary MidiDeviceProvider shouldn't returns incorrect results in case of
+ *          unsupported MidiDevice.Info
+ * @author Sergey Bylokhov
+ */
+public final class UnsupportedInfo {
+
+    public static void main(final String[] args) {
+        final MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();
+        for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) {
+            for (final MidiDevice.Info info : infos) {
+                if (mdp.isDeviceSupported(info)) {
+                    if (mdp.getDevice(info) == null) {
+                        throw new RuntimeException("MidiDevice is null");
+                    }
+                } else {
+                    try {
+                        mdp.getDevice(info);
+                        throw new RuntimeException(
+                                "IllegalArgumentException expected");
+                    } catch (final IllegalArgumentException ignored) {
+                        // expected
+                    }
+                }
+            }
+        }
+    }
+}